针对2.6.16(网桥工作流程)

参考了独孤九贱大侠的文章,针对2.6.16代码。

零. STP的几个状态:

. Block :阻断状态,接收但不转发数据。

. Listening :侦听状态,不转发数据,可以收发BPDU ,执行选举root bridge,root port,designate port 等动作。

. Learning :学习状态,不转发数据,开始学习MAC ,为数据转发作准备,所以称之为Learning 状态

. Forward :转发状态,转发数据。

. Disable :禁用状态,既不参与STP 计算,也不转发数据。

一. 首先是Bridge 注册过程:

static int __init br_init(void)

{

br_fdb_init();

#ifdef CONFIG_BRIDGE_NETFILTER

if (br_netfilter_init())

return 1;

#endif

brioctl_set(br_ioctl_deviceless_stub);

/* 数据包的处理函数注册为br_handle_frame */

br_handle_frame_hook = br_handle_frame;

br_fdb_get_hook = br_fdb_get;

br_fdb_put_hook = br_fdb_put;

register_netdevice_notifier(&br_device_notifier);

return 0;

}

二. 网桥处理函数

int br_handle_frame(struct net_bridge_port *p, struct sk_buff **pskb)

{

struct sk_buff *skb = *pskb;

const unsigned char *dest = eth_hdr(skb)->h_dest;

if (p->state == BR_STATE_DISABLED)

goto err;

if (!is_valid_ether_addr(eth_hdr(skb)->h_source))

goto err;

/*

网桥打开了STP 功能,

是否为网桥组播地址,PS :bridge_ula定义为 { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 },只比较前5个字段,

为什么第5个字段要和0XF0比较,就不知道了 (01-80-c2-00-00-F0 ???)

*/

if (p->br->stp_enabled &&

!memcmp(dest, bridge_ula, 5) &&

!(dest[5] & 0xF0)) {

if (!dest[5]) {

/* 涉及到STP 功能的数据包都交给函数br_stp_handle_bpdu来处理 */

NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev,

NULL, br_stp_handle_bpdu);

return 1;

}

goto err;

}

/* 如果端口处于Learning 或Forwarding 状态时,要记录从端口通过的数据包的源MAC 地址,以update CAM 表 */

if (p->state == BR_STATE_FORWARDING || p->state == BR_STATE_LEARNING) {

if (br_should_route_hook) {

if (br_should_route_hook(pskb))

return 0;

skb = *pskb;

dest = eth_hdr(skb)->h_dest;

}

if (!compare_ether_addr(p->br->dev->dev_addr, dest))

skb->pkt_type = PACKET_HOST;

/* 在br_handle_frame中更新CAM 表 */

NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,

br_handle_frame_finish);

return 1;

}

err:

kfree_skb(skb);

return 1;

}

三. 接下来我们首先分析更新CAM 表的函数

br_handle_frame_finish,在第4小节再分析STP 的处理函数br_stp_handle_bpdu

int br_handle_frame_finish(struct sk_buff *skb)

{

const unsigned char *dest = eth_hdr(skb)->h_dest;

struct net_bridge_port *p = rcu_dereference(skb->dev->br_port);

struct net_bridge *br;

struct net_bridge_fdb_entry *dst;

int passedup = 0;

if (!p || p->state == BR_STATE_DISABLED)

goto drop;

/*

insert into forwarding database after filtering to avoid spoofing

使用数据包的源MAC 地址来更新CAM 表

*/

br = p->br;

br_fdb_update(br, p, eth_hdr(skb)->h_source);

if (p->state == BR_STATE_LEARNING)

goto drop;

/*

如果网桥的虚拟网卡处于混杂模式,那么每个接收到的数据包都需要克隆一份送到AF_PACKET协议处理 */

if (br->dev->flags & IFF_PROMISC) {

struct sk_buff *skb2;

skb2 = skb_clone(skb, GFP_ATOMIC);

if (skb2 != NULL) {

passedup = 1;

br_pass_frame_up(br, skb2);

}

}

/*

目的MAC 为广播或多播,则需要向本机的上层协议栈传送这个数据包,这里有一个标志变量passedup 用于表示是否传送过了,如果已传送过,那就算了

*/

if (is_multicast_ether_addr(dest)) {

br_flood_forward(br, skb, !passedup);

if (!passedup)

br_pass_frame_up(br, skb);

goto out;

}

dst = __br_fdb_get(br, dest);

if (dst != NULL && dst->is_local) {

if (!passedup)

br_pass_frame_up(br, skb);

else

kfree_skb(skb);

goto out;

}

if (dst != NULL) {

br_forward(dst->dst, skb);

goto out;

}

/* CAM表中没有,只能flood 了。。。。 */

br_flood_forward(br, skb, 0);

out:

return 0;

drop:

kfree_skb(skb);

goto out;

}

