博所搬至http://xiaogr.com
全部博文(79)
分类: LINUX
2007-10-25 18:47:13
OK。到这里为止,网桥的配置已经讲述完了。我们来看一下网桥是怎么对数据包进行处理的
网桥对接收数据的处理:
回到本章的开始的handle_bridge函数,会调用br_handle_frame_hook进行接收数据的处理
在网桥的初始化代码中,把br_handle_frame_hook赋值为了br_handle_frame
没错,这就是网桥的处理函数。跟进个函数
nt br_handle_frame(struct net_bridge_port *p, struct sk_buff **pskb)
{
struct sk_buff *skb = *pskb;
//目的mac地址
const unsigned char *dest = eth_hdr(skb)->h_dest;
//端口禁用
if (p->state == BR_STATE_DISABLED)
goto err;
//源mac 为多播或者广播,丢弃
//FF.XX.XX.XX.XX.XX形式
if (eth_hdr(skb)->h_source[0] & 1)
goto err;
//如果状态为学习或者转发,则学习源mac 更新CAM 表
if (p->state == BR_STATE_LEARNING ||
p->state == BR_STATE_FORWARDING)
// br_fdb_insert函数我们在前面已经分析过了
br_fdb_insert(p->br, p, eth_hdr(skb)->h_source, 0);
//stp 的处理,stp-enabled 是否启用stp 协议
//bridge_ula stp使用的多播mac地址
if (p->br->stp_enabled &&
!memcmp(dest, bridge_ula, 5) &&
!(dest[5] & 0xF0)) {
if (!dest[5]) {
NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev,
NULL, br_stp_handle_bpdu);
return 1;
}
}
else if (p->state == BR_STATE_FORWARDING) {
//在初始化中,并末对br_should_route_hook进行赋值
//所以br_should_route_hook为假
if (br_should_route_hook) {
if (br_should_route_hook(pskb))
return 0;
skb = *pskb;
dest = eth_hdr(skb)->h_dest;
}
//目的地址与桥地址相同。则传与上层处理
//置skb->pkt_type = PACKET_HOST
if (!memcmp(p->br->dev->dev_addr, dest, ETH_ALEN))
skb->pkt_type = PACKET_HOST;
//网桥在NF_BR_PRE_ROUTING点上的netfiter处理
NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
br_handle_frame_finish);
return 1;
}
err:
kfree_skb(skb);
return 1;
}
在这个函数里,进行相关的入口判断之后,会把当前数据包的源MAC与接口对应更新到CAM表中,更新函数br_fdb_insert()在前面已经分析过了,不太明白的可以倒过去看下,不过注意了,这是不是做为静态项插入的。
接着判断包是不是传给本机的,如果是,则置包的pkt_type为PACKET_HOST
关于NF_HOOK()宏,我们在以后的netfiter中有专题分析。这是我们只要知道,正常的数据包会流进br_handle_frame_finish()进行处理
/* note: already called with rcu_read_lock (preempt_disabled) */
int br_handle_frame_finish(struct sk_buff *skb)
{
//取得目的MAC地址
const unsigned char *dest = eth_hdr(skb)->h_dest;
struct net_bridge_port *p = skb->dev->br_port;
struct net_bridge *br = p->br;
struct net_bridge_fdb_entry *dst;
int 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为传送标志,为1 时表示已经上传过了
if (dest[0] & 1) {
br_flood_forward(br, skb, !passedup);
if (!passedup)
br_pass_frame_up(br, skb);
goto out;
}
//查询CAM 表
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;
}
//如果查询不到,在其它端口上都发送此包
br_flood_forward(br, skb, 0);
out:
return 0;
}
在这里函数里,通过查找CAM表,取得发送端口,如果当前CAM表里没有到目的MAC的端口,则在其它端口上都发送此数据包。
在这个函数里,我们看到,查询CAM表的函数为:__br_fdb_get()
接着分析一下此函数
struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br,
const unsigned char *addr)
{
struct hlist_node *h;
struct net_bridge_fdb_entry *fdb;
//遍历对应MAC哈希项中的fdb
hlist_for_each_entry_rcu(fdb, h, &br->hash[br_mac_hash(addr)], hlist) {
if (!memcmp(fdb->addr.addr, addr, ETH_ALEN)) {
if (unlikely(has_expired(br, fdb)))
break;
return fdb;
}
}
return NULL;
}
这个函数非常容易,首先取得目的MAC对应的哈希项。然后再遍历里面的数据,查看是否含有目的地址的项。如果是送给本机的数据包,则传至上层协议,如不是,则需要转发。关于上层怎么处理,以及如何转发。请在后续章节中讨论。