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

简介:

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

——lvyilong316

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

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

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

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

    它们的组织关系如下图:

5bf0a0672d82ebfa74a60f1799b75106f268cb19


下面开始分析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_PACKETETH_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; //获取协议类型

    //根据协议类型在hashptype_base中找到相关的注册协议(IPARP)

    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,但并不一定是最开始接收skbdev,如经过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;

    //如果skbshare的(skb->users不为1),则clone一个skb_buffskb指向这个新的skb->users1skb_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,这取决于封包的目的地址。

目录
相关文章
|
4天前
|
安全 网络安全 云计算
云计算与网络安全:技术融合与挑战分析
【9月更文挑战第31天】本文将深入探讨云计算和网络安全之间的关系,包括云服务、网络安全、信息安全等技术领域。我们将从云计算的基本概念和特性出发,分析其在网络安全方面的优势和挑战,并探讨如何通过技术和策略来提高云计算的安全性。同时,我们也将讨论网络安全在云计算环境下的重要性,以及如何通过有效的安全措施来保护云服务的安全。最后,我们将通过代码示例来展示如何在云计算环境中实现网络安全。
13 3
|
8天前
|
缓存 网络协议 网络架构
网络抓包分析【IP,ICMP,ARP】以及 IP数据报,MAC帧,ICMP报和ARP报的数据报格式
本文详细介绍了如何使用网络抓包工具Wireshark进行网络抓包分析,包括以太网v2 MAC帧、IP数据报、ICMP报文和ARP报文的格式,以及不同网络通信的过程。文章通过抓包分析展示了IP数据报、ICMP数据报和ARP数据报的具体信息,包括MAC地址、IP地址、ICMP类型和代码、以及ARP的硬件类型、协议类型、操作类型等。通过这些分析,可以更好地理解网络协议的工作机制和数据传输过程。
网络抓包分析【IP,ICMP,ARP】以及 IP数据报,MAC帧,ICMP报和ARP报的数据报格式
|
22天前
|
云安全 安全 网络安全
探索云计算与网络安全的共生之道在数字化浪潮席卷全球的今天,云计算作为信息技术的一大革新,正重塑着企业的运营模式与服务交付。然而,随着云服务的普及,网络安全与信息安全的挑战也日益凸显,成为制约其发展的关键因素。本文旨在深入探讨云计算环境下的网络安全问题,分析云服务、网络安全及信息安全之间的相互关系,并提出相应的解决策略,以期为构建一个更安全、可靠的云计算生态系统提供参考。
本文聚焦于云计算环境中的网络安全议题,首先界定了云服务的基本概念及其广泛应用领域,随后剖析了当前网络安全面临的主要威胁,如数据泄露、身份盗用等,并强调了信息安全在维护网络空间秩序中的核心地位。通过对现有安全技术和策略的评估,包括加密技术、访问控制、安全审计等,文章指出了这些措施在应对复杂网络攻击时的局限性。最后,提出了一系列加强云计算安全的建议,如采用零信任架构、实施持续的安全监控与自动化响应机制、提升员工的安全意识教育以及制定严格的合规性标准等,旨在为云计算的安全可持续发展提供实践指南。
52 0
|
2月前
|
存储 安全 算法
取证分析在网络安全中的关键作用
【8月更文挑战第31天】
29 0
|
2月前
|
机器学习/深度学习 运维 监控
|
2月前
|
Windows
【Azure 环境】在Windows环境中抓取网络包(netsh trace)后,如何转换为Wireshark格式以便进行分析
【Azure 环境】在Windows环境中抓取网络包(netsh trace)后,如何转换为Wireshark格式以便进行分析
|
2月前
|
网络协议 Linux
在Linux中,如何改IP、主机名、DNS?
在Linux中,如何改IP、主机名、DNS?
|
2月前
|
网络协议 Ubuntu Linux
在Linux中,如何将本地80端口的请求转发到8080端口,当前主机IP为192.168.16.1,其中本地网卡eth0。
在Linux中,如何将本地80端口的请求转发到8080端口,当前主机IP为192.168.16.1,其中本地网卡eth0。
|
2月前
|
监控 网络协议 Linux
在Linux中,如何查看当前系统每个 IP 的连接数?
在Linux中,如何查看当前系统每个 IP 的连接数?
|
2月前
|
域名解析 缓存 负载均衡
在Linux中,自定义解析域名的时候,可以编辑哪个⽂件?是否可以⼀个ip对应多个域名?是否⼀个域名对应多个ip?
在Linux中,自定义解析域名的时候,可以编辑哪个⽂件?是否可以⼀个ip对应多个域名?是否⼀个域名对应多个ip?
下一篇
无影云桌面