四. 在介绍br_stp_handle_bpdu

这个函数之前,我们首先来看一下内核中用来表示一个BPDU 的数据结构是什么 -- br_config_bpdu

struct br_config_bpdu

{

unsigned topology_change:1; /* 拓扑改变标志 */

unsigned topology_change_ack:1; /* 拓扑改变回应标志 */

bridge_id root; /* 根ID ,用于会聚后的网桥网络中,所有配置 BPDU 中的该字段都应该具有相同值(同VLAN ),又可分为两个 BID 子字段:网桥优先级和网桥 MAC 地址 */ int root_path_cost; /* 路径开销,通向有根网桥(Root Bridge)的所有链路的积累资本 */

bridge_id bridge_id; /* 创建当前 BPDU 的网桥 BID */

port_id port_id; /* 端口ID ,每个端口值都是唯一的 */

int message_age; /* 记录 Root Bridge 生成当前 BPDU 起源信息的所消耗时间 */

int max_age; /* 保存 BPDU 的最长时间,也反映了拓朴变化通知(Topology Change Notification)过程中的网桥表生存时间情况 */

int hello_time; /* 周期性配置 BPDU 间的时间 */

int forward_delay; /* 用于在 Listening 和 Learning 状态的时间,也反映了拓朴变化通知(Topology Change Notification)过程中的时间情况 */

};

五. B PDU 处理函数br_stp_handle_bpdu

int br_stp_handle_bpdu(struct sk_buff *skb)

{

struct net_bridge_port *p = rcu_dereference(skb->dev->br_port);

struct net_bridge *br;

unsigned char *buf;

if (!p)

goto err;

br = p->br;

spin_lock(&br->lock);

if (p->state == BR_STATE_DISABLED || !(br->dev->flags & IFF_UP))

goto out;

/*

insert into forwarding database after filtering to avoid spoofing

根据BPDU 中的MAC 地址来更新CAM 表

*/

br_fdb_update(br, p, eth_hdr(skb)->h_source);

if (!br->stp_enabled)

goto out;

/* need at least the 802 and STP headers */

if (!pskb_may_pull(skb, sizeof(header)+1) ||

memcmp(skb->data, header, sizeof(header)))

goto out;

buf = skb_pull(skb, sizeof(header));

if (buf[0] == BPDU_TYPE_CONFIG) {

struct br_config_bpdu bpdu;

if (!pskb_may_pull(skb, 32))

goto out;

/*

802.3数据包格式:

目的地址 | 源地址 | 长度 | DSAP | SSAP | cntl | org_code | 类型 | 数据

6 6 2 1 1 1 3 2 38~1492

*/

buf = skb->data; // 跳过了数据字段的6个字节,buf 指向了类型字段

/*

STP 的数据包不是以太网格式的,而是802.3格式的,

*/

bpdu.topology_change = (buf[1] & 0x01) ? 1 : 0;

bpdu.topology_change_ack = (buf[1] & 0x80) ? 1 : 0;

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]

(buf[11]

(buf[12]

buf[13];

bpdu.bridge_id.prio[0] = buf[14]; | CRC 4

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]

bpdu.message_age = br_get_ticks(buf+24); // 0

bpdu.max_age = br_get_ticks(buf+26); // 20

bpdu.hello_time = br_get_ticks(buf+28); // 2

bpdu.forward_delay = br_get_ticks(buf+30); // 15

/* 若是收到了config bpdu,则调用下面的函数 */

br_received_config_bpdu(p, &bpdu);

}

/* 若是拓扑变化通知TCN ,则调用下面的函数 */

else if (buf[0] == BPDU_TYPE_TCN) {

br_received_tcn_bpdu(p);

}

out:

spin_unlock(&br->lock);

err:

kfree_skb(skb);

return 0;

六. 数据结构

在处理BPDU 之前,我们先来看一下net_bridge和net_bridge_port这两个数据结构,这两个是很重要的数据结构,如果不理解这两个结构,后面的分析就很难进行了。

struct net_bridge

{

spinlock_t lock;

struct list_head port_list;

struct net_device *dev;

struct net_device_stats statistics;

spinlock_t hash_lock;

struct hlist_head hash[BR_HASH_SIZE];

struct list_head age_list;

unsigned long feature_mask;

/* STP */

bridge_id designated_root; //网络中根桥的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;

u16 root_port;

unsigned char 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;

struct kobject ifobj;

};

-------------------

struct net_bridge_port

{

struct net_bridge *br;

struct net_device *dev;

struct list_head list;

/* STP */

u8 priority;

u8 state;

u16 port_no;

unsigned char topology_change_ack;

unsigned char config_pending;

port_id port_id; //端口的ID ,在从端口发出的BPDU 中,会将这个端口的ID 存入BPDU 中

port_id designated_port;

bridge_id designated_root;

bridge_id designated_bridge;

u32 path_cost;

u32 designated_cost;

struct timer_list forward_delay_timer;

struct timer_list hold_timer;

struct timer_list message_age_timer;

struct kobject kobj;

struct work_struct carrier_check;

struct rcu_head rcu;

};

七. c onfig_bpdu的处理函数--br_received_config_bpdu

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)) { //第 八 部分介绍

br_record_config_information(p, bpdu);

br_configuration_update(br);

br_port_state_selection(br);

if (!br_is_root_bridge(br) && was_root) {

del_timer(&br->hello_timer);

if (br->topology_change_detected) {

del_timer(&br->topology_change_timer);

br_transmit_tcn(br);

mod_timer(&br->tcn_timer,

jiffies + br->bridge_hello_time);

}

}

if (p->port_no == br->root_port) {

br_record_config_timeout_values(br, bpdu);

br_config_bpdu_generation(br);

if (bpdu->topology_change_ack)

br_topology_change_acknowledged(br);

}

}

