众所周之,网桥之所以是网桥,比HUB更智能,是因为它有一个MAC-PORT的表,这样转发数据就不用广播,而查表定端口就可以了
每次收到一个包,网桥都会学习其来源MAC,添加进这个表。Linux中这个表叫CAM表(这个名字是其它资料上看的)。如果桥的状态是LEARNING或FORWARDING(学习或转发),则学习该包的源地址skb->mac.ethernet->h_source,把其添加到CAM表中,如果已经存在于表中了,则更新定时器,br_fdb_insert完成了这一过程
if (p->state == BR_STATE_LEARNING ||
p->state == BR_STATE_FORWARDING)
br_fdb_insert(br, p, skb->mac.ethernet->h_source, 0);
|
STP协议的BPDU包的目的MAC采用的是多播目标MAC地址:从01-80-c2-00-00-00(Bridge_group_addr:网桥组多播地址)开始.所以这里是如果开启了STP,而当前数据包又是一个BPDU(!memcmp(dest, bridge_ula, 5), unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 },则交由相应函数处理。if (br->stp_enabled &&,这里只比较前5个字节,没有仔细研究过STP是使用了全部多播地址(从0 1 : 0 0 : 5 e : 0 0 : 0 0 : 0 0到0 1 : 0 0 : 5 e : 7 f : ff : ff。),还是只使用了一部份,这里看来似乎只是一部份,没去深究了
!memcmp(dest, bridge_ula, 5) &&
!(dest[5] & 0xF0)) /*01-80-c2-00-00-F0 是一个什么地址?为什么要判断呢?*/
goto handle_special_frame;
/*处理钩子函数,然后转交br_handle_frame_finish函数继续处理*/
if (p->state == BR_STATE_FORWARDING) {
NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
br_handle_frame_finish);
read_unlock(&br->lock);
return;
}
err:
read_unlock(&br->lock);
err_nolock:
kfree_skb(skb);
return;
handle_special_frame:
if (!dest[5]) {
br_stp_handle_bpdu(skb);
return;
}
kfree_skb(skb);
}
|
四、br_handle_frame_finish
static int br_handle_frame_finish(struct sk_buff *skb)
{
struct net_bridge *br;
unsigned char *dest;
struct net_bridge_fdb_entry *dst;
struct net_bridge_port *p;
int passedup;
/*前面基本相同*/
dest = skb->mac.ethernet->h_dest;
p = skb->dev->br_port;
if (p == NULL)
goto err_nolock;
br = p->br;
read_lock(&br->lock);
if (skb->dev->br_port == NULL)
goto err;
passedup = 0;
/*如果网桥的虚拟网卡处于混杂模式,那么每个接收到的数据包都需要克隆一份
送到AF_PACKET协议处理体(网络软中断函数net_rx_action中ptype_all链的处理)。*/
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 (dest[0] & 1) {
br_flood_forward(br, skb, !passedup);
if (!passedup)
br_pass_frame_up(br, skb);
goto out;
}
/*Linux中的MAC-PORT表是CAM表,这里根据目的地址来查表,
以确定由哪个接口把包转发出去
每一个表项是通过结构struct net_bridge_fdb_entry来描述的:
struct net_bridge_fdb_entry
{
struct net_bridge_fdb_entry *next_hash; //用于CAM表连接的链表指针
struct net_bridge_fdb_entry **pprev_hash; //为什么是pprev不是prev呢?还没有仔细去研究
atomic_t use_count; //此项当前的引用计数器
mac_addr addr; //MAC地址
struct net_bridge_port *dst; //此项所对应的物理端口
unsigned long ageing_timer; //处理MAC超时
unsigned is_local:1; //是否是本机的MAC地址
unsigned is_static:1; //是否是静态MAC地址
};*/
dst = br_fdb_get(br, dest);
/*查询CAM表后,如果能够找到表项,并且目的MAC是到本机的虚拟网卡的,
那么就需要把这个包提交给上层协议,
这样,我们就可以通过这个虚拟网卡的地址来远程管理网桥了*/
if (dst != NULL && dst->is_local) {
if (!passedup)
br_pass_frame_up(br, skb);
else
kfree_skb(skb);
br_fdb_put(dst);
goto out;
}
/*查到表了,且不是本地虚拟网卡的,转发之*/
if (dst != NULL) {
br_forward(dst->dst, skb);
br_fdb_put(dst);
goto out;
}
/*如果表里边查不到,那么只好学习学习HUB了……*/
br_flood_forward(br, skb, 0);
out:
read_unlock(&br->lock);
return 0;
err:
read_unlock(&br->lock);
err_nolock:
kfree_skb(skb);
return 0;
}
|
如果您对本文有任何疑问或者建议,请到讨论区发表您的意见:
>>
论坛入口 <<
上一篇:
Linux操作系统设备特性及设备管理分析 下一篇:
用Shell编程实现DOS风格Linux命令行
【文章评论】
【收藏本文】
【推荐好友】
【打印本文】
【我要投稿】 【论坛讨论】