经讲述完了。我们来看一下网桥是怎么对数据包进行处理的 网桥对接收数据的处理: 回到本章的开始的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() |
|
来自: enchen008 > 《kernel-v2.6》