专栏首页专注网络研发Linux数据报文的来龙去脉

Linux数据报文的来龙去脉

作者:gfree.wind@gmail.com

作为网络领域的开发人员,我们经常要与Linux的数据报文打交道,一定要搞清楚数据报文是从何而来,又是如何离去。以前针对这个主题写过一些文章(主要是从源码角度),这次会更重视流程示意图(在细节上必然有所简化),争取在一篇文章中,就让大家理清数据报文的来龙去脉。

一、网卡把报文传到内核的流程图

图1. 网卡传递数据包到内核的流程图

1. 网卡在启动时会申请一个接收ring buffer,其条目都会指向一个skb的内存。

2. DMA完成数据报文从网卡硬件到内存到拷贝后,网卡发送一个中断通知CPU。

3. CPU执行网卡驱动注册的中断处理函数,中断处理函数只做一些必要的工作,如读取硬件状态等,并把当前该网卡挂在NAPI的链表中,同时会“触发”NET_RX_SOFTIRQ(其实就是设置对应软中断的标志位)。

4. CPU中断处理函数返回后,会检查是否有软中断需要执行。因第三步设置了NET_RX_SOFTIRQ,则执行报文接收软中断。

5. 在NET_RX_SOFTIRQ软中断中,执行NAPI操作,回调第三步挂载的驱动poll函数。

6. 驱动会对interface进行poll操作,检查网卡是否有接收完毕的数据报文。

7. 将网卡中已经接收完毕的数据报文取出,继续在软中断进行后续处理。

注:驱动对interface执行poll操作时,会尝试循环检查网卡是否有接收完毕的报文,直到设置的budget上限,或者已经就绪报文。

二、接收软中断将报文分发给协议栈的示意图

图2. 接收软中断将报文分发给对应协议栈

1. 假设是CPU0在执行接收软中断net_rx_action

2. 接收报文的网卡是否支持XDP,若支持且启用,则进行xdp的检查。

3. 查看是否启用了RPS & RFS。若启用且目的CPU非当前CPU,则将数据报文enque到目的CPU的backlog队列中,并在软中断中发送IPI中断给目的CPU。目的CPU的IPI处理函数会将目的CPU的backlog挂到其NAPI列表中,这样由其他CPU发送过来的报文,就会在其后面NAPI中被处理。

4. CPU0负责处理当前报文。如果网卡没有vlan offload,则需要软件剥掉vlan头,以便后面的报文处理。

5. 在分发报文时,可能会有多个handler关心此报文。所以在分发时,都是增加引用计数,然后给对应的处理函数。

6. 将报文分发给“ETH_P_ALL”的handler,即关心所有以太网报文的handler(不限于任何协议)

7. 通过以太网报文的协议,将数据报文分发给该协议的handler,如IPv4,IPv6,PPPoE等。

三、协议栈将数据报文发给套接字(以IPv4为例)的流程图

图3. 协议栈将报文发给套接字的流程图

报文从上到下的分发过程很相似。每层协议都会包含上层协议类型,然后根据类型进行分发。以IPv4协议栈(inet协议族)来说,初始化阶段,调用dev_add_pack注册了二层以太网的IP协议类型。而四层协议icmp、udp、tcp也是在inet初始化阶段,调用inet_add_protocol注册。

那么,报文接收的流程如下:

1. __netif_receive_skb_core处于二层协议处理阶段,其根据以太网的报文类型,从packet_type中找到匹配的三层协议。

2. 进入ip报文的处理函数ip_rcv,进行netfiler的prerouting阶段的检查。

3. 获得四层协议类型,调用其early_demux。这是一个优化,对于符合条件的报文,可以尽早处理。

4. 查找路由。对于发给本机的IP报文,其路由的input处理函数,即ip_local_deliver。

5. 继续netfilter的localin阶段的检查。

6. 通过检查后,将报文发给本机的raw socket。

7. 根据四层协议类型,调用匹配的四层协议处理函数。对于UDP报文来说,就是udp_rcv。

8. 根据源端口和目的端口,确定socket套接字。

9. 将skb报文加入套接字的接收队列。

四、报文从应用层到网卡的流程图

