linux网络实现分析(2)——数据包的接收(从链路层到ip层)

linux网络实现分析(2)——数据包的接收(从链路层到ip层)

——lvyilong316

任何数据包在由驱动接收进入协议栈都会经过netif_receive_skb函数,可以说这个函数是协议栈的入口。在分析这个函数前,首先介绍下三层协议在内核中的组织方式。

    在Linux内核中,有两种不同目的的3层协议:

(1) ptype_all管理的协议主要用于分析目的,它接收所有到达第3层协议的数据包。

(2) ptype_base管理正常的3层协议,仅接收具有正确协议标志符的数据包,例如,Internet的0x0800。

    它们的组织关系如下图:

下面开始分析netif_receive_skb函数。

netif_receive_skb


int netif_receive_skb(struct sk_buff *skb)

{

   
struct packet_type *ptype, *pt_prev;

   
struct net_device *orig_dev;

   
struct net_device *null_or_orig;

   
int ret = NET_RX_DROP;

   
__be16 type;

 

    if (skb->vlan_tci
&& vlan_hwaccel_do_receive(skb)) //有vlan tag,则vlan处理

       
return NET_RX_SUCCESS;

 

   
null_or_orig = NULL;

    orig_dev = skb->dev;   //接受数据包的原始设备

   
if (orig_dev->master) {

       
if (skb_bond_should_drop(skb))

           
null_or_orig = orig_dev; /* deliver only exact match */

       
else

           
skb->dev = orig_dev->master;

    }

   
pt_prev = NULL;

rcu_read_lock();

    //如AF_PACKET等ETH_P_ALL网络协议会被放在ptype_all链表中(tcpdump)。

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);//bridge逻辑处理

    if (!skb)

        goto out;

 

    type = skb->protocol; //获取协议类型

    //根据协议类型在hash表ptype_base中找到相关的注册协议(IP、ARP)

    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);

        /* Jamal, now you will not able to
escape explaining

         * me how you were going to use this.
:-)

         */

        ret = NET_RX_DROP;

    }

 

out:

    rcu_read_unlock();

    return ret;

}

deliver_skb

    int deliver_skb(struct sk_buff *skb,struct
packet_type *pt_prev, struct net_device *orig_dev){

atomic_inc(&skb->users); //这句不容忽视,与后面流程的kfree_skb()相呼

return pt_prev->func(skb,
skb->dev, pt_prev, orig_dev)
;//调函数ip_rcv() arp_rcv()等

}

 

如果是IP数据包,则调用ip_rcv()函数,这是因为ip协议再引导时间初始化协议处理函数为ip_rcv(),具体如下:

net/ipv4/af_inet.c,

static struct
packet_type ip_packet_type __read_mostly = {

    .type = cpu_to_be16(ETH_P_IP),

    .func = ip_rcv,

    .gso_send_check = inet_gso_send_check,

    .gso_segment = inet_gso_segment,

    .gro_receive = inet_gro_receive,

    .gro_complete = inet_gro_complete,

};

static int
__init inet_init(void){

    …

    dev_add_pack(&ip_packet_type);

    …

}

 

下面来看ip_rcv()函数:

ip_rcv()

net/ipv4/ip_input.c

/*

参数说明:

@dev:即skb->dev,但并不一定是最开始接收skb的dev,如经过bridge逻辑又发回本机协议栈时,此时dev即为刚经过的bridge对应的虚拟dev,具体见bridge逻辑。

@pt: 指向协议的指针,这里即ip_packet_type。

@orig_dev: 这是最开始接收数据包的设备dev,再netif_receive_skb中被设置。

*/

int ip_rcv(struct sk_buff *skb, struct
net_device *dev, struct packet_type *pt, struct net_device *orig_dev)

{

   
struct iphdr *iph;

   
u32 len;

    //如果skb是share的(skb->users不为1),则clone一个skb_buff,skb指向这个新的skb->users为1的skb_buff

   
if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {

       
IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INDISCARDS);

       
goto out;

    }

    //进入ip_rcv之前,skb->date已经指向链路头后面,链路头已被解析。

   