else if (br_is_designated_port(p)) {

br_reply(p);

}

}

八. b r_supersedes_port_info(p, bpdu)

这个函数,就是把包中的值,同先前指定的对应值进行判断和比较,经确定是否需要更新 /* called under bridge lock */

/* 如果要更新则返回1,不然返回0 */

static int br_supersedes_port_info(struct net_bridge_port *p, struct br_config_bpdu *bpdu) {

int t;

/* 收到的BPDU 的root ID和当前交换机端口上记录的root ID进行比较 */

t = memcmp(&bpdu->root, &p->designated_root, 8);

if (t

return 1;

else if (t > 0)

return 0;

/* 若相等,则往下 */

if (bpdu->root_path_cost

designated_cost)

return 1;

else if (bpdu->root_path_cost > p->designated_cost)

return 0;

t = memcmp(&bpdu->bridge_id, &p->designated_bridge, 8);

if (t

return 1;

else if (t > 0)

return 0;

if (memcmp(&bpdu->bridge_id, &p->br->bridge_id, 8))

return 1;

if (bpdu->port_id designated_port)

return 1;

return 0;

}

参考了独孤九贱大侠的文章,针对2.6.16代码。

零. STP的几个状态:

. Block :阻断状态,接收但不转发数据。

. Listening :侦听状态,不转发数据,可以收发BPDU ,执行选举root bridge,root port,designate port 等动作。

. Learning :学习状态,不转发数据,开始学习MAC ,为数据转发作准备,所以称之为Learning 状态

. Forward :转发状态,转发数据。

. Disable :禁用状态,既不参与STP 计算,也不转发数据。

一. 首先是Bridge 注册过程:

static int __init br_init(void)

{

br_fdb_init();

#ifdef CONFIG_BRIDGE_NETFILTER

if (br_netfilter_init())

return 1;

#endif

brioctl_set(br_ioctl_deviceless_stub);

/* 数据包的处理函数注册为br_handle_frame */

br_handle_frame_hook = br_handle_frame;

br_fdb_get_hook = br_fdb_get;

br_fdb_put_hook = br_fdb_put;

register_netdevice_notifier(&br_device_notifier);

return 0;

}

二. 网桥处理函数

int br_handle_frame(struct net_bridge_port *p, struct sk_buff **pskb)

{

struct sk_buff *skb = *pskb;

const unsigned char *dest = eth_hdr(skb)->h_dest;

if (p->state == BR_STATE_DISABLED)

goto err;

if (!is_valid_ether_addr(eth_hdr(skb)->h_source))

goto err;

/*

网桥打开了STP 功能,

是否为网桥组播地址,PS :bridge_ula定义为 { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 },只比较前5个字段,

为什么第5个字段要和0XF0比较,就不知道了 (01-80-c2-00-00-F0 ???)

*/

if (p->br->stp_enabled &&

!memcmp(dest, bridge_ula, 5) &&

!(dest[5] & 0xF0)) {

if (!dest[5]) {

/* 涉及到STP 功能的数据包都交给函数br_stp_handle_bpdu来处理 */

NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev,

NULL, br_stp_handle_bpdu);

return 1;

}

goto err;

}

/* 如果端口处于Learning 或Forwarding 状态时,要记录从端口通过的数据包的源MAC 地址,以update CAM 表 */

if (p->state == BR_STATE_FORWARDING || p->state == BR_STATE_LEARNING) {

if (br_should_route_hook) {

if (br_should_route_hook(pskb))

return 0;

skb = *pskb;

dest = eth_hdr(skb)->h_dest;

}

if (!compare_ether_addr(p->br->dev->dev_addr, dest))

skb->pkt_type = PACKET_HOST;

/* 在br_handle_frame中更新CAM 表 */

NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,

br_handle_frame_finish);

return 1;

}

err:

kfree_skb(skb);

return 1;

}

