解析接收网络包的过程

本文涉及的产品
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: 【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 队列。
相关文章
|
13天前
|
SQL 安全 算法
网络安全与信息安全的全面解析:应对漏洞、加密技术及提升安全意识的策略
本文深入探讨了网络安全和信息安全的重要性,详细分析了常见的网络安全漏洞以及其利用方式,介绍了当前流行的加密技术及其应用,并强调了培养良好安全意识的必要性。通过综合运用这些策略,可以有效提升个人和企业的网络安全防护水平。
|
6天前
|
机器学习/深度学习 数据采集 存储
时间序列预测新突破:深入解析循环神经网络(RNN)在金融数据分析中的应用
【10月更文挑战第7天】时间序列预测是数据科学领域的一个重要课题,特别是在金融行业中。准确的时间序列预测能够帮助投资者做出更明智的决策,比如股票价格预测、汇率变动预测等。近年来,随着深度学习技术的发展,尤其是循环神经网络(Recurrent Neural Networks, RNNs)及其变体如长短期记忆网络(LSTM)和门控循环单元(GRU),在处理时间序列数据方面展现出了巨大的潜力。本文将探讨RNN的基本概念,并通过具体的代码示例展示如何使用这些模型来进行金融数据分析。
42 2
|
10天前
|
SQL 安全 网络安全
网络安全的盾牌与利剑:漏洞防御与加密技术解析
【10月更文挑战第3天】在数字化浪潮中,网络安全成为保障信息资产的关键防线。本文将深入探讨网络安全中的两大核心议题:网络漏洞防御和加密技术。我们将从基础概念出发,逐步分析漏洞产生的原因、影响及防范措施,并详细解读加密技术的工作原理和应用实例。文章旨在通过理论与实践的结合,增强读者的安全意识和技能,为构建更加稳固的网络环境提供实用指南。
22 1
|
8天前
|
安全 网络安全 API
网络安全的盾牌与剑:漏洞防御与加密技术解析
【10月更文挑战第5天】在数字时代的浪潮中,网络安全成为保护个人隐私与企业资产的关键战场。本文深入浅出地探讨了网络安全中的两大核心要素——安全漏洞与加密技术,旨在提升公众的安全意识,并分享实用的防护知识。通过分析常见的网络攻击手段和防御策略,文章揭示了网络安全的本质,强调了预防胜于治疗的智慧。
24 5
|
9天前
|
自动驾驶 物联网 5G
|
12天前
|
域名解析 缓存 网络协议
【网络】DNS,域名解析系统
【网络】DNS,域名解析系统
49 1
|
1天前
|
供应链 网络协议 数据安全/隐私保护
|
4天前
|
运维 安全 网络协议
Python 网络编程:端口检测与IP解析
本文介绍了使用Python进行网络编程的两个重要技能:检查端口状态和根据IP地址解析主机名。通过`socket`库实现端口扫描和主机名解析的功能,并提供了详细的示例代码。文章最后还展示了如何整合这两部分代码,实现一个简单的命令行端口扫描器,适用于网络故障排查和安全审计。
12 0
|
4天前
|
SQL 安全 网络安全
网络安全与信息安全:防范措施与加密技术解析
【10月更文挑战第9天】在数字化时代,网络安全与信息安全已成为我们日常生活中不可或缺的一部分。本文将深入探讨网络安全漏洞、加密技术和安全意识等方面的内容,以帮助读者更好地了解和应对网络安全威胁。通过分享相关知识和案例分析,我们希望提高大家对网络安全的认识和重视程度,共同维护一个安全、可靠的网络环境。
13 0
|
14天前
http数据包抓包解析课程笔记
http数据包抓包解析课程笔记

推荐镜像

更多