inet_init是如何被调用的?从start_kernel到inet_init调用路径
start_kernel 函数在 init/main.c 文件中定义,它是内核启动的入口点。
在 start_kernel 中,会调用 rest_init,这是一个初始化函数,它在 kernel/init.c 文件中定义。
rest_init 会调用 kernel_init_freeable,这是另一个初始化函数,它在 kernel/init.c 文件中定义。
kernel_init_freeable 中会调用 net_init,这是网络初始化的入口点,它在 net/core/sock.c 文件中定义。
net_init 会调用 inet_init,这是初始化TCP/IP协议栈的函数,它在 net/ipv4/af_inet.c 文件中定义。
跟踪分析TCP/IP协议栈如何将自己与上层套接口与下层数据链路层关联起来的?
协议上下都通过TCP/ IP相关联。
TCP/IP协议栈通过一系列的数据结构和函数调用来实现与上层套接口(如应用层)和下层数据链路层的关联。以下是TCP/IP协议栈中一些关键组件和它们如何相互关联的概述:
-
套接口(Socket):
- 套接口是应用层与网络协议栈之间的接口。在Linux中,套接口通常通过系统调用
socket
创建。
- 套接口提供了一组API,如
bind
, listen
, connect
, accept
, send
, recv
等,这些API允许应用层发送和接收数据。
-
协议族(Protocol Family):
- 当创建套接口时,需要指定协议族,如
AF_INET
(IPv4)或 AF_INET6
(IPv6)。
- 协议族定义了数据包的格式和地址结构。
-
协议(Protocol):
- 在创建套接口时,还需要指定协议,如
SOCK_STREAM
(TCP)或 SOCK_DGRAM
(UDP)。
- 协议定义了数据传输的方式,如可靠性、顺序等。
-
网络核心API:
- 网络核心API,如
inet_sendmsg
和 inet_recvmsg
,提供了与套接口API交互的接口。
- 这些API处理套接口数据的发送和接收,并将数据传递给相应的协议层。
-
协议层处理:
- TCP/IP协议栈中的每个协议层(如TCP、IP)都有自己的处理函数。
- 当数据从应用层通过套接口发送时,它会经过协议层的处理,如TCP层的
tcp_sendmsg
,然后传递给IP层。
- 在接收数据时,数据首先由IP层处理,然后传递给TCP层的
tcp_recvmsg
,最后传递给应用层。
-
数据链路层:
- IP层将数据封装成IP数据包,然后传递给网络设备驱动程序。
- 网络设备驱动程序将IP数据包封装成数据链路层的帧,并发送到物理网络上。
-
路由和ARP:
- 在发送数据包之前,内核会查询路由表以确定数据包的下一跳。
- 如果下一跳不在本地网络中,内核会通过ARP协议解析下一跳的MAC地址。
-
网络设备驱动程序:
- 网络设备驱动程序负责与物理网络接口通信,发送和接收数据包。
通过这些组件和它们的交互,TCP/IP协议栈实现了从应用层到数据链路层的完整通信路径。在实际的Linux内核源代码中,这些组件的实现可能会更加复杂,并且涉及到更多的细节。
TCP的三次握手源代码跟踪分析
TCP三次握手涉及的函数通常在 net/ipv4/tcp_ipv4.c 文件中。
tcp_v4_connect 函数处理连接请求,它调用 tcp_create_openreq_child 创建一个新的连接请求。
tcp_create_openreq_child 创建一个新的 struct request_sock 实例,并调用 tcp_v4_send_syn 发送SYN包。
tcp_v4_send_syn 设置SYN标志位并发送SYN包。
tcp_v4_send_synack 在接收到SYN包后发送SYN-ACK包。
tcp_v4_do_rcv 处理接收到的TCP段,根据状态机进行状态转换。
send在TCP/IP协议栈中的执行路径
send 函数在用户空间通过系统调用 sys_sendto 或 sys_sendmsg 实现。
在内核空间,sys_sendto 或 sys_sendmsg 调用 inet_sendmsg。
inet_sendmsg 调用 tcp_sendmsg,然后是 tcp_write_xmit。
tcp_write_xmit 封装数据成TCP段,并通过 ip_local_out 发送。
recv在TCP/IP协议栈中的执行路径
recv 函数在用户空间通过系统调用 sys_recvfrom 或 sys_recvmsg 实现。
在内核空间,sys_recvfrom 或 sys_recvmsg 调用 inet_recvmsg。
inet_recvmsg 调用 tcp_recvmsg,然后是 tcp_prequeue_process。
数据被接收后,通过 sk_filter_trim_cap 等函数进行过滤,然后传递给应用层。
路由表的结构和初始化过程
路由表由 struct fib_table 和 struct fib_info 表示,这些结构体在 net/ipv4/fib_semantics.h 文件中定义。
初始化过程在 fib_net_init 函数中进行,这个函数在 net/ipv4/fib_main.c 文件中定义。
通过目的IP查询路由表的到下一跳的IP地址的过程
在Linux网络核心中,查询路由表以找到目的IP地址的下一跳IP地址的过程涉及以下几个关键步骤:
-
路由表查找:
- 当数据包到达网络核心时,内核会首先查找路由表以确定数据包的下一跳。
- 路由表是由
struct fib_table
结构体表示的,它包含了一系列的路由规则,每个规则由struct fib_info
结构体表示。
-
最长前缀匹配(LPM):
- 内核使用最长前缀匹配算法来查找与目的IP地址匹配的最长前缀。
- 这个过程通常在
fib_lookup
函数中完成,该函数在net/ipv4/fib_semantics.c
文件中定义。
-
路由选择:
- 如果找到了匹配的路由,内核会根据路由规则选择下一跳的IP地址。
- 路由规则可能包括直接连接的网络接口、远程网络的网关地址等。
-
ARP解析:
- 如果下一跳是远程网络,内核会通过ARP协议解析下一跳的MAC地址。
- ARP请求由
arp_send_request
函数发送,该函数在net/ipv4/arp.c
文件中定义。
-
数据包封装:
- 一旦确定了下一跳的IP地址和MAC地址,数据包就会被封装成适当的数据链路层帧。
- 在IPv4中,这通常涉及到
dev_hard_header
和skb_push
等函数。
-
数据包发送:
- 最后,封装好的数据包会被发送到网络接口,由网络设备驱动程序处理。
这个过程涉及到多个内核模块和函数,它们协同工作以确保数据包能够正确地路由到目的地。在实际的Linux内核源代码中,这些步骤可能会更加复杂,并且涉及到更多的细节和错误处理逻辑。
##ARP缓存的数据结构及初始化过程
ARP缓存使用 struct arp_table 数据结构表示,这个结构体在 net/ipv4/arp.h 文件中定义。
初始化过程在 arp_init 函数中进行,这个函数在 net/ipv4/arp.c 文件中定义。
如何将IP地址解析出对应的MAC地址
将IP地址解析为MAC地址的过程涉及发送ARP请求,这在 net/ipv4/arp.c 文件中的 arp_send 函数中实现。
void arp_send(int type, int ptype, __be32 dest_ip,
struct net_device *dev, __be32 src_ip,
const unsigned char *dest_hw, const unsigned char *src_hw,
const unsigned char *target_hw)
{
arp_send_dst(type, ptype, dest_ip, dev, src_ip, dest_hw, src_hw,
target_hw, NULL);
}
可以看到相应的封装
跟踪TCP send过程中的路由查询和ARP解析的最底层实现
在 tcp_write_xmit 中,数据被封装成TCP段。
然后调用 ip_local_out 进行路由查询,这在 net/ipv4/ip_output.c 文件中的 ip_local_out 函数中实现。
如果目标IP地址不在本地网络中,内核会调用 arp_send_request 来解析目标IP地址对应的MAC地址,这在 net/ipv4/arp.c 文件中的 arp_send_request 函数中实现。