首页 > 系统相关 >Linux收包之数据L3层是如何流转的

Linux收包之数据L3层是如何流转的

时间:2023-12-25 19:56:32浏览次数:33  
标签:IP 收包 ip Linux dev L3 skb 报头 数据包

一、环境说明

内核版本:Linux 3.10

内核源码地址:https://elixir.bootlin.com/linux/v3.10/source (包含各个版本内核源码,且网页可全局搜索函数)

网卡:Intel的igb网卡

网卡驱动源码目录:drivers/net/ethernet/intel/igb/

二、L3层概览

 

本章主要介绍收包的流程,在L3层是如何处理的。

类型为ETH_P_IP类型的数据包,被传递到三层,调用ip_rcv函数。

三、ip_rcv函数

netif_receive_skb函数会把指向L3协议指针(skb->nh)设置在L2报头尾端。因此,IP层函数可以安全地将它转换成iphdr结构。
此时,skb->data指向L3报头。
ip_rcv的主要工作就是对封包做健康检查,然后调用Netfilter钩子。

// file: net/ipv4/ip_input.c
int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
{
    const struct iphdr *iph;
    u32 len;

    if (skb->pkt_type == PACKET_OTHERHOST) //丢弃掉不是发往本机的报文,网卡开启混杂模式会收到此类报文
        goto drop;


    IP_UPD_PO_STATS_BH(dev_net(dev), IPSTATS_MIB_IN, skb->len);

    if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) { //检查skb是否为share?是:则克隆报文(克隆报文,内存分配失败的话,该封包会被丢弃)
        IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INDISCARDS);
        goto out;
    }

    if (!pskb_may_pull(skb, sizeof(struct iphdr))) //确保skb->data所指的区域包含的数据区块至少和IP报头一样长,即确保skb还可以容纳标准的报头(即20字节)
        goto inhdr_error;

    iph = ip_hdr(skb); //重新获取IP头(pskb_may_pull可以改变缓冲区结构)

    if (iph->ihl < 5 || iph->version != 4) //ip头长度至少为20字节(ihl>=5,报头的尺寸是4字节的备注),只支持v4
        goto inhdr_error;

    // 这项检查会拖到现在才做,是因为此函数必须先确定基本报头没有被截断,而且从中读取东西前已通过基本健康检查
    if (!pskb_may_pull(skb, iph->ihl*4)) //重复先前做过的检查,只不过这一次使用的是完整的ip报头尺寸。
        goto inhdr_error;

    iph = ip_hdr(skb);

    if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl))) //ip头csum校验
        goto csum_error;

    len = ntohs(iph->tot_len); //取ip分组总长,即ip首部加数据的长度
    if (skb->len < len) { //skb的实际总长度小于ip分组总长,则drop(由于L2协议会填补有效载荷,Ethernet数据帧最小64字节)
        IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INTRUNCATEDPKTS);
        goto drop;
    } else if (len < (iph->ihl*4)) //确保封包的尺寸至少和IP报头的尺寸一样大(IP头不能分段,每个IP片段至少包含一个IP报头)
        goto inhdr_error;

    /* 调整数据包结构与校验和
     * 检查是否有:L2协议填充封包以达到特定的最小长度?
     * 有:把封包剪成正确尺寸,再让L4校验和失效,以免进行接收的NIC计算
     */
    if (pskb_trim_rcsum(skb, len)) { //
        IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INDISCARDS);
        goto drop;
    }

    /* Remove any debris in the socket control block */
    memset(IPCB(skb), 0, sizeof(struct inet_skb_parm)); //清空cb,即inet_skb_parm值

    /* Must drop socket now because of tproxy. */
    skb_orphan(skb);

    //调用netfilter,实现iptables功能,通过后调用ip_rcv_finish函数
    return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, skb, dev, NULL,
               ip_rcv_finish);

csum_error:
    IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_CSUMERRORS);
inhdr_error:
    IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INHDRERRORS);
drop:
    kfree_skb(skb);
out:
    return NET_RX_DROP;
}

ip_rcv()函数首先是对数据包类型进行检查,如果是PACKET_OTHERHOST类型,则直接丢弃。
skb_share_check()函数检查能否共享数据包结构?能:克隆一个新的数据包结构,函数使用这个新的数据包结构。克隆的数据包可以进行修改。
每一个数据包必须包含一个完整的IP头部,数据块中至少应该包含IP头部,pskb_may_pull()函数检查它是否包含IP头部。
ip_hdr()函数从数据包中取得IP头部,然后对头部进行检查。
ip_fast_csum()函数检查IP头部的校验和。接下来检查数据包的长度是否与头部记录的长度相符,它至少应该等于数据包头部的长度。
接着调用pskb_trim_rcsum()函数为传输层调整数据包与校验和。
ip_rcv()函数最后通过HF_HOOK宏,调用netfilter,实现iptables功能,通过后调用ip_rcv_finish函数。

