linux 内核协议栈 ip_loopback,Linux内核网络协议栈

本文分析基于Linux Kernel 1.2.13

作者:闫明

注:标题中的”(上)“,”(下)“表示分析过程基于数据包的传递方向:”(上)“表示分析是从底层向上分析、”(下)“表示分析是从上向下分析。

上一篇博文中我们从宏观上分析了Linux内核中网络栈的初始化过程,这里我们再从宏观上分析一下一个数据包在各网络层的传递的过程。

我们知道网络的OSI模型和TCP/IP模型层次结构如下:

a4c26d1e5885305701be709a3d33442f.png

上文中我们看到了网络栈的层次结构:

a4c26d1e5885305701be709a3d33442f.png

我们就从最底层开始追溯一个数据包的传递流程。

1、网络接口层

* 硬件监听物理介质,进行数据的接收,当接收的数据填满了缓冲区,硬件就会产生中断,中断产生后,系统会转向中断服务子程序。

*

在中断服务子程序中,数据会从硬件的缓冲区复制到内核的空间缓冲区,并包装成一个数据结构(sk_buff),然后调用对驱动层的接口函数netif_rx()将数据包发送给链路层。该函数的实现在net/inet/dev.c中,(在整个网络栈实现中dev.c文件的作用重大,它衔接了其下的驱动层和其上的网络层,可以称它为链路层模块的实现)

该函数的实现如下:

voidnetif_rx(structsk_buff *skb)

{

staticintdropping = 0;

skb->sk = NULL;

skb->free = 1;

if(skb->stamp.tv_sec==0)

skb->stamp = xtime;

if(!backlog_size)

dropping = 0;

elseif(backlog_size > 300)

dropping = 1;

if(dropping)

{

kfree_skb(skb, FREE_READ);

return;

}

#ifdef CONFIG_SKB_CHECK

IS_SKB(skb);

#endif

skb_queue_tail(&backlog,skb);//加入队列backlog

backlog_size++;

mark_bh(NET_BH);//下半部分bottom half技术可以减少中断处理程序的执行时间

return;

}

该函数中用到了bootom

half技术,该技术的原理是将中断处理程序人为的分为两部分,上半部分是实时性要求较高的任务,后半部分可以稍后完成,这样就可以节省中断程序的处理时间。可整体的提高系统的性能。该技术将会在后续的博文中详细分析。

我们从上一篇分析中知道,在网络栈初始化的时候,已经将NET的下半部分执行函数定义成了net_bh(在socket.c文件中1375行左右)

bh_base[NET_BH].routine= net_bh;//设置NET 下半部分的处理函数为net_bh

* 函数net_bh的实现在net/inet/dev.c中

voidnet_bh(void*tmp)

{

structsk_buff *skb;

structpacket_type *ptype;

structpacket_type *pt_prev;

unsigned shorttype;

if(set_bit(1, (void*)&in_bh))//标记BUSY状态

return;

dev_transmit();//调用dev_tinit()函数发送数据

cli();//防止队列操作错误,需要关中断和开中断

while((skb=skb_dequeue(&backlog))!=NULL)//出队直到队列为空

{

backlog_size--;//队列元素个数减一

sti();

skb->h.raw = skb->data + skb->dev->hard_header_len;

skb->len -= skb->dev->hard_header_len;

type = skb->dev->type_trans(skb, skb->dev);//取出该数据包所属的协议类型

pt_prev = NULL;

for(ptype = ptype_base; ptype != NULL; ptype = ptype->next)//遍历ptype_base所指向的网络协议队列

{

//判断协议号是否匹配

if((ptype->type == type || ptype->type == htons(ETH_P_ALL)) && (!ptype->dev || ptype->dev==skb->dev))

{

if(pt_prev)

{

structsk_buff *skb2;

skb2=skb_clone(skb, GFP_ATOMIC);//复制数据包结构

if(skb2)

pt_prev->func(skb2, skb->dev, pt_prev);//调用相应协议的处理函数,

//这里和网络协议的种类有关系

//如IP 协议的处理函数就是ip_rcv

}

pt_prev=ptype;

}

}

if(pt_prev)

pt_prev->func(skb, skb->dev, pt_prev);

else

kfree_skb(skb, FREE_WRITE);

dev_transmit();

cli();

}

in_bh = 0;//BUSY状态还原

sti();

dev_transmit();//清空缓冲区

}

2、网络层

*

就以IP数据包为例来说明,那么从链路层向网络层传递时将调用ip_rcv函数。该函数完成本层的处理后会根据IP首部中使用的传输层协议来调用相应协议的处理函数。

UDP对应udp_rcv、TCP对应tcp_rcv、ICMP对应icmp_rcv、IGMP对应igmp_rcv(虽然这里的ICMP,IGMP一般成为网络层协议,但是实际上他们都封装在IP协议里面,作为传输层对待)

