Linux桥接功能的分析.docx
- 文档编号:30272960
- 上传时间:2023-08-13
- 格式:DOCX
- 页数:14
- 大小:140.36KB
Linux桥接功能的分析.docx
《Linux桥接功能的分析.docx》由会员分享,可在线阅读,更多相关《Linux桥接功能的分析.docx(14页珍藏版)》请在冰豆网上搜索。
Linux桥接功能的分析
网桥设计与实现相关知识
文档编号:
00-6201-100
当前版本:
1.0.0.0
创建日期:
2011-8-17
编写作者:
ganjingwei
网桥知识总结
前言2
关于此文档2
参考资料3
第一章网桥的基本概念4
1.1网桥的作用4
1.2网桥的工作原理4
第二章Linux桥功能5
2.1数据包流程5
2.2相关代码6
2.2.1br_handle_frame函数6
2.2.2br_handle_frame_finish函数6
前言
关于此文档
此文档是本人这段时间内学习Linux网络协议栈网桥功能相关知识,总结并且整理出来的文档。
供大家参考。
本文档描述Linux相关知识,各章节说明如下:
1前言,即此章节;
2网桥理论知识;
3重要数据结构
4网桥和端口的创建过程
5Linux网桥的实现及数据包流程;
参考资料
网络资源。
本文中的所有代码以broadcom4.12L.01为依据,内核代码为2.6。
第一章网桥的基本概念
1.1网桥的作用
网桥工作在ISO层次结构中的二层,通过mac地址来区分,实现几个网络或主机之间的互联。
1.2网桥的工作原理
网桥的工作原理其实很简单,就是:
某个主机的消息从哪个端口进来,那么这个主机必定位与这个端口这一侧,从这个端口一定能找到这个主机,给这个主机的消息也就应该从这个端口转发。
比如,一个网桥连接主机A和主机B。
当主机A连接到网桥时,会告诉网桥,它在端口A。
网桥会维持一个映射表,让端口A与主机A的mac地址对应。
当有其他主机试图通过网桥转发目的mac地址为主机A的mac地址的消息时,网桥会通过查表来指导应该由端口A转发。
同理,当网桥连接两个或两个以上主机或主机构成的网络时,也按照这个原则,维持一个记录端口和mac地址的映射表,需要转发消息的时候通过查找这个表来找到应该去的地址。
当然,如果收到的消息是发给网桥本身的,就要交给上层协议来处理。
这就是网桥的工作原理。
第二章数据结构
2.1net_bridge
这个结构体描述一个网桥。
dev是这个桥对应的设备。
port_list是net_bridge_port的链表头。
hash[BR_HASH_SIZE]是net_bridge_fdb_entry的散列表,也是网桥MAC地址和端口的映射表CAM。
structnet_bridge
{
spinlock_tlock;
structlist_headport_list;
structnet_device*dev;
structnet_device_statsstatistics;
spinlock_thash_lock;
structhlist_headhash[BR_HASH_SIZE];
structlist_headage_list;
unsignedlongfeature_mask;
unsignedlongflags;
bridge_iddesignated_root;
bridge_idbridge_id;
u32root_path_cost;
unsignedlongmax_age;
unsignedlonghello_time;
unsignedlongforward_delay;
unsignedlongbridge_max_age;
unsignedlongageing_time;
unsignedlongbridge_hello_time;
unsignedlongbridge_forward_delay;
u8group_addr[ETH_ALEN];
u16root_port;
enum{
BR_NO_STP,/*nospanningtree*/
BR_KERNEL_STP,/*oldSTPinkernel*/
BR_USER_STP,/*newRSTPinuserspace*/
}stp_enabled;
unsignedchartopology_change;
unsignedchartopology_change_detected;
structtimer_listhello_timer;
structtimer_listtcn_timer;
structtimer_listtopology_change_timer;
structtimer_listgc_timer;
structkobject*ifobj;
};
2.2net_bridge_port
这是网桥端口的结构体。
br指向它属于的网桥。
port_no是端口ID,唯一的标识。
structnet_bridge_port
{
structnet_bridge*br;
structnet_device*dev;
structlist_headlist;
u8priority;
u8state;
u16port_no;
unsignedchartopology_change_ack;
unsignedcharconfig_pending;
port_idport_id;
port_iddesignated_port;
bridge_iddesignated_root;
bridge_iddesignated_bridge;
u32path_cost;
u32designated_cost;
structtimer_listforward_delay_timer;
structtimer_listhold_timer;
structtimer_listmessage_age_timer;
structkobjectkobj;
structrcu_headrcu;
};
2.3net_bridge_fdb_entry
这是散列表的中间链接结构体,net_bridge中的hash,通过这个结构链入net_bridge_port。
其中包含了端口的一些信息,最重要的是local,从它可以知道这个端口信息是本地的,还是其他相连主机的。
structnet_bridge_fdb_entry
{
structhlist_nodehlist;
structnet_bridge_port*dst;
structrcu_headrcu;
atomic_tuse_count;
unsignedlongageing_timer;
mac_addraddr;
unsignedcharis_local;
unsignedcharis_static;
};
第三章桥的创建
3.1br_add_bridge[net\bridge\br_if.c]
当使用SIOCBRADDBR调用ioctl时,会通过br_add_bridge函数创建新的网桥。
在这个函数中配置一个新网桥设备的初始参数。
这个函数在br_if.c中。
在这里申请一个桥设备。
dev = new_bridge_dev(net, name);
然后给设备命名,并注册设备和设备文件。
ret=dev_alloc_name(dev,dev->name);
ret=register_netdevice(dev);
ret=br_sysfs_addbr(dev);
3.2br_add_if()[net\bridge\br_if.c]
当使用SIOCBRADDIF调用ioctl时,会通过调用br_add_if向网卡加入新的端口。
创建新的net_bridge_portp,会从br->port_list中分配一个未用的port_no,p->br会指向br,p->state设为BR_STATE_DISABLED。
这里的p实际代表的就是网卡设备。
p=new_nbp(br,dev);
将新创建的p加入CAM表中,CAM表是用来记录mac地址与物理端口的对应关系;而刚刚创建了p,因此也要加入CAM表中,并且该表项应是local的(具体关系如图3-1所示),可以看到,CAM表在实现中作为net_bridge的hash表,以addr作为hash值,链入net_bridge_fdb_entry,再由它的dst指向net_bridge_port。
err=br_fdb_insert(br,p,dev->dev_addr);
图3-1桥映射表结构
br_fdb_insert函数中,如果这个物理地址已经存在记录,则删除原有的记录,保存新的记录。
但是如果是本地地址,直接返回。
fdb=fdb_find(head,addr);
if(fdb){
if(fdb->is_local)
return0;
printk(KERN_WARNING"%saddinginterfacewithsameaddress""asareceivedpacket\n",source->dev->name);
fdb_delete(br,fdb);
}
if(!
fdb_create(br,head,source,addr,1,1))
return-ENOMEM;
return0;
设备的br_port指向p。
这里要明白的是,net_bridge可以看作全局量,是网桥,而net_bridge_port则是与网卡相对应的端口,因此每个设备dev有个指针br_port指向该端口。
rcu_assign_pointer(dev->br_port,p);
将新创建的net_bridge_port加入br的链表port_list中,在创建新的net_bridge_port时,会分配一个未用的port_no,而这个port_no就是根据br->port_list中的已经添加的net_bridge_port来找到未用的port_no的。
具体过程如图3-2所示。
list_add_rcu(&p->list,&br->port_list);
图3-2端口链表
重新计算网桥的ID,这里根据br->port_list链表中的net_bridge_port的最小的addr来作为网桥的ID。
br_stp_recalculate_bridge_id(br);
到这里,新网桥的创建就完成了。
第四章Linux桥功能
4.1数据包流程
图4-1Linux桥的数据包函数调用流程
如图4-1所示,netif_receive_skb是数据包的基本接收函数,由网卡驱动调用,把网卡接收到并初步处理的数据包传递给链路层。
这个函数会首先根据设备类型做一个类型判断,这里的类型主要针对有没有master来区别。
如果找到匹配的,就交给对应的协议处理。
handle_bridge是桥功能开始的操作函数,在这里如果发现是回环类型的数据包,或者是br_port为空的数据包,则会直接返回到netif_receive_skb函数,进而进入以太网帧的协议类型的匹配来调用deliver_skb提交给上层操作,否则进入桥的下一步操作。
如果设备不作为网桥,也会调用deliver_skb,提交给上层。
deliver_skb通过不同的协议类型提交给不同的接收函数。
br_handle_frame函数,会先进入一个判断,判断数据包的目的地址是否为多播组地址,是则进入BR_LOCAL_IN,进而进入一些操作,更新桥接转发表CAM。
如果判断为否,则进入桥接的route阶段,进入BR_PRE_ROUTING这个HOOK点,然后调用br_handle_frame_finish。
br_handle_frame_finish中,先调用__br_fdb_get函数,这个函数完全目的地址的查找。
如果目的地址是本身,则调用br_pass_frame_up函数,并进而调用netif_receive_skb。
但此时,skb的设备已经不是之前的端口设备,而变成了br0,br_port也为空,所以根据之前的判断,handle_bridge直接返回。
因为这个数据包时需要提交给上层的,要通过桥组处理。
如果端口处于LEARNING状态,则只是学习到CAM表中,而不对报文作任何处理,所以丢弃掉报文。
端口已处于FORWARDING状态,此时分情况:
1.如果报文是多播的,则br_flood_forward(br,skb,skb2);
2.如果报文是单播的,但不在网桥的CAM表中,则br_flood_forward(br,skb,skb2);
3.如果报文是单播的,在网桥的CAM表中,但不是发往本机,则br_forward(dst->dst,skb,skb2);
4.如果报文是单播的,在网桥的CAM表中,且是发往本机,则br_pass_frame_up(skb2);
4.2CAM查找流程
CAM的查找比较简单:
br_handle_frame_finish
->__br_fdb_get
在__br_fdb_get中,先用mac地址计算出hash值,用这个值定位到br->hash的数组中,然后用得到的表头去查找表项。
如果找到,返回这个entry;没找到返回NULL。
4.3相关代码
4.3.1netif_receive_skb函数
……………………………………
null_or_orig=NULL;
orig_dev=skb->dev;
if(orig_dev->master){
if(skb_bond_should_drop(skb))
{
null_or_orig=orig_dev;/*deliveronlyexactmatch*/
}
else
skb->dev=orig_dev->master;
}
……………………………………
list_for_each_entry_rcu(ptype,&ptype_all,list){
if(ptype->dev==null_or_orig||ptype->dev==skb->dev||
ptype->dev==orig_dev){
if(pt_prev)
ret=deliver_skb(skb,pt_prev,orig_dev);
pt_prev=ptype;
}
}
……………………………………
skb=handle_bridge(skb,&pt_prev,&ret,orig_dev);
……………………………………
ype=skb->protocol;
list_for_each_entry_rcu(ptype,
&ptype_base[ntohs(type)&PTYPE_HASH_MASK],list){
if(ptype->type==type&&
(ptype->dev==null_or_orig||ptype->dev==skb->dev||
ptype->dev==orig_dev)){
if(pt_prev)
ret=deliver_skb(skb,pt_prev,orig_dev);
pt_prev=ptype;
}
}
if(pt_prev){
ret=pt_prev->func(skb,skb->dev,pt_prev,orig_dev);
}else{
kfree_skb(skb);
ret=NET_RX_DROP;
}
……………………………………
4.3.2br_handle_frame函数
……………………………………
if(unlikely(is_link_local(dest))){//如果是这个多播组的判断
if(NF_HOOK(PF_BRIDGE,NF_BR_LOCAL_IN,skb,skb->dev,
NULL,br_handle_local_finish))
returnNULL;/*frameconsumedbyfilter*/
else
returnskb;/*continueprocessing*/
}
……………………………………
NF_HOOK(PF_BRIDGE,NF_BR_PRE_ROUTING,skb,skb->dev,NULL,
br_handle_frame_finish);//转发数据包
……………………………………
4.3.3br_handle_frame_finish函数
……………………………………
br_fdb_update(br,p,eth_hdr(skb)->h_source);//更新桥转发表
……………………………………
dst=__br_fdb_get(br,dest);//查找转发表,得到目的地址
……………………………………
if(skb2)
br_pass_frame_up(br,skb2);//这个数据包时发给网桥设备本身的,需要提交给上层
if(skb){
if(dst)
br_forward(dst->dst,skb);//这个数据包时需要通过网桥转发的
else
br_flood_forward(br,skb);//这个数据包在CAM中没有找到去处,应该广播它
}
……………………………………
4.3.4__br_fdb_get函数
structhlist_node*h;
structnet_bridge_fdb_entry*fdb;
hlist_for_each_entry_rcu(fdb,h,&br->hash[br_mac_hash(addr)],hlist){
if(!
compare_ether_addr(fdb->addr.addr,addr)){
if(unlikely(has_expired(br,fdb)))
break;
returnfdb;
}
}
returnNULL;
4.3.5br_fdb_insert->fdb_insert函数
……………………………………
fdb=fdb_find(head,addr);
if(fdb){//如果已经存在
if(fdb->is_local)//此表项是本地端口表项,什么也不做
return0;
fdb_delete(br,fdb);//删除之前的表项
if(!
fdb_create(br,head,source,addr,1,1))//创建新表项
return-ENOMEM;
return0;
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Linux 功能 分析