三. 接下来我们首先分析更新CAM 表的函数

br_handle_frame_finish,在第4小节再分析STP 的处理函数br_stp_handle_bpdu

int br_handle_frame_finish(struct sk_buff *skb)

{

const unsigned char *dest = eth_hdr(skb)->h_dest;

struct net_bridge_port *p = rcu_dereference(skb->dev->br_port);

struct net_bridge *br;

struct net_bridge_fdb_entry *dst;

int passedup = 0;

if (!p || p->state == BR_STATE_DISABLED)

goto drop;

/*

insert into forwarding database after filtering to avoid spoofing

使用数据包的源MAC 地址来更新CAM 表

*/

br = p->br;

br_fdb_update(br, p, eth_hdr(skb)->h_source);

if (p->state == BR_STATE_LEARNING)

goto drop;

/*

如果网桥的虚拟网卡处于混杂模式,那么每个接收到的数据包都需要克隆一份送到AF_PACKET协议处理 */

if (br->dev->flags & IFF_PROMISC) {

struct sk_buff *skb2;

skb2 = skb_clone(skb, GFP_ATOMIC);

if (skb2 != NULL) {

passedup = 1;

br_pass_frame_up(br, skb2);

}

}

/*

目的MAC 为广播或多播,则需要向本机的上层协议栈传送这个数据包,这里有一个标志变量passedup 用于表示是否传送过了,如果已传送过,那就算了

*/

if (is_multicast_ether_addr(dest)) {

br_flood_forward(br, skb, !passedup);

if (!passedup)

br_pass_frame_up(br, skb);

goto out;

}

dst = __br_fdb_get(br, dest);

if (dst != NULL && dst->is_local) {

if (!passedup)

br_pass_frame_up(br, skb);

else

kfree_skb(skb);

goto out;

}

if (dst != NULL) {

br_forward(dst->dst, skb);

goto out;

}

/* CAM表中没有,只能flood 了。。。。 */

br_flood_forward(br, skb, 0);

out:

return 0;

drop:

kfree_skb(skb);

goto out;

}

四. 在介绍br_stp_handle_bpdu

这个函数之前,我们首先来看一下内核中用来表示一个BPDU 的数据结构是什么 -- br_config_bpdu

struct br_config_bpdu

{

unsigned topology_change:1; /* 拓扑改变标志 */

unsigned topology_change_ack:1; /* 拓扑改变回应标志 */

bridge_id root; /* 根ID ,用于会聚后的网桥网络中,所有配置 BPDU 中的该字段都应该具有相同值(同VLAN ),又可分为两个 BID 子字段:网桥优先级和网桥 MAC 地址 */ int root_path_cost; /* 路径开销,通向有根网桥(Root Bridge)的所有链路的积累资本 */

bridge_id bridge_id; /* 创建当前 BPDU 的网桥 BID */

port_id port_id; /* 端口ID ,每个端口值都是唯一的 */

int message_age; /* 记录 Root Bridge 生成当前 BPDU 起源信息的所消耗时间 */

int max_age; /* 保存 BPDU 的最长时间,也反映了拓朴变化通知(Topology Change Notification)过程中的网桥表生存时间情况 */

int hello_time; /* 周期性配置 BPDU 间的时间 */

int forward_delay; /* 用于在 Listening 和 Learning 状态的时间,也反映了拓朴变化通知(Topology Change Notification)过程中的时间情况 */

};

五. B PDU 处理函数br_stp_handle_bpdu

int br_stp_handle_bpdu(struct sk_buff *skb)

