linux协议栈bridgeWord下载.docx
《linux协议栈bridgeWord下载.docx》由会员分享,可在线阅读,更多相关《linux协议栈bridgeWord下载.docx(14页珍藏版)》请在冰豆网上搜索。
pt_prev,&
ret,orig_dev))
gotoout;
}
staticinlinestructsk_buff*handle_bridge(structsk_buff*skb,
structpacket_type**pt_prev,int*ret,
structnet_device*orig_dev)
structnet_bridge_port*port;
//对于回环设备以及skb->
dev->
br_port为空(即不被任何网桥所包含)的数据包直接返回
if(skb->
pkt_type==PACKET_LOOPBACK||
(port=rcu_dereference(skb->
br_port))==NULL)
returnskb;
if(*pt_prev){
*ret=deliver_skb(skb,*pt_prev,orig_dev);
*pt_prev=NULL;
//网桥的基本挂接点处理函数
returnbr_handle_frame_hook(port,skb);
br_handle_frame_hook在网桥初始化模块br_init(void)函数中被赋值.
br_handle_frame_hook=br_handle_frame;
所以网桥对于数据包的处理过程是从br_handle_frame开始的。
structsk_buff*br_handle_frame(structnet_bridge_port*p,structsk_buff*skb)
constunsignedchar*dest=eth_hdr(skb)->
h_dest;
int(*rhook)(structsk_buff*skb);
//判断是否为有效的物理地址,非全0地址以及非广播地址
if(!
is_valid_ether_addr(eth_hdr(skb)->
h_source))
gotodrop;
//判断skb包是否被共享skb->
users!
=1,若是,则复制一份,否则直接返回
skb=skb_share_check(skb,GFP_ATOMIC);
skb)
returnNULL;
//这个函数并非像想象的那样,判断是否为本地地址
//而是在判断是否为链路本地多播地址,01:
80:
c2:
00:
0x
if(unlikely(is_link_local(dest))){
/*Pauseframesshouldn'
tbepassedupbydriveranyway*/
protocol==htons(ETH_P_PAUSE))
/*IfSTPisturnedoff,thenforward*/
if(p->
br->
stp_enabled==BR_NO_STP&
&
dest[5]==0)
gotoforward;
if(NF_HOOK(PF_BRIDGE,NF_BR_LOCAL_IN,skb,skb->
dev,
NULL,br_handle_local_finish))
/*frameconsumedbyfilter*/
else
/*continueprocessing*/
forward:
switch(p->
state){
caseBR_STATE_FORWARDING:
//如果网桥处于forwarding状态,并且该报文必须要走L3层进行转发,则直接返回
//br_should_route_hook钩子函数在ebtable里面设置为ebt_broute函数,它根据用户的规
//决定该报文是否要通过L3层来转发;
一般rhook为空
rhook=rcu_dereference(br_should_route_hook);
if(rhook!
=NULL){
if(rhook(skb))
dest=eth_hdr(skb)->
/*fallthrough*/
caseBR_STATE_LEARNING:
//如果数据包的目的mac地址为虚拟网桥设备的mac地址,则标记为host
compare_ether_addr(p->
dev_addr,dest))
skb->
pkt_type=PACKET_HOST;
//调用网桥在NF_BR_PREROUTING处挂载的钩子函数,因为网桥在其钩子函数过//程中嵌套调用了INET层BR_PREROUTING的钩子函数,过程有些曲折,故最后//再分析
NF_HOOK(PF_BRIDGE,NF_BR_PRE_ROUTING,skb,skb->
dev,NULL,
br_handle_frame_finish);
break;
default:
drop:
kfree_skb(skb);
FORWARDING以及LEARNING为网桥的状态,网桥端口一般有5种状态:
1)
disable被管理员禁用
2)
blcok休息,不参与数据包转发
3)
listening监听
4)
learning学习ARP信息,准备向工作状态改变
5)
forwarding正常工作,转发数据包
/*note:
alreadycalledwithrcu_read_lock(preempt_disabled)*/
intbr_handle_frame_finish(structsk_buff*skb)
structnet_bridge_port*p=rcu_dereference(skb->
br_port);
structnet_bridge*br;
structnet_bridge_fdb_entry*dst;
structsk_buff*skb2;
//判断网桥状态
p||p->
state==BR_STATE_DISABLED)
/*insertintoforwardingdatabaseafterfilteringtoavoidspoofing*/
//br为虚拟网桥结构
br=p->
br;
//根据数据包的源物理地址,更新网桥的转发表
br_fdb_update(br,p,eth_hdr(skb)->
h_source);
state==BR_STATE_LEARNING)
/*Thepacketskb2goestothelocalhost(NULLtoskip).*/
//skb2数据包用于交付本机,skb数据包则用于forward
skb2=NULL;
//如果网口处于混杂模式,复制一份交付主机
if(br->
flags&
IFF_PROMISC)
skb2=skb;
dst=NULL;
//如果为广播数据包,增加计数,同样需要发一份给主机
if(is_multicast_ether_addr(dest)){
br->
stats.multicast++;
/*根据网桥口以及目标地址判断是否为本机数据包*/
elseif((dst=__br_fdb_get(br,dest))&
dst->
is_local){
/*Donotforwardthepacketsinceit'
slocal.*/
skb=NULL;
if(skb2==skb)
skb2=skb_clone(skb,GFP_ATOMIC);
if(skb2)
/*完成将数据包交付给本机的工作*/
br_pass_frame_up(br,skb2);
if(skb){
if(dst)
//如果存在目的地址则将其转发
br_forward(dst->
dst,skb);
//否则,flood数据包,向除接收网口外的其余网口发送该数据包
br_flood_forward(br,skb);
out:
return0;
//此函数主要实现通过网桥接收发往本机的数据包
staticvoidbr_pass_frame_up(structnet_bridge*br,structsk_buff*skb)
structnet_device*indev,*brdev=br->
dev;
//完成数据包的统计计数
brdev->
stats.rx_packets++;
stats.rx_bytes+=skb->
len;
//将skb的dev改变为网桥结构的brdev
//此时skb的dev选项由实际网络设备eth0等改变为虚拟网桥设备br0
indev=skb->
dev=brdev;
//重新走数据包接收流程,netif_receive_skb
//但因为dev的改变,dev的br_port字段不再为空,不会重走网桥流程,直接交付
NF_HOOK(PF_BRIDGE,NF_BR_LOCAL_IN,skb,indev,NULL,
netif_receive_skb);
下面看一下转发数据包的流程,对于flood_forward的流程,同样通过br_forward来实现,只不过改为循环遍历hash表中的设备,对于每一个设备都调用一次br_forward流程。
/*calledwithrcu_read_lock*/
voidbr_forward(conststructnet_bridge_port*to,structsk_buff*skb)
/*如果skb->
dev不等于网桥的dev,同时网桥状态为forwarding,则进行转发*/
if(should_deliver(to,skb)){
__br_forward(to,skb);
return;
staticvoid__br_forward(conststructnet_bridge_port*to,structsk_buff*skb)
structnet_device*indev;
if(skb_warn_if_lro(skb)){
//将skb的dev字段改为查找到的出口dev字段
dev=to->
skb_forward_csum(skb);
//遍历执行NF_BR_FORWARD钩子函数
NF_HOOK(PF_BRIDGE,NF_BR_FORWARD,skb,indev,skb->
br_forward_finish);
intbr_forward_finish(structsk_buff*skb)
//继续跑NF_BR_POST_ROUTING处的钩子函数
returnNF_HOOK(PF_BRIDGE,NF_BR_POST_ROUTING,skb,NULL,skb->
br_dev_queue_push_xmit);
intbr_dev_queue_push_xmit(structsk_buff*skb)
/*dropmtuoversizedpacketsexceptgso*/
/*如果skb数据包的长度大于MTU值,则丢弃*/
if(packet_length(skb)>
skb->
mtu&
!
skb_is_gso(skb))
else{
/*ip_refragcallsip_fragment,doesn'
tcopytheMACheader.*/
if(nf_bridge_maybe_copy_header(skb))
skb_push(skb,ETH_HLEN);
//此时skb的dev已经替换成进行转发的dev了,dev_queue_xmit将使
//用该网口设备的发送函数完成数据包的发送
dev_queue_xmit(skb);
发送过程:
协议栈上层需要发送报文时,调用dev_queue_xmit(skb)函数。
如果这个报文需要通过网桥设备来发送,则skb->
dev指向一个网桥设备。
网桥设备没有使用发送队列(dev->
qdisc为空),所以dev_queue_xmit将直接调用dev->
hard_start_xmit函数,而网桥设备的hard_start_xmit等于函数br_dev_xmit;
/*netdevicetransmitalwayscalledwithnoBH(preempt_disabled)*/
/*br_dev_xmit为网桥设备的数据包发送函数*/
intbr_dev_xmit(structsk_buff*skb,structnet_device*dev)
structnet_bridge*br=netdev_priv(dev);
constunsignedchar*dest=skb->
data;
dev->
stats.tx_packets++;
stats.tx_bytes+=skb->
skb_reset_mac_header(skb);
skb_pull(skb,ETH_HLEN);
/*如果为广播地址,则flood该数据包
*如果能够根据skb中的目的mac地址查找到对应的网口,则通过br_deliver发送该数据包
*如果查找不到,同样flood该数据包
*/
if(dest[0]&
1)
br_flood_deliver(br,skb);
elseif((dst=__br_fdb_get(br,dest))!
=NULL)
br_deliver(dst->
br_flood_deliver函数的实现过程,同样是遍历hash表,对于每一个网口设备都调用一次__br_deliver,所以下面就主要看一下br_deliver函数的流程
voidbr_deliver(conststructnet_bridge_port*to,structsk_buff*skb)
__br_deliver(to,skb);
staticvoid__br_deliver(conststructnet_bridge_port*to,structsk_buff*skb)
/*将skb中的dev改成出口设备所对应的dev*/
NF_HOOK(PF_BRIDGE,NF_BR_LOCAL_OUT,skb,NULL,skb->
/*最终仍然通过br_dev_queue_push_xmit完成数据包的发送过程*/
至此,整个网桥中数据的处理流程已经完全结束了。
Netfilter:
对于网桥中的netfilter的钩子函数的调度过程有些曲折,对于INET层的钩子函数全部被嵌套进BRIDGE层钩子函数的运行流程中。
下面首先来看一下网桥一共挂载了哪些钩子函数
staticstructnf_hook_opsbr_nf_ops[]__read_mostly={
{.hook=br_nf_pre_routing,
.owner=THIS_MODULE,
.pf=PF_BRIDGE,
.hooknum=NF_BR_PRE_ROUTING,
.priority=NF_BR_PRI_BRNF,},
{.hook=br_nf_local_in,
.hooknum=NF_BR_LOCAL_IN,
{.hook=br_nf_forward_ip,
.hooknum=NF_BR_FORWARD,
.priority=NF_BR_PRI_BRNF-1,},
{.hook=br_nf_forward_arp,
{.hook=br_nf_local_out,
.hooknum=NF_BR_LOCAL_OUT,
.priority=NF_BR_PRI_FIRST,},
{.hook=br_nf_post_routing,
.hooknum=NF_BR_POST_ROUTING,
.priority=NF_BR_PRI_LAST,},
//以上为BRIDGE层挂载的钩子函数,一下为INET层挂载的钩子函数
{.hook=ip_sabotage_in,
.pf=PF_INET,
.hooknum=NF_INET_PRE_ROUTING,
.priority=NF_IP_PRI_FIRST,},
.pf=PF_INET6,
.hooknum=NF_INE