解析接收网络包的过程

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 【8月更文挑战第6天】IP层->TCP层->Socket层

在 tcp_v4_rcv 中,得到 TCP 的头之后,我们可以开始处理 TCP 层的事情。因为 TCP 层是分状态的,状态被维护在数据结构 struct sock 里面,因而我们要根据 IP 地址以及 TCP 头里面的内容,在 tcp_hashinfo 中找到这个包对应的 struct sock,从而得到这个包对应的连接的状态。


接下来,我们就根据不同的状态做不同的处理,TCP_LISTEN、TCP_NEW_SYN_RECV 状态属于连接建立过程中。TCP_TIME_WAIT 状态是连接结束的时候的状态。

我们来分析最主流的网络包的接收过程,这里面涉及三个队列:

  • backlog 队列
  • prequeue 队列
  • sk_receive_queue 队列


为什么接收网络包的过程,需要在这三个队列里面倒腾过来、倒腾过去呢?这是因为,同样一个网络包要在三个主体之间交接。

第一个主体是软中断的处理过程。在执行 tcp_v4_rcv 函数的时候,依然处于软中断的处理逻辑里,所以必然会占用这个软中断。

第二个主体就是用户态进程。如果用户态触发系统调用 read 读取网络包,也要从队列里面找。

第三个主体就是内核协议栈。哪怕用户进程没有调用 read,读取网络包,当网络包来的时候,也得有一个地方收着呀。

当前这个 sock 是不是正有一个用户态进程等着读数据呢,如果没有,内核协议栈也调用 tcp_add_backlog,暂存在 backlog 队列中,并且抓紧离开软中断的处理过程。

如果把 sysctl_tcp_low_latency 设置为 0,那就要放在 prequeue 队列中暂存,这样不用等待网络包处理完毕,就可以离开软中断的处理过程,但是会造成比较长的时延。如果把 sysctl_tcp_low_latency 设置为 1,我们还是调用 tcp_v4_do_rcv。

在 tcp_v4_do_rcv 中,分两种情况,一种情况是连接已经建立,处于 TCP_ESTABLISHED 状态,调用 tcp_rcv_established。另一种情况,就是其他的状态,调用 tcp_rcv_state_process。

对于 TCP 所有状态的处理,其中和连接建立相关的状态。

image.png image.gif

在 tcp_data_queue 中,对于收到的网络包,我们要分情况进行处理。

第一种情况,seq == tp->rcv_nxt,说明来的网络包正是我服务端期望的下一个网络包。这个时候我们判断 sock_owned_by_user,也即用户进程也是正在等待读取,这种情况下,就直接 skb_copy_datagram_msg,将网络包拷贝给用户进程就可以了。


如果用户进程没有正在等待读取,或者因为内存原因没有能够拷贝成功,tcp_queue_rcv 里面还是将网络包放入 sk_receive_queue 队列。

接下来,tcp_rcv_nxt_update 将 tp->rcv_nxt 设置为 end_seq,也即当前的网络包接收成功后,更新下一个期待的网络包。

我们还会判断一下另一个队列,out_of_order_queue,也看看乱序队列的情况,看看乱序队列里面的包,会不会因为这个新的网络包的到来,也能放入到 sk_receive_queue 队列中。

乱序的包不能进入 sk_receive_queue 队列。因为一旦进入到这个队列,意味着可以发送给用户进程。然而,按照 TCP 的定义,用户进程应该是按顺序收到包的,没有排好序,就不能给用户进程。

第二种情况,end_seq 不大于 rcv_nxt,也即服务端期望网络包 5。但是,来了一个网络包 3,怎样才会出现这种情况呢?肯定是服务端早就收到了网络包 3,但是 ACK 没有到达客户端,中途丢了,那客户端就认为网络包 3 没有发送成功,于是又发送了一遍,这种情况下,要赶紧给客户端再发送一次 ACK,表示早就收到了。

第三种情况,seq 不小于 rcv_nxt + tcp_receive_window。这说明客户端发送得太猛了。本来 seq 肯定应该在接收窗口里面的,这样服务端才来得及处理,结果现在超出了接收窗口,说明客户端一下子把服务端给塞满了。