if (!pskb_may_pull(skb, sizeof(struct iphdr)))

       
goto inhdr_error;

 

   
iph = ip_hdr(skb);

 

   
if (iph->ihl < 5 || iph->version != 4) //丢掉ip头长小于4*5=20的,或者ip version不为4的包(头部长度是以4字节为单位的)

       
goto inhdr_error;

 

   
if (!pskb_may_pull(skb, iph->ihl*4))

       
goto inhdr_error;

   
iph = ip_hdr(skb);    

 

   
if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl)))//丢弃校验和(仅头部)错误的包

       
goto inhdr_error;

 

   
len = ntohs(iph->tot_len); //len为数据包的真实长度

  

   //由于链路层传输时,由于对齐或最小mtu等原因,可能会对数据包填充,所以这里要将数据包截断为其真实长度(len),len包括线性和非线性数据两部分。另外pskb_trim_rcsum会让L4校验和失效,以免接受的NIC计算过此值,但是计算的不正确。

   
if (pskb_trim_rcsum(skb, len)) {

       
IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INDISCARDS);

       
goto drop;

    }

   
skb_orphan(skb);//使包成为不属于任何套接字的孤包(skb->sk
= NULL)

    //IP
PRE_ROUTING

    return NF_HOOK(PF_INET,
NF_INET_PRE_ROUTING, skb, dev, NULL,

               ip_rcv_finish);

 

inhdr_error:

   
IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INHDRERRORS);

drop:

   
kfree_skb(skb);

out:

   
return NET_RX_DROP;

}

 

在经过IP协议的PRE_ROUTING hook点后,将调用ip_rcv_finish函数。

 ip_rcv_finish

net/ipv4/ip_input.c,

static int ip_rcv_finish(struct sk_buff
*skb)

{

   
const struct iphdr *iph = ip_hdr(skb);

   
struct rtable *rt;

 

   // skb->_skb_dst 指向dst_entry

   
if (skb_dst(skb) == NULL) {//如果 skb->_skb_dst为空

       
int err = ip_route_input(skb,
iph->daddr, iph->saddr, iph->tos,

skb->dev);  //查找路由表

       

       
}

    }

  //当ip头部超过20字节时,表示有一些选项要处理,此时skb_cow(命名来自于copy on write)会被调用,如果skb和别人有share,就拷贝一个副本,因为处理选项有可能修改ip头。同时处理选项

   
if (iph->ihl > 5 && ip_rcv_options(skb))

       
goto drop;

 

   
rt = skb_rtable(skb);

   
if (rt->rt_type == RTN_MULTICAST) {  //多播

       
IP_UPD_PO_STATS_BH(dev_net(rt->u.dst.dev), IPSTATS_MIB_INMCAST,

                skb->len);

    }
else if (rt->rt_type == RTN_BROADCAST) 
//广播

       
IP_UPD_PO_STATS_BH(dev_net(rt->u.dst.dev), IPSTATS_MIB_INBCAST,

                skb->len);

 

   
return dst_input(skb);

 

drop:

   
kfree_skb(skb);

   
return NET_RX_DROP;

}

 

  dst_input

static inline int dst_input(struct sk_buff
*skb)

{

   
return skb_dst(skb)->input(skb);

}

即调用skb->_skb_dst->input(skb),skb->_skb_dst在ip_route_input中被初始化为ip_local_deliver或ip_forward,这取决于封包的目的地址。

时间: 2024-10-26 19:07:34

linux网络实现分析(2)——数据包的接收(从链路层到ip层)的相关文章

linux网络实现分析(1)——数据包的接收(从网卡到协议栈)