四、ip_rcv_finish函数

 

标签:IP,收包,ip,Linux,dev,L3,skb,报头,数据包
From: https://www.cnblogs.com/573583868wuy/p/17926839.html

相关文章

  • linux 用户注销
    1.先用w命令查看当前登录系统的用户:[root@centos~]#w11:48:09up 3:13, 2users, loadaverage:0.00,0.01,0.00USER  TTY   FROM       LOGIN@ IDLE JCPU PCPUWHATroot  pts/0  218.17.167.82  11:47  0.00s......
  • linux生成ssh的一对公钥和私钥
    1.首先进入.SSH目录中Linux中,每个用户的根目录下都有一个.ssh目录,保存了ssh相关的key和一些记录文件。例如:cd~/ll-a 2. 使用ssh-keygen生成keyssh-keygen可以生成ssh协议所需要的公钥和私钥,例如:ssh-keygen-trsa然后回提示让你输入一些文件名啥的,别管那些,一路按E......
  • linux-DNS服务器
    一、1、理解区域(zone)DNS的每一个区域都是一个域---一个区域可以管辖多个子域、2、解析正向解析:通过域名解析出ip地址反向解析:根据ip地址解析出dns名称解析过程客户端dns(host文件)本地dns区域dns服务器缓存3、部署dns服务器一般使用传统BIND软件包或者unbound、......
  • linux编译器:gcc/g++的使用
    原文连接:https://blog.csdn.net/weixin_72060925/article/details/131274627原文链接:https://blog.csdn.net/qq_65207641/article/details/128629904一、编辑器与编译器的区别vim是代码编辑器,代码编辑器的功能是让我们输入代码的。所以从这个角度出发,我们常见的记事本也可以......
  • 适合工业和消费类物联网应用,PIC24FJ128GL305-I/PT、PIC24FJ128GL303-I/M5、PIC24FJ128
    典型应用•人机接口(HMI)•工业和消费类物联网应用•电池供电应用•医疗应用•汽车应用•信息娱乐介绍:PIC24FJGL16位微控制器是超低功耗MCU,设计用于开发各种创新应用(带有或不带显示屏)。这些MCU集成许多内核独立外设(CIP),可在省电模式下运行,可为电池供电和功耗敏感型......
  • linux&windows通过脚本下载ftp文件
    windows@echooffREM登陆ftp下载文件setftpUser=test_usersetftpPass=123456setftpIP=192.168.1.205setftpFolder=/setLocalFolder=C:/Users/Administrator/DesktopsetftpFile=%temp%/TempFTP.txt>"%ftpFile%"(echo,%ftpUser%echo,%ftpPass%......
  • linux系统安装git
    在Linux系统上安装Git可以通过包管理器进行。下面是使用不同包管理器在常见的Linux发行版上安装Git的步骤:使用apt(Debian/Ubuntu)如果您的系统使用apt包管理器,可以使用以下命令安装Git:sudoaptupdatesudoaptinstallgit使用yum(CentOS/RHEL)对于使用yum......
  • 在CentOS Linux系统上安装Docker
    安装Docker在CentOSLinux系统上的步骤如下:更新系统软件包列表:sudoyumupdate安装所需的软件包以支持Docker:sudoyuminstall-yyum-utilsdevice-mapper-persistent-datalvm2添加Docker的官方GPG密钥:sudoyum-config-manager--add-repohttps://download.docke......
  • linux进阶(一)
    1、数据库----跨服务器传输文件scproot@ip:/oafilesystembackup/oadbbak/db-20230512040001-expdp.dmp/oadbbak----单表恢复impdpquery/密码@ecologydirectory=ecologydumpfile=db-20230512040001-expdp.dmptables=ecology.uf_cw_gzb_cwignore=yremap_schema=ecology:que......
  • Linux so文件
    https://www.python100.com/html/I3T3M93XN47U.html一、什么是SO文件SO文件(SharedObject),也被称为共享库、动态链接库,是一种在Linux系统中使用的二进制文件。它包含了可重用的代码、数据和函数等,可以由多个程序同时使用,以节省空间。SO文件是一个编译好的目标文件,其中包含了可供......