这种情况下,服务端不能再接收数据包了,只能发送 ACK 了,在 ACK 中会将接收窗口为 0 的情况告知客户端,客户端就知道不能再发送了。这个时候双方只能交互窗口探测数据包,直到服务端因为用户进程把数据读走了,空出接收窗口,才能在 ACK 里面再次告诉客户端,又有窗口了,又能发送数据包了。

第四种情况,seq 小于 rcv_nxt,但是 end_seq 大于 rcv_nxt,这说明从 seq 到 rcv_nxt 这部分网络包原来的 ACK 客户端没有收到,所以重新发送了一次,从 rcv_nxt 到 end_seq 时新发送的,可以放入 sk_receive_queue 队列。


当接收的网络包进入各种队列之后,接下来我们就要等待用户进程去读取它们了。

读取一个 socket,就像读取一个文件一样,读取 socket 的文件描述符,通过 read 系统调用。

read 系统调用对于一个文件描述符的操作,大致过程都是类似的。最终它会调用到用来表示一个打开文件的结构 stuct file 指向的 file_operations 操作。

整个过程可以分成以下几个层次。

  • 硬件网卡接收到网络包之后,通过 DMA 技术,将网络包放入 Ring Buffer;
  • 硬件网卡通过中断通知 CPU 新的网络包的到来;
  • 网卡驱动程序会注册中断处理函数 ixgb_intr;
  • 中断处理函数处理完需要暂时屏蔽中断的核心流程之后,通过软中断 NET_RX_SOFTIRQ 触发接下来的处理过程;
  • NET_RX_SOFTIRQ 软中断处理函数 net_rx_action,net_rx_action 会调用 napi_poll,进而调用 ixgb_clean_rx_irq,从 Ring Buffer 中读取数据到内核 struct sk_buff;
  • 调用 netif_receive_skb 进入内核网络协议栈,进行一些关于 VLAN 的二层逻辑处理后,调用 ip_rcv 进入三层 IP 层;
  • 在 IP 层,会处理 iptables 规则,然后调用 ip_local_deliver 交给更上层 TCP 层;
  • 在 TCP 层调用 tcp_v4_rcv,这里面有三个队列需要处理,如果当前的 Socket 不是正在被读;取,则放入 backlog 队列,如果正在被读取,不需要很实时的话,则放入 prequeue 队列,其他情况调用 tcp_v4_do_rcv;
  • 在 tcp_v4_do_rcv 中,如果是处于 TCP_ESTABLISHED 状态,调用 tcp_rcv_established,其他的状态,调用 tcp_rcv_state_process;
  • 在 tcp_rcv_established 中,调用 tcp_data_queue,如果序列号能够接的上,则放入 sk_receive_queue 队列;如果序列号接不上,则暂时放入 out_of_order_queue 队列,等序列号能够接上的时候,再放入 sk_receive_queue 队列。

接下来就是用户态读取网络包的过程,这个过程分成几个层次。

  • VFS 层:read 系统调用找到 struct file,根据里面的 file_operations 的定义,调用 sock_read_iter 函数。sock_read_iter 函数调用 sock_recvmsg 函数。
  • Socket 层:从 struct file 里面的 private_data 得到 struct socket,根据里面 ops 的定义,调用 inet_recvmsg 函数。
  • Sock 层:从 struct socket 里面的 sk 得到 struct sock,根据里面 sk_prot 的定义,调用 tcp_recvmsg 函数。
  • TCP 层:tcp_recvmsg 函数会依次读取 receive_queue 队列、prequeue 队列和 backlog 队列。