linux网络实现分析(1)--数据包的接收(从网卡到协议栈) --lvyilong316 说明:源码参考2.6.32 从网卡到协议栈的skb接收有两种方式:NAPI和非NAPI.其中有公共逻辑,也有区别.首先看下用到的基本数据结构. 1. 基本数据结构 l   softnet_data    每cpu数据 struct softnet_data {     struct Qdisc        *output_queue;  //发送帧队列     struct sk_buff_head

linux网络实现分析(3)——数据包的发送(IP层到链路层)

二层(链路层)数据包发送过程分析         当上层准备好一个包之后,交给链路层,链路层数据包发送主要通过dev_queue_xmit函数处理.数据包的发送可分为两种,一种是正常的传输流程,即通过网卡驱动,另一种是通过软中断(见注3).为了理解方便,首先看一下dev_queue_xmi函数的整体调用关系图.     dev_queue_xmit     本函数用来将带发送的skb加入一个dev的队列(Queue),调用这个函数前必须设置好skb的device和priority,本函数可以在中

Linux网络 - 数据包的接收过程

本文将介绍在Linux系统中,数据包是如何一步一步从网卡传到进程手中的. 如果英文没有问题,强烈建议阅读后面参考里的两篇文章,里面介绍的更详细. 本文只讨论以太网的物理网卡,不涉及虚拟设备,并且以一个UDP包的接收过程作为示例. 本示例里列出的函数调用关系来自于kernel 3.13.0,如果你的内核不是这个版本,函数名称和相关路径可能不一样,但背后的原理应该是一样的(或者有细微差别) 网卡到内存 网卡需要有驱动才能工作,驱动是加载到内核中的模块,负责衔接网卡和内核的网络模块,驱动在加载的时候将

c#-C#网络编程使用SharpPcap.dll来抓取网络上的TCP数据包,怎么才能完整的还原抓取的数据?

问题描述 C#网络编程使用SharpPcap.dll来抓取网络上的TCP数据包,怎么才能完整的还原抓取的数据? 5C 如题,C#网络编sd程使用SharpPcap.dll来抓取网络上的TCP数据包,怎么才能完整的还原抓取的数据?我是要抓取特定的两台机器之间的数据传递,已知传递的数据包的组成规则,有这么几个问题:1.怎么才能完整的获取所有的数据包?2.获取的数据包需不需要考虑TCP数据包的重传或者错误传递的数据包? 谢谢. 解决方案 c# 使用sharppcap实现 网络抓包 使用SharpPCa

分析ospf数据包-分析OSPF数据包时有问题

问题描述 分析OSPF数据包时有问题 获取到数据包后,想要单击列表中的一条信息,想显示具体信息的时候,程序就出现了问题. 如下图

网络上捕获的数据包,如何判断是否加密?

问题描述 网络上捕获的数据包,如何判断是否加密?谢谢赐教 解决方案 解决方案二:这个不能看出来吧,数据加密不加密没有什么特征可以说明解决方案三:如果有TLS的报文,那说明肯定加密了,如果没有就不好确定了解决方案四:如果你指的是HTTPS,楼上说的对.如果非标准的协议,走TCP或者UDP的,那就很难判断了.要casebycase来看了.你可以把trace发给我,我帮你看卡,cosmosfang@hotmail.com解决方案五:不知用随机性检测是否能判断,NISTTEST,目前正从这方面考虑解决方

网络协议-PPTP协议数据包有几个无法抓到,急~~

问题描述 PPTP协议数据包有几个无法抓到,急~~ RT,PPTP协议数据包通过Wireshark有4个无法抓到分别是:Incoming-Call-Request Incoming-Call-Reply Incoming-Call-Connected WAN-Error-Notify请问有没有大神抓到过这四个包?这四个包在哪种环境里面能够抓到?这4个数据包在整个数据包传输过程中是怎样的发包顺序呢? 现在我能抓到的包如图所示,哪怕数据包出错也没有出现"WAN-Error-Notify"&

网络 linux-用ostinato构造一数据包(有Dmac、Smac),数据包本地发出后,在目的端抓不到该数据包

问题描述 用ostinato构造一数据包(有Dmac.Smac),数据包本地发出后,在目的端抓不到该数据包 用ostinato构造一数据包(有源mac和目的mac以及ethtype),数据包本地发出后(本地wireshark可以抓到发出的包),在目的端抓不到该数据包?求解

介绍一个开源国产数据包生成与协议分析工具

问题描述 FinePacketBuilder(简称FPB)-开源国产数据包生成与协议分析工具,这是我从过年后一直在开发的工具软件,现在开发已经告一段落并且完全可以使用了.FPB是开源免费数据包生成与协议分析工具,同时还提供了著名的PCAP数据包截取软件包的Java封装.FPB具有独创的协议语法表示语言和通用CD(编码解码器)框架,能够方便地开发自己的协议解码器并已支持如下的内置协议:Ethernet,ARP,ICMPv4,IPv4,ICMPv6,IPv6,UDP,TCP,HTTP下面是FPB的一