{

struct net_bridge_port *p = rcu_dereference(skb->dev->br_port);

struct net_bridge *br;

unsigned char *buf;

if (!p)

goto err;

br = p->br;

spin_lock(&br->lock);

if (p->state == BR_STATE_DISABLED || !(br->dev->flags & IFF_UP))

goto out;

/*

insert into forwarding database after filtering to avoid spoofing

根据BPDU 中的MAC 地址来更新CAM 表

*/

br_fdb_update(br, p, eth_hdr(skb)->h_source);

if (!br->stp_enabled)

goto out;

/* need at least the 802 and STP headers */

if (!pskb_may_pull(skb, sizeof(header)+1) ||

memcmp(skb->data, header, sizeof(header)))

goto out;

buf = skb_pull(skb, sizeof(header));

if (buf[0] == BPDU_TYPE_CONFIG) {

struct br_config_bpdu bpdu;

if (!pskb_may_pull(skb, 32))

goto out;

/*

802.3数据包格式:

目的地址 | 源地址 | 长度 | DSAP | SSAP | cntl | org_code | 类型 | 数据

6 6 2 1 1 1 3 2 38~1492

*/

buf = skb->data; // 跳过了数据字段的6个字节,buf 指向了类型字段

/*

STP 的数据包不是以太网格式的,而是802.3格式的,

*/

bpdu.topology_change = (buf[1] & 0x01) ? 1 : 0;

bpdu.topology_change_ack = (buf[1] & 0x80) ? 1 : 0;

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]

(buf[11]

(buf[12]

buf[13];

bpdu.bridge_id.prio[0] = buf[14]; | CRC 4

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]

bpdu.message_age = br_get_ticks(buf+24); // 0

bpdu.max_age = br_get_ticks(buf+26); // 20

bpdu.hello_time = br_get_ticks(buf+28); // 2

bpdu.forward_delay = br_get_ticks(buf+30); // 15

/* 若是收到了config bpdu,则调用下面的函数 */

br_received_config_bpdu(p, &bpdu);

}

/* 若是拓扑变化通知TCN ,则调用下面的函数 */

else if (buf[0] == BPDU_TYPE_TCN) {

br_received_tcn_bpdu(p);

}

out:

spin_unlock(&br->lock);

err:

kfree_skb(skb);

return 0;

六. 数据结构

在处理BPDU 之前,我们先来看一下net_bridge和net_bridge_port这两个数据结构,这两个是很重要的数据结构,如果不理解这两个结构,后面的分析就很难进行了。

struct net_bridge

{

spinlock_t lock;

struct list_head port_list;

struct net_device *dev;

struct net_device_stats statistics;

spinlock_t hash_lock;

struct hlist_head hash[BR_HASH_SIZE];

struct list_head age_list;

unsigned long feature_mask;

/* STP */

bridge_id designated_root; //网络中根桥的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;

u16 root_port;

unsigned char 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;

struct kobject ifobj;

};

-------------------

struct net_bridge_port

{

struct net_bridge *br;

struct net_device *dev;

struct list_head list;

/* STP */

u8 priority;

u8 state;

u16 port_no;

unsigned char topology_change_ack;

unsigned char config_pending;

port_id port_id; //端口的ID ,在从端口发出的BPDU 中,会将这个端口的ID 存入BPDU 中

port_id designated_port;

bridge_id designated_root;

bridge_id designated_bridge;

u32 path_cost;

u32 designated_cost;

struct timer_list forward_delay_timer;

struct timer_list hold_timer;

struct timer_list message_age_timer;

struct kobject kobj;

struct work_struct carrier_check;

struct rcu_head rcu;

};

七. c onfig_bpdu的处理函数--br_received_config_bpdu

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)) { //第 八 部分介绍

br_record_config_information(p, bpdu);

br_configuration_update(br);

br_port_state_selection(br);

if (!br_is_root_bridge(br) && was_root) {

del_timer(&br->hello_timer);

if (br->topology_change_detected) {

del_timer(&br->topology_change_timer);

br_transmit_tcn(br);

mod_timer(&br->tcn_timer,

jiffies + br->bridge_hello_time);

}

}

if (p->port_no == br->root_port) {

br_record_config_timeout_values(br, bpdu);

br_config_bpdu_generation(br);

if (bpdu->topology_change_ack)

br_topology_change_acknowledged(br);

}

}

else if (br_is_designated_port(p)) {

br_reply(p);

}

}

八. b r_supersedes_port_info(p, bpdu)