目录
打赏
0
0
0
0
443
分享
相关文章
|
18天前
|
网络问题解析:如何解决CondaHTTPError HTTP 000 CONNECTION FAILED错误。
以上就是斯诺普为你准备的解决Conda出现HTTP连接错误的手术室。希望这辆小车可以顺利驶出棘手的泥潭,再次在自由的大路上疾驰。一切的尝试和努力,只为更好的探索与开发。
70 17
深入解析PDCERF:网络安全应急响应的六阶段方法
PDCERF是网络安全应急响应的六阶段方法,涵盖准备、检测、抑制、根除、恢复和跟进。本文详细解析各阶段目标与操作步骤,并附图例,助读者理解与应用,提升组织应对安全事件的能力。
544 89
深入解析图神经网络注意力机制:数学原理与可视化实现
本文深入解析了图神经网络(GNNs)中自注意力机制的内部运作原理,通过可视化和数学推导揭示其工作机制。文章采用“位置-转移图”概念框架,并使用NumPy实现代码示例,逐步拆解自注意力层的计算过程。文中详细展示了从节点特征矩阵、邻接矩阵到生成注意力权重的具体步骤,并通过四个类(GAL1至GAL4)模拟了整个计算流程。最终,结合实际PyTorch Geometric库中的代码,对比分析了核心逻辑,为理解GNN自注意力机制提供了清晰的学习路径。
246 7
深入解析图神经网络注意力机制:数学原理与可视化实现
【Android】网络技术知识总结之WebView,HttpURLConnection,OKHttp,XML的pull解析方式
本文总结了Android中几种常用的网络技术,包括WebView、HttpURLConnection、OKHttp和XML的Pull解析方式。每种技术都有其独特的特点和适用场景。理解并熟练运用这些技术,可以帮助开发者构建高效、可靠的网络应用程序。通过示例代码和详细解释,本文为开发者提供了实用的参考和指导。
69 15
【Azure K8S | AKS】在AKS的节点中抓取目标POD的网络包方法分享
在AKS中遇到复杂网络问题时,可通过以下步骤进入特定POD抓取网络包进行分析:1. 使用`kubectl get pods`确认Pod所在Node;2. 通过`kubectl node-shell`登录Node;3. 使用`crictl ps`找到Pod的Container ID;4. 获取PID并使用`nsenter`进入Pod的网络空间;5. 在`/var/tmp`目录下使用`tcpdump`抓包。完成后按Ctrl+C停止抓包。
69 12
JS数组操作方法全景图,全网最全构建完整知识网络!js数组操作方法全集(实现筛选转换、随机排序洗牌算法、复杂数据处理统计等情景详解,附大量源码和易错点解析)
这些方法提供了对数组的全面操作,包括搜索、遍历、转换和聚合等。通过分为原地操作方法、非原地操作方法和其他方法便于您理解和记忆,并熟悉他们各自的使用方法与使用范围。详细的案例与进阶使用,方便您理解数组操作的底层原理。链式调用的几个案例,让您玩转数组操作。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
阿里云CDN:全球加速网络的实践创新与价值解析
在数字化浪潮下,用户体验成为企业竞争力的核心。阿里云CDN凭借技术创新与全球化布局,提供高效稳定的加速解决方案。其三层优化体系(智能调度、缓存策略、安全防护)确保低延迟和高命中率,覆盖2800+全球节点,支持电商、教育、游戏等行业,帮助企业节省带宽成本,提升加载速度和安全性。未来,阿里云CDN将继续引领内容分发的行业标准。
131 7
自己动手编写tcp/ip协议栈1:tcp包解析
学习tuntap中的tun的使用方法,并使用tun接口解析了ip包和tcp包,这是实现tcp/ip协议栈的第一步。
71 15
阿里云网络安全体系解析:如何构建数字时代的"安全盾牌"
在数字经济时代,阿里云作为亚太地区最大的云服务提供商,构建了行业领先的网络安全体系。本文解析其网络安全架构的三大核心维度:基础架构安全、核心技术防护和安全管理体系。通过技术创新与体系化防御,阿里云为企业数字化转型提供坚实的安全屏障,确保数据安全与业务连续性。案例显示,某金融客户借助阿里云成功拦截3200万次攻击,降低运维成本40%,响应时间缩短至8分钟。未来,阿里云将继续推进自适应安全架构,助力企业提升核心竞争力。
深入解析:Linux网络配置工具ifconfig与ip命令的全面对比
虽然 `ifconfig`作为一个经典的网络配置工具,简单易用,但其功能已经不能满足现代网络配置的需求。相比之下,`ip`命令不仅功能全面,而且提供了一致且简洁的语法,适用于各种网络配置场景。因此,在实际使用中,推荐逐步过渡到 `ip`命令,以更好地适应现代网络管理需求。
101 11

推荐镜像

更多