图4. 应用层发包到网卡的流程

1. 应用层调用socket系统调用,内核会在内部根据协议类型,创建一个socket对象,且其成员变量ops指向udp_proto(以UDP为例)。

2. 应用层调用send,write等发送函数,将socket fd和数据data传给内核。

3. 内核通过fd获得socket对象,并将应用层的数据复制到内核,调用socket成员变量对应的sendmsg。

4. 内核调用ip_route_output_flow查询路由。

5. 调用ip_make_skb,申请一个skb用于发送报文,并填充了IP头。

6. 调用udp_send、ip_send_skb,填充UDP报文头,计算IP头的checksum等。

7. 调用ip_localout,到达本机IP层发送报文的最后阶段,进行netfilter localout阶段的检查。

8. 调用neigh_output,即邻居层填充二层目的MAC。如果没有ARP信息,则缓存报文,发送ARP报文,进行二层地址解析。

9. 调用dev_queue_xmit,进入qdisc schedule即流量控制,也就是TC的实现。

10. 默认情况,网卡的qdisc策略是FIFO。被schedule的数据报文,通过dev_hard_start_xmit调用网卡驱动的ndo_start_xmit,将报文交给网卡进行发送。

通过以上四个分解的流程图,相信大家对于Linux数据报文的来龙去脉,有了一定的了解。如在文章开头所云,这些流程图都做了必要的简化。在很多步骤都可以进行展开,也涉及了更多细节。

一句话,内核的网络模块,既简单又复杂。它的流程很简单,但每一步的处理又涉及了很多细节,欢迎大家和我一起讨论研究。

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

本文分享自微信公众号 - LinuxerPub(LinuxerPub),作者:glinuxer

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-03-11

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Linux数据报文的来龙去脉

    作为网络领域的开发人员,我们经常要与Linux的数据报文打交道,一定要搞清楚数据报文是从何而来,又是如何离去。以前针对这个主题写过一些文章(主要是从源码角度),...

    glinuxer
  • Linux网络性能优化相关策略

    1. rx-checksumming:校验接收报文的checksum。

    glinuxer
  • TCP的MTU Probe和MSS(2)

    在上一篇《TCP的MTU Probe和MSS(1)》介绍了TCP使用MTU Probe来避免PMTU变小而导致发送失败的方法。其主要思想是在TC...

    glinuxer
  • Linux数据报文的来龙去脉

    作为网络领域的开发人员,我们经常要与Linux的数据报文打交道,一定要搞清楚数据报文是从何而来,又是如何离去。以前针对这个主题写过一些文章(主要是从源码角度),...

    glinuxer
  • 计算机网络之网络安全基础-消息完整性与数字签名

    (1). 发送方对报文m应用散列函数, 得到固定长度的散列码, 获得报文摘要h, 将扩展报文(m,h)发送给接收方;

    越陌度阡
  • 速读原著-TCP/IP(ICMP:Internet控制报文协议)

    I C M P经常被认为是 I P层的一个组成部分。它传递差错报文以及其他需要注意的信息。I C M P报文通常被I P层或更高层协议( T C P或U D P...

    cwl_java
  • 计算机网络学习记录(不断更新)

    平时TTL减为0时最后一个经手的路由器会返回TTL-Exceeded报文,到目的主机后由于端口大于30000,会返回Port-Unreachable报文,这样就...

    xiaoxi666
  • 网络安全

    对称密钥中,加解密双方难以使用相同密钥,难以事先确定使用一样的密钥。如果网上传输密钥,也会被人截取(截取后,该信息不会发给接收方,只能由接收方发)知道的,恶意者...

    intsmaze-刘洋
  • php一步一步实现mysql协议(三) ——登录认证密码加密

    认证阶段抓包如上图,和初始化握手一样,前四个字节属于消息头,后面的部分属于消息体 。报文的结构图如下:

    码缘
  • [协议]ICMP协议剖析

    1、ICMP简介 ICMP全名为(INTERNET CONTROL MESSAGE PROTOCOL)网络控制消息协议。 ICMP的协议号为1。 ICMP报文就...

    静默虚空

扫码关注云+社区

领取腾讯云代金券