这个函数,就是把包中的值,同先前指定的对应值进行判断和比较,经确定是否需要更新 /* called under bridge lock */

/* 如果要更新则返回1,不然返回0 */

static int br_supersedes_port_info(struct net_bridge_port *p, struct br_config_bpdu *bpdu) {

int t;

/* 收到的BPDU 的root ID和当前交换机端口上记录的root ID进行比较 */

t = memcmp(&bpdu->root, &p->designated_root, 8);

if (t

return 1;

else if (t > 0)

return 0;

/* 若相等,则往下 */

if (bpdu->root_path_cost

designated_cost)

return 1;

else if (bpdu->root_path_cost > p->designated_cost)

return 0;

t = memcmp(&bpdu->bridge_id, &p->designated_bridge, 8);

if (t

return 1;

else if (t > 0)

return 0;

if (memcmp(&bpdu->bridge_id, &p->br->bridge_id, 8))

return 1;

if (bpdu->port_id designated_port)

return 1;

return 0;

}


相关内容

  • 网络连接技术
  • 第三章 网络连接技术 3.1 网络传输媒介及连线设备 网络上数据的传输需要有"传输媒体",这好比是车辆必须在公路上行驶一样,道路质量的好坏会影响到行车的安全舒适.同样,网络传输媒介的质量好坏也会影响数据传输的质量,包括速率.数据丢失等. 常用的网络传输媒介可分为两类:一类是有线的 ...

  • 二三层交换机基础知识介绍
  • 目 录 一. 以太网基本概念 . ...................................................... 1 1.1 以太网帧格式 . ........................................................ 1 1.2 ...

  • 以太网的工作原理
  • 以太网的工作原理 收藏此信息 打印该信息 添加:用户发布 来源:未知 在今天的商务世界中,可靠.高效地获取信息已经成为实现竞争优势所必不可少的重要资产.文件柜和堆积如山的文件已经让位于以电子方式存储和管理信息的计算机.相距千里之遥的同事可以在瞬间共享信息,同一办公场所的数百位员工可以同时查看网络上的 ...

  • 发展与演变:谈网桥与交换机的区别
  • 网桥 PATTON以太微型网桥 网桥(Bridge)也称为桥接器,是连接两个局域网的存储转发设备,用它可以使完全具有相同或相似体系结构网络系统的连接,这样不但能扩展网络的距离或范围,而且可提高网络的性能.可靠性和安全性.网桥工作在数据链路层,将两个LAN连起来,根据MAC地址来转发帧,可以看作一个& ...

  • 网络设备选择题
  • 练习题 一.单选题: 1.在一个以太网中,30台PC 通过CISCO R2501路由器S0口连接INTERNET ,QUIDWAY R2500 路由器配置如下: Cisco(config-if)#ip address 192.168.1.1.255.255.255.0 Cisco(config-if ...

  • 4047+计算机网络
  • 2012年秋期成人教育(本科) <计算机网络>课程期末复习指导 2012年12月修订 第一部分 课程考核说明 1.考核目的 通过本次考试,了解学生对本课程基本内容和重难点的掌握程度,以及运用本课程的基本知识.基本理论和基本方法来分析和解决计算机组网以及相应网管的理解和案例分析能力. 2. ...

  • 计算机网络第四版参考答案第四章
  • 第四章 局域网(P135) 1.局域网的主要特点是什么?为什么说局域网是一个通信网? 答:局域网LAN 是指在较小的地理范围内,将有限的通信设备互联起来的计算机通信网络. 从功能的角度来看,局域网具有以下几个特点:①共享传输信道.在局域网中,多个系统连接到一个共享的通信媒体上.②地理范围有限,用户个 ...

  • 计算机网络作业答案3
  • 2使用如图所示的网络例子,在建立以下的连续之后,给出所有交换机的虚电路表.假设 连续的序列是累积的,即建立第二个连续时第一个连续仍未断开,以此类推.同时,假设VCI 的分配始终选择每条链路上最低的未使用的VCI ,从0开始. (a )主机D 连到主机H (b )主机B 连到主机G 解: (c) 主机 ...

  • 网络硬件设备
  • 网络硬件设备发展现状及其趋势 一. 主要硬件设备 1.网卡 网络接口卡(Network Interface Card,NIC),又称网卡或网络适配器,工作在数据链路层的网络组件,是主机 和网络的接口,用于协调主机与网络间数据. 指令或信息的发送与接收,硬件结构如右图 所示.在发送方,把主机产生的串行 ...