这个函数比较复杂,后续会详细分析。这里粘贴一下,让我们对整体了解更清楚

intip_rcv(structsk_buff *skb,structdevice *dev,structpacket_type *pt)

{

structiphdr *iph = skb->h.iph;

structsock *raw_sk=NULL;

unsigned charhash;

unsigned charflag = 0;

unsigned charopts_p = 0;

structinet_protocol *ipprot;

staticstructoptions opt;

intbrd=IS_MYADDR;

intis_frag=0;

#ifdef CONFIG_IP_FIREWALL

interr;

#endif

ip_statistics.IpInReceives++;

skb->ip_hdr = iph;

if(skb->len<sizeof(structiphdr) || iph->ihl<5 || iph->version != 4 ||

skb->lentot_len) || ip_fast_csum((unsigned char*)iph, iph->ihl) !=0)

{

ip_statistics.IpInHdrErrors++;

kfree_skb(skb, FREE_WRITE);

return(0);

}

#ifdef CONFIG_IP_FIREWALL

if((err=ip_fw_chk(iph,dev,ip_fw_blk_chain,ip_fw_blk_policy, 0))!=1)

{

if(err==-1)

icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0, dev);

kfree_skb(skb, FREE_WRITE);

return0;

}

#endif

skb->len=ntohs(iph->tot_len);

if(iph->ihl != 5)

{

memset((char*) &opt, 0,sizeof(opt));

if(do_options(iph, &opt) != 0)

return0;

opts_p = 1;

}

if(iph->frag_off)

{

if(iph->frag_off & 0x0020)

is_frag|=1;

if(ntohs(iph->frag_off) & 0x1fff)

is_frag|=2;

}

if( iph->daddr != skb->dev->pa_addr && (brd = ip_chk_addr(iph->daddr)) == 0)

{

if(skb->pkt_type!=PACKET_HOST || brd==IS_BROADCAST)

{

kfree_skb(skb,FREE_WRITE);

return0;

}

#ifdef CONFIG_IP_FORWARD

ip_forward(skb, dev, is_frag);

#else

ip_statistics.IpInAddrErrors++;

#endif

kfree_skb(skb, FREE_WRITE);

return(0);

}

#ifdef CONFIG_IP_MULTICAST

if(brd==IS_MULTICAST && iph->daddr!=IGMP_ALL_HOSTS && !(dev->flags&IFF_LOOPBACK))

{

structip_mc_list *ip_mc=dev->ip_mc_list;

do

{

if(ip_mc==NULL)

{

kfree_skb(skb, FREE_WRITE);

return0;

}

if(ip_mc->multiaddr==iph->daddr)

break;

ip_mc=ip_mc->next;

}

while(1);

}

#endif

#ifdef CONFIG_IP_ACCT

ip_acct_cnt(iph,dev, ip_acct_chain);

#endif

if(is_frag)

{

skb=ip_defrag(iph,skb,dev);

if(skb==NULL)

return0;

skb->dev = dev;

iph=skb->h.iph;

}

skb->ip_hdr = iph;

skb->h.raw += iph->ihl*4;

hash = iph->protocol & (SOCK_ARRAY_SIZE-1);

if((raw_sk=raw_prot.sock_array[hash])!=NULL)

{

structsock *sknext=NULL;

structsk_buff *skb1;

raw_sk=get_sock_raw(raw_sk, hash, iph->saddr, iph->daddr);

if(raw_sk)

{

do

{

sknext=get_sock_raw(raw_sk->next, hash, iph->saddr, iph->daddr);

if(sknext)

skb1=skb_clone(skb, GFP_ATOMIC);

else

break;

if(skb1)

raw_rcv(raw_sk, skb1, dev, iph->saddr,iph->daddr);

raw_sk=sknext;

}

while(raw_sk!=NULL);

}

}

hash = iph->protocol & (MAX_INET_PROTOS -1);

for(ipprot = (structinet_protocol *)inet_protos[hash];ipprot != NULL;ipprot=(structinet_protocol *)ipprot->next)

{

structsk_buff *skb2;

if(ipprot->protocol != iph->protocol)

continue;

if(ipprot->copy || raw_sk)

{

skb2 = skb_clone(skb, GFP_ATOMIC);

if(skb2==NULL)

continue;

}

else

{

skb2 = skb;

}

flag = 1;

ipprot->handler(skb2, dev, opts_p ? &opt : 0, iph->daddr,

(ntohs(iph->tot_len) - (iph->ihl * 4)),

iph->saddr, 0, ipprot);

}

if(raw_sk!=NULL)

raw_rcv(raw_sk, skb, dev, iph->saddr, iph->daddr);

elseif(!flag)

{

if(brd != IS_BROADCAST && brd!=IS_MULTICAST)

icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0, dev);

kfree_skb(skb, FREE_WRITE);

}

return(0);

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值