目录
背景知识-Linux源码简介
TCP/IP协议栈相关问题
inet_init是如何被调用的?从start_kernel到inet_init调用路径
1.start_kernel():
2.inet_init() :
3.fs_initcall()
跟踪分析TCP/IP协议栈如何将自己与上层套接口与下层数据链路层关联起来的?
TCP的三次握手源代码跟踪分析,跟踪找出设置和发送SYN/ACK的位置,以及状态转换的位置
send在TCP/IP协议栈中的执行路径
recv在TCP/IP协议栈中的执行路径
路由表的结构和初始化过程
路由表数据结构:
初始化过程:
通过目的IP查询路由表的到下一跳的IP地址的过程
ARP缓存的数据结构及初始化过程,包括ARP缓存的初始化
ARP数据结构:
初始化过程:
如何将IP地址解析出对应的MAC地址
跟踪TCP send过程中的路由查询和ARP解析的最底层实现
-
背景知识-Linux源码简介
Linux源码目录,以最新版为例,
源码在线阅读地址Linux source code (v6.7-rc7) - Bootlin
目录说明图源地址Linux内核源码分析:Linux内核版本号和源码目录结构 - 知乎 (zhihu.com)
由上述右图可知,本篇文章所要阅读的TCP/IP协议栈源码在文件夹net/ipv4下。接下来将就TCP/IP协议栈源码内容简要回答下述几个相关问题并做出总结。
-
TCP/IP协议栈相关问题
-
inet_init是如何被调用的?从start_kernel到inet_init调用路径
1.start_kernel()
:这是内核的入口点,初始化核心的硬件组件。首先找到start_kernel函数的位置,该函数用于初始化Linux内核,该函数进行了一系列初始化操作,在
start_kernel
的执行过程中,内核会进行大量的硬件和系统参数的初始化工作,以确保系统的正常运行。接下来我们在该函数中查找关于网络子系统初始化的代码。但在该函数中未找到明确调用inet_init()函数的语句,具体为什么我们稍后解释。
2.inet_init()
:这是在 Linux 内核中用于初始化网络子系统的函数。在内核启动过程中,它被调用以初始化网络协议栈。
inet_init
函数在内核的启动过程中被调用,具体路径从start_kernel
开始,通过一系列初始化函数最终到达inet_init
。首先搜索源码中找到inet_init函数的位置,在net/ipv4/af_inet.c中。
我们找到该函数被调用的位置
3.fs_initcall()
发现119个文件中都有调用该函数,那么我们不可能挨个看,于是我搜索到fs_initcall函数的相关信息:在 Linux 内核中,
fs_initcall
是一个用于初始化文件系统的宏,它定义在fs/Kconfig
和fs /Makefile
中。这个宏用于指定哪些文件系统初始化函数应该在内核启动时按照特定的顺序执行。fs_initcall宏最终是定义了一个静态变量,该变量的类型是initcall_t,值是宏参数表示的函数地址。这就解释了为什么全局都找不到inet_init()被调用的语句。
-
跟踪分析TCP/IP协议栈如何将自己与上层套接口与下层数据链路层关联起来的?
TCP/IP 协议栈将自己与上层套接字(socket)和下层数据链路层关联起来是通过一系列复杂的结构和函数调用实现的。
- 套接字(Socket)层:这是应用程序和协议栈之间的接口。应用程序通过系统调用(如
socket
,bind
,listen
,accept
等)创建和使用套接字。- 协议核心(proto_core):这是协议处理的核心部分,它为每种协议(如 TCP, UDP 等)提供了一个函数指针数组,这些函数指针指向处理特定操作(如接收、发送、连接等)的函数。
- 协议处理(proto_handler):每种协议都有自己的处理函数,这些函数通过协议核心进行注册,以处理特定的操作。例如,TCP 协议处理函数会处理 TCP 连接的建立、数据传输等。
- 套接字结构(sock):每个套接字都有一个与之关联的套接字结构。这个结构包含了指向协议处理的指针,以及其他与套接字相关的信息(如地址、选项等)。
- 路由(route):路由结构包含了如何将数据包路由到目标的信息。这包括目标地址、使用的协议、输出接口等。
- 网络接口(net_device):这是数据链路层的接口。每个网络设备(如以太网卡)都有一个与之关联的
net_device
结构。这个结构包含了设备的各种属性,如硬件地址、MTU 等。- 网络核心(net_core):网络核心提供了通用的网络功能,如初始化网络设备、分发数据包等。
- 数据链路层:这是物理层和网络层之间的接口。Linux 内核支持多种数据链路层协议,如以太网、PPP 等。每种数据链路层都有自己的处理函数,这些函数通过
net_dev
结构注册到网络核心。- 发送和接收:当应用程序发送或接收数据时,它会通过套接字结构访问协议处理函数。协议处理函数会进一步调用网络核心的函数来分发数据包到适当的数据链路层处理函数,最终将数据包发送到物理层或从物理层接收数据包。
-
TCP的三次握手源代码跟踪分析,跟踪找出设置和发送SYN/ACK的位置,以及状态转换的位置
套接字创建与绑定:应用程序通过系统调用(如
socket
,bind
)创建一个套接字,并将其绑定到一个本地地址和端口上。这涉及到内核中socket.c
和相关文件中的函数。三次握手过程:当一个 TCP 连接被发起时(客户端到服务器),会发生三次握手过程。握手过程通过内核中的
tcp_v4_do_rcv
函数开始,该函数在net/ipv4/tcp_input.c
中。SYN 段的接收与处理:当服务器收到一个 SYN 段时,它会处理这个段并发送一个 SYN-ACK 段作为响应。在
tcp_v4_do_rcv
函数中,当检测到一个 SYN 段时,会调用tcp_rcv_synsent _state_process
函数。在这个函数中,会设置SYN-ACK
并将其发送回客户端。状态转换:TCP 有一个状态机,它定义了各种状态之间的转换。在三次握手过程中,服务器的状态会从
TCP_CLOSE
变为TCP_ESTABLISHED
。状态转换的具体位置可以在net/ipv4/tcp_c ore.c
中的tcp_set_state
函数中找到。
-
send在TCP/IP协议栈中的执行路径
应用层:在应用层,应用程序调用
send
函数发送数据。传输层:在传输层,TCP(Transmission Control Protocol)协议负责数据的可靠传输。当接收到来自应用层的
send
请求时,TCP 会添加必要的头信息(如序列号、窗口大小等)到数据包,确保数据按照正确的顺序到达目的地,检测和处理可能的丢包和重复数据包。网络层:在网络层,IP(Internet Protocol)协议负责将数据包从源地址发送到目的地址。当接收到来自传输层的 TCP 数据包时,IP 协议添加必要的头信息(如源和目的 IP 地址)到数据包并将数据包路由到正确的网络或子网。
链路层:在链路层,通常由特定的网络设备驱动程序处理数据包的发送。这一层负责将数据包转换为可以在物理网络上传输的信号(如电信号或光信号)。
物理层:在物理层,数据包最终转换为可以在物理介质(如电缆、光纤或空气)上传输的信号。
-
recv在TCP/IP协议栈中的执行路径
链路层:链路层(如以太网)接收来自物理层的数据包。链路层解封装数据包,提取出IP数据包。
网络层 (IP):IP 协议接收来自链路层的 IP 数据包。IP 检查数据包的头部信息,例如源IP地址和目的IP地址,以确定是否应将数据包传递给本地主机或转发给其他路由器。如果数据包是发送给本机的,IP 将其传递给传输层。
传输层 (TCP):TCP 协议接收来自网络层的 IP 数据包。TCP 检查数据包的头部信息,如序列号和端口号,以确定应将数据包传递给哪个应用程序。TCP 解封装数据包,提取出原始数据,并确保数据的顺序是正确的。TCP 将数据传递给应用层。
应用层:应用程序调用
recv
函数来接收数据。recv
函数从内核空间复制数据到应用程序的缓冲区。应用程序处理接收到的数据,并可以继续发送或接收更多的数据。
-
路由表的结构和初始化过程
在Linux内核中,路由表的结构和初始化过程涉及到多个组件和数据结构。
路由表数据结构:
struct route_entry
:这是路由表条目的基本结构,包含了目标网络地址、路由类型(直接、网关等)、子网掩码、下一跳地址等信息。struct fib_entry
:这是一个更通用的结构,用于表示路由表中的条目,可以包含多个route_entry
。struct fib_table
:这是路由表的抽象结构,定义了路由表的添加、删除、查找等操作。
初始化过程:
- 在系统启动时,内核会初始化路由表相关的数据结构。
- 解析和加载内核模块(如ip_route_mod),这些模块提供了路由表的操作功能。
- 读取和解析配置文件(如
/etc/network/interfaces
),以获取网络接口和路由的配置信息。
-
通过目的IP查询路由表的到下一跳的IP地址的过程
当需要查询路由表时,内核会使用目的IP地址作为查询条件。首先,内核会查找与目的IP地址匹配的路由条目。这通常通过遍历路由表中的每个条目并比较其目标网络地址来完成。如果找到了匹配的条目,内核将提取该条目的下一跳IP地址。这一地址通常存储在
fib_entry
结构中的fib_nh
字段中。内核使用特定的算法(如最长前缀匹配)来查找与目的IP地址匹配的路由条目。如果找到多个匹配的条目,内核可能会根据优先级或其他策略选择其中的一个条目。如果存在多个匹配的条目(即多路径),内核可能需要执行策略路由决策。这可能涉及根据不同的参数(如源IP地址、目的端口等)选择不同的下一跳地址。一旦确定了下一跳IP地址,内核将该地址返回给调用者。
-
ARP缓存的数据结构及初始化过程,包括ARP缓存的初始化
ARP数据结构:
struct neighbour
:这是邻居表的核心结构,包含了ARP缓存条目的主要信息,如IP地址、MAC地址、设备索引等。struct neigh_table
:这是邻居表的抽象结构,定义了ARP缓存的添加、删除、查找等操作。
初始化过程:
- 创建邻居表:在系统启动时,内核会创建邻居表。这个表用于存储ARP缓存条目。
- 加载和解析ARP模块:内核会加载ARP模块,该模块提供了ARP缓存操作的功能。同时,内核会解析ARP配置文件(如
/etc/arpwatch.conf
),获取ARP缓存的配置信息。- 初始化ARP缓存:内核会根据配置信息初始化ARP缓存。这包括设置ARP缓存的大小、分配内存空间等。
-
如何将IP地址解析出对应的MAC地址
在Linux内核中,IP地址到MAC地址的解析是通过ARP(Address Resolution Protocol)协议来实现的。当一个主机需要将IP数据包发送到另一个主机时,如果它不知道目标主机的MAC地址,它会发送一个ARP请求来获取目标主机的MAC地址。
ARP请求和应答:当一个主机需要知道另一个主机的MAC地址时,它会发送ARP请求数据包。ARP请求包含发送者的IP地址和MAC地址,以及目标IP地址。目标主机收到ARP请求后,会发送ARP应答,其中包含自己的MAC地址。
ARP缓存:Linux内核维护了一个ARP缓存,其中存储了已知主机的IP地址到MAC地址的映射。这样,如果一个主机之前已经解析过另一个主机的MAC地址,它可以直接从ARP缓存中获取。
ARP解析函数:在Linux内核中,
arp_find()
函数用于根据IP地址查找对应的MAC地址。这个函数首先会在ARP缓存中查找,如果找不到,则会发送ARP请求来获取MAC地址。维护ARP缓存:ARP缓存中的条目会随着网络活动而变化。当网络接口启动或停止时,或者当收到ARP请求或应答时,内核会更新ARP缓存
-
跟踪TCP send过程中的路由查询和ARP解析的最底层实现
进入内核空间:当应用程序调用
send()
函数时,它首先进入内核空间。这是通过系统调用接口完成的。TCP Send处理:内核中的TCP协议栈开始处理发送操作。这包括将数据包分段、添加TCP头等。文章来源:https://www.toymoban.com/news/detail-785787.html
- 查找路由:内核会查询路由表以确定最佳的出口接口。这涉及到遍历路由表并根据目标IP地址、子网掩码等信息进行匹配。
- ARP解析(如果需要):如果目标IP地址不在ARP缓存中,内核会发送ARP请求来获取MAC地址。ARP请求发送后,内核会等待ARP应答或超时。
- 数据包封装:一旦获得了MAC地址,内核会将IP数据包封装在以太网帧中,并设置相应的MAC头信息。
- 发送数据包:最后,内核将数据包发送到选定的网络接口。这涉及到硬件操作和可能的DMA传输。
上述内容大部分原创,有一部分理解不了的求助了gpt,如有错误请多包含。文章来源地址https://www.toymoban.com/news/detail-785787.html
到了这里,关于Linux内核中的TCP/IP协议栈源代码分析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!