linux协议栈之网桥实现之一.docx
《linux协议栈之网桥实现之一.docx》由会员分享,可在线阅读,更多相关《linux协议栈之网桥实现之一.docx(9页珍藏版)》请在冰豆网上搜索。
![linux协议栈之网桥实现之一.docx](https://file1.bdocx.com/fileroot1/2023-1/1/31d7d3b4-5aa9-463b-966d-6129196d9f0e/31d7d3b4-5aa9-463b-966d-6129196d9f0e1.gif)
linux协议栈之网桥实现之一
linux协议栈之网桥实现之一
网卡驱动最个函数netif_receive_skb.就从说起。
简单起见,去掉里面预编译代码intnetif_receive_skb(structsk_buff*skb) (net/core/dev.c)
{
structpacket_type*ptype,*pt_prev;
intret=NET_RX_DROP;
unsignedshorttype;
//打接收时间戳
if(!
skb->stamp.tv_sec)
net_timestamp(&skb->stamp);
//如果存dev->master。
则更新相应指针
skb_bond(skb);
//更新CPU接收统计数据
__get_cpu_var(netdev_rx_stat).total++;
skb->h.raw=skb->nh.raw=skb->data;
skb->mac_len=skb->nh.raw-skb->mac.raw;
pt_prev=NULL;
rcu_read_lock();
//处理所有协议模块
list_for_each_entry_rcu(ptype,&ptype_all,list){
if(!
ptype->dev||ptype->dev==skb->dev){
if(pt_prev)
ret=deliver_skb(skb,pt_prev);
pt_prev=ptype;
}
}
//分片处理
handle_diverter(skb);
//网桥处理
if(handle_bridge(&skb,&pt_prev,&ret))
gotoout;
type=skb->protocol;
//协议调相应模块处理。
list_for_each_entry_rcu(ptype,&ptype_base[ntohs(type)&15],list){
if(ptype->type==type&&
(!
ptype->dev||ptype->dev==skb->dev)){
if(pt_prev)
ret=deliver_skb(skb,pt_prev);
pt_prev=ptype;
}
}
if(pt_prev){
ret=pt_prev->func(skb,skb->dev,pt_prev);
}else{
kfree_skb(skb);
/*Jamal,nowyouwillnotabletoescapeexplaining
*mehowyouweregoingtousethis.:
-)
*/
ret=NET_RX_DROP;
}
out:
rcu_read_unlock();
returnret;
}此函数主完成分片重组,网桥处理,根据不同协议调不同传输层处理模块。
本节重点概述linux网桥实现处理。
传输层协议分层续章节陆续给出。
进入网桥处理代码:
#ifdefined(CONFIG_BRIDGE)||defined(CONFIG_BRIDGE_MODULE) (net/core/dev.c)
int(*br_handle_frame_hook)(structnet_bridge_port*p,structsk_buff**pskb);
static__inline__inthandle_bridge(structsk_buff**pskb,
structpacket_type**pt_prev,int*ret)
{
structnet_bridge_port*port;
//回环接口?
非以太网接口?
if((*pskb)->pkt_type==PACKET_LOOPBACK||
(port=rcu_dereference((*pskb)->dev->br_port))==NULL)
return0;
if(*pt_prev){
*ret=deliver_skb(*pskb,*pt_prev);
*pt_prev=NULL;
}
//br_handle_frame_hook个全局函数指针
returnbr_handle_frame_hook(port,pskb);
}
#else
#definehandle_bridge(skb,pt_prev,ret)(0)
#endif
从此以看出。
如果编译时候选择网桥模式,则进入网桥处理模块,否则,只个空函数,直接返回。
br_handle_frame_hook代表函数什么呢?
网桥数据处理框架又什么样呢?
关于网桥:
网桥个二层设备,深入以当成个二层交换机。
二层协议转发数据。
网桥转发数据,维持个端口MAC应表,通常通CAM表。
根据这张表以数据送往相应端口进行发送.网桥转发过程:
1:
接收个包。
判断自己CAM表否含包此包源地址.如果没有,则源地址端口更新至于CAM表.2:
判断包否送给本机,如果,则送往本机层协议栈处理。
如果不,则查寻CAM表。
找相应出口。
3:
如果找出口,则此包送至出口。
如果不存,各端口发送。
4:
如果CAM表应表项规定时间没有得更新,则除此项。
网桥配置:
Brctl个比较好配置网桥工具。
源代码配置极其简单。
们从网桥配置流程说起,看linux核怎样步步管理。
首先,创建个网桥:
brctladdbrbr0 (建立个br0网桥)然,接口添加进网桥:
brctladdif br0eth0 (eth0eth1添加进网桥br0) brctladdif broeth1OK,网桥现就配置好。
这台linux主机以当作交换机使,从eth0包都以转发eth1。
现,们看代码进行处理首先brctladdbr。
查看brctl代码调:
ioctl(br_socket_fd,SIOCBRADDBR,brname);然brctladdif brctl代码调:
ioctl(br_socket_fd,SIOCBRADDIF,&ifr);呵呵。
Brctl代码简单吧,只调户空间配置工具ioctl.Linux网桥分析:
好,现就以进入核分析网桥模式:
staticint__initbr_init(void) (net/brige/br.c)
{
//分配slab缓冲区
br_fdb_init();
//网桥netfiter处理,以章节分析
#ifdefCONFIG_BRIDGE_NETFILTER
if(br_netfilter_init())
return1;
#endif
//户空间ioctl调函数
brioctl_set(br_ioctl_deviceless_stub);
//接收数据包处理,就们面netif_receive_skb函数看br_handle_frame_hook
br_handle_frame_hook=br_handle_frame;
#ifdefined(CONFIG_ATM_LANE)||defined(CONFIG_ATM_LANE_MODULE)
br_fdb_get_hook=br_fdb_get;
br_fdb_put_hook=br_fdb_put;
#endif
//netdev_chain通知链表注册。
关于通知链表,面已经介绍过,这里不再讨论
register_netdevice_notifier(&br_device_notifier);
return0;
}新建网桥:
从面分析以知道,户空间调ioctl(br_socket_fd,SIOCBRADDBR,brname).进入br_ioctl_deviceless_stub,以看相关处理:
intbr_ioctl_deviceless_stub(unsignedintcmd,void__user*uarg)
{
switch(cmd){
caseSIOCGIFBR:
caseSIOCSIFBR:
returnold_deviceless(uarg);
//新建网桥
caseSIOCBRADDBR:
//除网桥
caseSIOCBRDELBR:
{
charbuf[IFNAMSIZ];
if(!
capable(CAP_NET_ADMIN))
return-EPERM;
//copy_from_user:
户空间数据拷入核空间
if(copy_from_user(buf,uarg,IFNAMSIZ))
return-EFAULT;
buf[IFNAMSIZ-1]=0;
if(cmd==SIOCBRADDBR)
returnbr_add_bridge(buf);
returnbr_del_bridge(buf);
}
}
return-EOPNOTSUPP;}
这里,们传入cmdSIOCBRADDBR.转入br_add_bridge(buf)进行:
intbr_add_bridge(constchar*name)
{
structnet_device*dev;
intret;
//虚拟桥新建个net_device
//面“网络设备管理”经讲述此结构
dev=new_bridge_dev(name);
if(!
dev)
return-ENOMEM;
//sysfs建立相关信息
ret=br_sysfs_addbr(dev);
dev_put(dev);
if(ret)
unregister_netdev(dev);
out:
returnret;
err2:
free_netdev(dev);
err1:
rtnl_unlock();
gotoout;}
网桥注册跟们以看物理网络设备注册样。
们关心网桥应net_device结构什么样,继续跟踪进new_bridge_dev:
staticstructnet_device*new_bridge_dev(constchar*name)
{
structnet_bridge*br;
structnet_device*dev;
//分配net_device
dev=alloc_netdev(sizeof(structnet_bridge),name,
br_dev_setup);
if(!
dev)
returnNULL; 网桥私区结构net_bridge br=netdev_priv(dev);
//私区结构dev字段指向本身
br->dev=dev;
br->lock=SPIN_LOCK_UNLOCKED;
//队列始化。
port_list保存这个桥端口列表
INIT_LIST_HEAD(&br->port_list);
br->hash_lock=SPIN_LOCK_UNLOCKED;
//面这部份代码跟stp协议相关,们暂不关心
br->bridge_id.prio[0]=0x80;
br->bridge_id.prio[1]=0x00;
memset(br->bridge_id.addr,0,ETH_ALEN);
dev->open=br_dev_open;
dev->set_multicast_list=br_dev_set_multicast_list;
dev->change_mtu=br_change_mtu;
dev->destructor=free_netdev;
SET_MODULE_OWNER(dev);
dev->stop=br_dev_stop;
dev->accept_fastpath=br_dev_accept_fastpath;
dev->tx_queue_len=0;
dev->set_mac_address=NULL;
dev->priv_flags=IFF_EBRIDGE;
}这部份,桥设备私区空间进行始化。
这里,有必给桥net_device应私区结构:
structnet_bridge
{
//读锁
spinlock_t lock;
//端口列表
structlist_head port_list;
//网桥应虚拟设备
structnet_device *dev;
//网桥应虚拟网卡统计数据
structnet_device_stats statistics;
//hash表锁
spinlock_t hash_lock;
//MACPORT应表,即CAM
structhlist_head hash[BR_HASH_SIZE];
structlist_head age_list;
/*STP*/
u16 root_port;
rtnl_lock();
br->stp_enabled=0;
br_stp_timer_init(br);
returndev;
//stp协议应数据
unsignedchar stp_enabled;
//由核确定接口名字,例如eth0eth1等
br->designated_root=br->bridge_id;
}
bridge_id designated_root;
unsignedchar topology_change;
if(strchr(dev->name,'%')){
br->root_path_cost=0;
br_dev_setup还做些另函数指针始化:
bridge_id bridge_id;
ret=dev_alloc_name(dev,dev->name);
br->root_port=0;
voidbr_dev_setup(structnet_device*dev)
u32 root_path_cost;
if(ret<0)
br->bridge_max_age=br->max_age=20*HZ;
{
unsignedlong max_age;
gotoerr1;
br->bridge_hello_time=br->hello_time=2*HZ;
//桥MAC地址设零
unsignedlong hello_time;
}
br->bridge_forward_delay=br->forward_delay=15*HZ;
memset(dev->dev_addr,0,ETH_ALEN);
unsignedlong forward_delay;
//向核注册此网络设备
br->topology_change=0;
//以太网结构始化
unsignedlong bridge_max_age;
ret=register_netdevice(dev);
br->topology_change_detected=0;
ether_setup(dev);
unsignedlong ageing_time;
if(ret)
br->ageing_time=300*HZ;
//系列函数指针始化
unsignedlong bridge_hello_time;
gotoerr2;
INIT_LIST_HEAD(&br->age_list);
dev->do_ioctl=br_dev_ioctl;
unsignedlong bridge_forward_delay;
dev_hold(dev);
dev->get_stats=br_dev_get_stats;
rtnl_unlock(); dev->hard_start_xmit=br_dev_xmit;
unsignedchar topology_change_detected;