传输层 udptcp
内核版本采用4.9.88
套接字分析
-
系统入口要求:每个操作系统都必须提供网络子系统入口及API,Linux内核网络子系统提供标准的POSIX套接字API接口
-
用户空间特性:在Linux中传输层之上的一切都属于用户空间,遵循Unix"一切皆为文件"范式
-
文件关联性:套接字与文件相关联,使用统一API使应用程序移植更容易
1)套接字类型详解
-
SOCK_STREAM流套接字
-
通信特性:提供可靠的字节流通信信道
-
典型协议:TCP套接字属于流套接字类型
-
可靠性表现:保证数据按顺序到达且不重复
-
-
SOCK_DGRAM数据报套接字
-
消息交换:支持以数据包为单位的消息交换
-
不可靠特性:通信信道不可靠,可能出现丢包、乱序或重复
-
典型协议:UDP套接字属于此类型
-
风险说明:数据可能被丢弃、不按序到达或重复传输
-
-
SOCK_RAW原始套接字
-
访问层级:直接访问IP层,绕过传输层协议
-
协议无关性:支持使用协议无关的传输层格式收发数据流
-
特殊用途:常用于网络协议开发和分析
-
-
SOCK_RDM可靠消息套接字
-
核心特性:提供可靠传输的消息服务
-
应用场景:主要用于透明进程间通信(TIPC)
-
开发背景:最初由爱立信公司开发用于执行应用程序
-
-
SOCK_SEQPACKET顺序数据包
-
连接特性:面向连接,类似SOCK_STREAM
-
边界维护:独特之处在于维护记录边界
-
识别方式:接收方可通过MSG_EOR标志确定边界
-
-
SOCK_DCCP数据报拥塞控制
-
协议定位:传输层协议,兼具TCP和UDP特点
-
核心功能:提供不可靠数据报的拥塞控制流
-
设计目标:解决数据报传输中的拥塞控制问题
-
1)套接字API与内核方法
-
内核映射关系:应用层套接字API在内核中对应net/socket.c文件中的系统调用实现
-
调用机制:通过SYSCALL_DEFINE2宏定义系统调用,如socketcall处理所有套接字相关操作
-
参数传递:使用unsigned long数组存储用户空间传递的参数,通过copy_from_user安全拷贝
2)socket.c文件与内核函数定义
-
文件位置:内核源码中位于net/socket.c目录下
-
函数前缀:所有套接字系统调用函数都以sys_开头,如sys_socket、sys_bind等
-
调用流程:通过switch-case结构分发不同的套接字操作请求
-
需阅读源码部分:
3)套接字类型与功能描述
-
SOCK_STREAM:提供可靠的字节流通信,TCP套接字属于此类型
-
SOCK_DGRAM:支持消息交换但不可靠,UDP套接字属于此类型
-
SOCK_RAW:直接访问IP层,支持协议无关的传输层数据收发
-
SOCK_RDM:用于透明进程间通信(TIPC)
-
SOCK_SEQPACKET:面向连接的顺序数据包流,类似SOCK_STREAM
-
SOCK_DCCP:数据报拥塞控制协议,提供不可靠数据报拥塞控制流
4)套接字操作函数
-
socket():创建套接字,类型由参数决定
-
bind():将套接字与本地端口和IP地址关联
-
connect():建立到对等套接字的连接,适用于SOCK_STREAM和SOCK_SEQPACKET类型
-
listen():使套接字能够接收连接请求,不适用于数据报套接字
-
accept():接收套接字连接请求,仅适用于基于连接的套接字类型
-
send()/recv():分别对应消息发送和接收,内核实现为sys_send和sys_recv
-
实现机制:每个应用层API调用对应内核中的sys_前缀函数
-
参数处理:通过a0,a1等寄存器传递参数,进行安全检查后调用具体实现
-
错误处理:返回负值表示错误,如-EINVAL表示无效参数
3. 传输控制协议
1)内核中的套接字结构
-
双结构表示:内核中有两个表示套接字的结构体,分别是socket和sock
-
源码位置:位于include/linux/net.h文件中,从第104行开始定义
-
功能分工:
-
socket:面向用户空间提供接口
-
sock:面向网络层(L3)提供接口
-
2)socket结构的主要成员
-
核心成员:
-
state:套接字状态(如SS_CONNECTED等)
-
type:套接字类型(如SOCK_STREAM等)
-
flags:套接字标志(如SOCK_NOSPACE等)
-
ops:协议特定的套接字操作,包含套接字回调函数的proto_ops结构
-
file:文件反向指针用于垃圾回收,关联的文件结构指针
-
sk:内部网络协议无关的套接字表示
-
wq:多种用途的等待队列
-
源码阅读:
-
3)sock结构的网络层位置
-
协议无关性:sock结构位于网络层,是与协议无关的结构
-
创建关联:创建套接字时会同时创建关联的sock对象
-
示例:在IPv4中,调用inet_create方法时会分配sock对象并关联到对应套接字
4)socket与sock的功能区别
-
socket功能:
-
面向用户空间提供接口
-
由sys_socket()方法创建
-
-
sock功能:
-
面向网络层(L3)提供接口
-
协议无关的结构体
-
包含网络层相关操作和数据
-
5)socket状态及其取值
-
状态枚举:
-
SS_FREE = 0:未分配状态
-
SS_UNCONNECTED:未连接任何套接字
-
SS_CONNECTING:正在连接过程中
-
SS_CONNECTED:已连接到套接字
-
SS_DISCONNECTING:正在断开连接
-
源码阅读:
-
-
典型场景:
-
刚创建的INET套接字状态为SS_UNCONNECTED
-
流套接字成功连接后状态变为SS_CONNECTED
-
6)socket类型及其枚举类型
-
类型定义:
-
SOCK_STREAM:流式套接字
-
SOCK_RAW:原始套接字
-
-
类型特性:
-
使用kmemcheck_bitfield_begin/end宏进行类型检查
-
类型值存储在short类型的变量中
-
7)socket标志与分配方式
-
标志作用:表示套接字的特殊属性和状态
-
分配方式:
-
非系统调用socket分配时直接设置核心标志
-
可通过通用try_open方法进行打开操作
-
8)socket相关对象与文件
-
关联文件:
-
file指针:与套接字关联的文件对象
-
-
关联对象:
-
sk指针:与套接字关联的sock对象
-
提供网络层接口功能
-
9)socket操作与回调函数
-
操作集合:
-
包含connect、listen、sendmsg、recvmsg等回调函数
-
实现用户空间接口的多个库级例程
-
-
协议特定:
-
每种协议需要自定义proto_ops结构
-
不是随意定义的固定结构
-
-
典型回调:
-
sendmsg实现send、sendto、sendmsg等功能
-
recvmsg实现read、recv、recvfrom、recvmsg等功能
-
4. 套接字在网络层中的表示
1)套接字结构
-
核心结构:sock结构体是套接字的网络层表示,位于内核源码的include/net/sock.h文件中
-
源码阅读位置:建议整个结构体内容都有所了解
-
重要成员:
-
sk_common:包含套接字的基础公共成员
-
sk_lock:套接字锁
-
sk_drops:数据包丢弃计数器
-
sk_rcvlowat:接收低水位标记
-
sk_error_queue:错误队列头
-
sk_receive_queue:接收队列头
-
-
实现特点:
-
结构体实现代码相当长,包含套接字的所有网络层属性
-
与socket结构体相关联,通过struct sock *sk指针连接
-
包含数据包队列存储、接收缓冲区大小、各种标志等关键信息
-
2)回调函数
-
核心成员:
-
sk_receive_queue:存储入站数据包的队列
-
sk_write_queue:存储出站数据包的队列
-
sk_rcvbuf:接收缓冲区大小(单位:字节)
-
sk_sndbuf:发送缓冲区大小(单位:字节)
-
sk_protocol:协议标识符(8位)
-
sk_type:套接字类型(16位)
-
-
关键回调:
-
sk_data_ready:通知套接字有新数据到达的回调函数
-
sk_write_space:指出可用内存处理数据传输的回调函数
-
-
其他特性:
-
sk_no_check_tx:禁用发送校验和标志
-
sk_no_check_rx:禁用接收校验和标志
-
4)系统调用socket参数与返回值
-
参数说明:
-
socket_family:地址族(AF_INET/AF_INET6)
-
socket_type:套接字类型(SOCK_STREAM/SOCK_DGRAM/SOCK_RAW)
-
protocol:协议类型(IPPROTO_TCP/IPPROTO_UDP)
-
-
返回值:
-
返回文件描述符sockfd,用于后续套接字操作
-
内核内部调用sys_socket进行处理
-
5)msghdr结构体
-
核心成员:
-
msg_name:目标套接字地址指针,可转换为sockaddr_in结构
-
msg_namelen:地址长度
-
msg_iter:数据块迭代器
-
msg_control:控制信息(辅助数据)指针
-
msg_controllen:控制信息长度
-
msg_flags:接收消息的标志位
-
-
源码阅读位置:
-
使用场景:
-
用于用户空间套接字发送/接收数据
-
通过sendmsg/recvmsg系统调用处理数据传输
-
包含完整的数据块和控制信息
-
用户数据包协议
1. UDP报头内核源码
-
msg_name字段:指向目标套接字地址的指针,可转换为指向结构sockaddr_in的指针
-
msg_namelen字段:表示地址长度
-
msg_control字段:用于存储控制信息(辅助数据)
-
msg_flags字段:接收到的消息标志位
-
cmsg_len字段:数据字节计数,包括头部信息
-
cmsg_level字段:标识原始协议
-
cmsg_type字段:协议特定类型
1)UDP数据包协议
-
source字段:16位源端口号,取值范围1-65535
-
dest字段:16位目的端口号,取值范围1-65535
-
len字段:表示有效负载和UDP报头的总长度,单位为字节
-
check字段:数据包的校验和,用于验证数据完整性
-
源码阅读:
2)UDP初始化操作
-
初始化操作定义对象并使用方法
-
初始化对象:通过定义udp_protocol(net_protocol对象)实现
-
添加方法:使用inet_add_protocol()函数进行注册
-
关键成员:
-
early_demux:设置为udp_v4_early_demux
-
handler:设置为udp_rcv处理函数
-
err_handler:设置为udp_err错误处理函数
-
no_policy:设置为1表示无策略
-
netns_ok:设置为1表示支持网络命名空间
-
源码阅读:
-
-
2. 套接字的网络层表示
-
数据结构:内核中使用struct sock结构体表示套接字
-
接收队列:sk_receive_queue存储入站数据包
-
缓冲区大小:sk_rcvbuf表示接收缓冲区大小(字节),sk_sndbuf表示发送缓冲区大小
-
发送队列:sk_write_queue存储出站数据包
-
协议标识:sk_protocol字段标识协议类型
-
回调函数:sk_data_ready通知新数据到达,sk_write_space指示可用内存
-
-
系统调用:
-
socket()参数:
-
socket_family:AF_INET/AF_INET6
-
socket_type:SOCK_STREAM/SOCK_DGRAM/SOCK_RAW
-
protocol:IPPROTO_TCP/IPPROTO_UDP
-
-
返回值:返回文件描述符sockfd用于后续操作
-
数据传输:通过sendmsg()/recvmsg()处理,使用msghdr对象封装数据
-
3. UDP初始化机制
1)协议注册
-
核心结构:通过struct net_protocol udp_protocol注册
-
处理函数:handler=udp_rcv处理接收数据
-
错误处理:err_handler=udp_err处理错误
-
特性标志:no_policy=1表示无策略检查,netns_ok=1支持网络命名空间
-
-
初始化流程:
-
注册时机:系统启动时通过inet_init_net()初始化
-
端口范围:默认本地端口范围32768-60999
-
统计信息:为CPU分配统计数据结构(ip_statistics等)
-
2)网络层初始化
-
关键函数:inet_init_net()执行初始化
-
端口锁定:使用seqlock_init初始化ip_local_ports锁
-
Ping控制:设置ping_group_range限制ping权限
-
默认参数:
-
TTL值:IPDEFTTL
-
动态地址:sysctl_ip_dynaddr=0
-
早期解复用:对TCP/UDP启用(值=1)
-
-
源码阅读:
-
-
实现细节:
-
多协议支持:通过inet_add_protocol()注册ICMP/UDP/TCP
-
错误处理:失败时输出pr_crit级别日志
-
回调机制:如udp_sendmsg()处理UDP数据发送
-
源码阅读:这个挺重要的,略微重点阅读
-
用户数据包协议内核源码
1)接收L3的UDP数据包
-
方法udp_rcv()介绍
-
主处理程序:udp_rcv()是Linux内核中负责接收来自网络层(L3)的UDP数据包的核心函数
-
调用位置:位于net/ipv4/udp.c文件中,函数签名为int udp_rcv(struct sk_buff *skb)
-
嵌套调用:实际工作通过调用udp4_lib_rcv(skb, &udp_table, IPPROTO_UDP)完成,传入UDP协议表和处理协议类型
-
-
udp_rcv()处理流程
-
初始化阶段:
-
获取socket结构体指针struct sock *sk
-
解析UDP头部struct udphdr *uh获取报文长度ulen
-
获取路由表项struct rtable *rt = skb_rtable(skb)
-
-
数据包验证:
-
检查是否有足够空间存放UDP头部:pskb_may_pull(skb, sizeof(struct udphdr))
-
验证报文长度是否合法:ulen > skb->len时跳转到short_packet处理
-
初始化校验和:udp4_csum_init(skb, uh, proto)
-
-
套接字查找:
-
使用skb_steal_sock(skb)尝试获取关联的socket
-
通过__udp4_lib_lookup_skb()在UDP哈希表中查找匹配的套接字
-
-
-
数据包验证与 处理
-
匹配处理:
-
找到匹配套接字时:调用udp_queue_rcv_skb(sk, skb)将数据包加入接收队列
-
未找到匹配时:检查校验和是否正确,错误则直接丢弃数据包
-
-
异常处理:
-
广播/组播数据包:调用__udp4_lib_mcast_deliver()处理
-
端口不可达情况:发送ICMP"目的不可达"响应,更新SNMP计数器
-
-
资源释放:
-
最终通过kfree_skb()释放SKB缓冲区
-
更新内存统计信息:atomic_sub(size, &sk->sk_rmem_alloc)
-
-
完整流程:
-
入口函数udp_rcv()接收L3数据包
-
调用__udp4_lib_rcv()进行核心处理
-
检查是否为组播数据(是则特殊处理)
-
在UDP哈希表中查找匹配套接字
-
找到匹配则入队,否则校验后发送ICMP响应
-
最终释放数据包资源
-
-
TCP分析
1. TCP报头结构主要成员
-
源端口与目的端口:
-
source:均为16位长度,取值范围1-65535
-
dest:源端口对应应用层写的源端口,目的端口标识目标服务
-
-
序列号与确认号:
-
seq:序列号(seq)为32位,用于数据包排序
-
ack_seq:确认号(ack_seq)为32位,当设置ACK标志时,表示期望收到的下一个数据包序列号
-
-
保留字段:
-
res1:4位长度,必须设置为0,为未来协议扩展保留
-
-
数据偏移量:
-
doff:4位长度,以4字节为单位表示TCP头长度
-
最小值为5(20字节),最大为15(60字节)
-
-
控制标志位:
-
FIN(1位):发送方数据发送完毕,用于关闭连接
-
SYN(1位):用于三次握手建立连接
-
RST(1位):收到非当前连接数据时使用
-
PSH(1位):要求尽快将数据交付用户空间
-
ACK(1位):确认号字段有效
-
URG(1位):紧急指针字段有效
-
ECE(1位):显式拥塞通知,提供网络拥塞反馈
-
CWR(1位):拥塞窗口缩小标志
-
-
窗口大小:
-
window:16位长度,表示接收窗口大小(字节单位)
-
-
校验和:
-
check:包含TCP头和数据的校验值
-
-
紧急指针:
-
urg_ptr:16位长度,仅当URG标志设置时有效
-
表示紧急数据相对于序列号的偏移量
-
-
源码阅读:
TCP初始化操作
-
核心结构体:
-
struct net_protocol:定义传输层协议
-
包含ICMP/IGMP协议处理
-
桥接网络层和传输层的报文接收流程
-
-
源码阅读:
-
-
关键成员:
-
handler:数据包接收处理函数指针
-
err_handler:错误报文处理函数
-
no_policy:安全策略标志
-
-
初始化流程:
1.定义tcp_protocol对象
2.通过inet_add_protocol()注册协议
3.内核根据协议类型(TCP/UDP/ICMP)调用对应处理函数
4.返回值<0表示注册失败
-
源码位置:
-
位于net/ipv4/目录下
-
TCP实现文件为tcp_ipv4.c
-
-
错误处理:
-
当inet_add_protocol()返回错误时
-
内核会输出协议添加失败信息
-
TCP定时器
1)定时器实现位置
-
文件路径: 位于Linux内核的net/ipv4/tcptimer.c文件中
-
查找方法: 通过内核源码目录结构可快速定位,IPv4相关实现都在net/ipv4/目录下
2)重传定时器
-
核心功能: 负责在指定时间内未得到确认的数据包重传
-
触发条件:
-
数据包丢失或损坏
-
每次数据段发送后自动启动
-
-
终止机制: 定时器到期后若仍未收到确认则取消定时器
3)延迟确认定时器
-
作用原理: 推迟发送确认数据包
-
适用场景: 当TCP收到需要确认但无需立即确认的数据时启用
4)存活定时器
-
检测功能: 检查连接是否已断开
-
应用场景: 连接长时间空闲时检测对方状态
-
处理机制: 通过tcpsendactivereset()函数重置连接
5)零窗口探测定时器
-
别名: 持续定时器(persist timer)
-
工作流程:
-
接收方缓冲区满时通告零窗口
-
发送方停止发送数据
-
若包含新窗口大小的数据段丢失,定时器定期探测窗口状态
-
-
终止条件: 当探测到接收方窗口大小不为零时停止
TCP初始化
1)初始化入口
-
用户空间调用: 创建SOCK_STREAM类型套接字
-
内核处理函数:
-
系统调用入口为sys_socket()
-
实际回调函数为tcp_v4_init_sock()(IPv6对应tcp_v6_init_sock)
-
2)主要初始化任务
-
状态设置
-
初始状态: 设置为TCP_CLOSE状态
-
-
定时器初始化
-
调用函数:tcp_init_xmit_timers()
-
作用: 初始化所有TCP定时器模块
-
-
缓冲区设置
-
发送缓冲区:sk−>sk_sndbuf默认16,384字节
-
接收缓冲区:sk−>sk_rcvbuf默认87,380字节
-
-
队列初始化
-
无序队列: 处理非常规数据包
-
有序队列: 维护正常数据流顺序
-
-
参数初始化
-
包含内容: TCP头部各字段默认值初始化
-
典型值: 窗口大小初始化为10等基础参数
-
-
源码阅读:
TCP的连接和拆除
1)状态转换机制
-
监听状态: 调用listen()后进入TCP_LISTEN状态
-
状态标识: 通过sk−>skstate成员变量表示
2)三次握手过程
-
第一次握手
-
客户端动作: 发送SYN请求
-
状态转换: 进入TCP_SYN_SENT状态
-
-
第二次握手
-
服务端响应:
-
创建TCP_SYN_RECV状态套接字
-
返回SYN_ACK应答
-
-
-
第三次握手
-
客户端确认:
-
收到SYN_ACK后进入TCP_ESTABLISHED状态
-
发送最终ACK确认
-
-
-
连接建立
-
服务端最终状态: 收到ACK后改为TCP_ESTABLISHED状态
-
数据传输: 连接建立完成后即可开始数据传输
-
3)数据包接收处理
-
核心函数:tcp_v4_rcv()
-
源码阅读:
-
处理流程:
-
完整性检查(包类型、长度等)
-
初始化工作
-
调用__inet_lookup_skb()查找匹配套接字
-
-
异常处理: 检查失败时直接丢弃数据包
初始化相关问题
1. skb分析
1)数据包丢弃条件
-
非本地数据包处理:当数据包不是发送或发往本地的包时,系统会直接丢弃(discard)该数据包
-
掩码执行位置:丢弃操作通过1612行的掩码进行处理
2)TCP头部验证
-
长度校验:通过PS x/PS KB脉破函数检查包长是否大于TCP头长度
-
头部提取:使用th指针取得TCP首部,通过1612行掩码处理
-
偏移量验证:检查TCP首部长度与偏移量字段(offset)是否匹配
2. 接收TCP数据包过程
1)数据包查找流程
-
socket查找:通过inet_look_up函数查找匹配的socket,若找不到则丢弃数据包
-
状态检查:检查socket是否处于半关闭状态(half-closed)
-
规则验证:依次检查IPsec规则、MD5哈希校验、BPF校验规则
2)用户态处理
-
进程锁定:检查是否有用户态进程对socket进行锁定
-
状态保护:当socket被锁定时,其状态不可更改
-
队列处理:被锁定的数据包会进入后备处理队列,相关进程进入套接字后备等待队列
3. 发送TCP数据包过程
1)发送初始化
-
系统调用:通过send/sendto/sendmsg等系统调用触发
-
核心函数:最终由tcp_sendmsg函数处理,将用户空间数据复制到内核空间
-
状态检查:验证socket是否处于ESTABLISHED或CLOSE_WAIT状态
2)数据复制流程
-
内存分配:分配SKB缓冲区对象存储用户数据
-
循环拷贝:通过while循环控制所有用户数据块到内核空间的拷贝
-
队列管理:操作发送队列(双向链表)的尾节点进行数据添加
3)错误处理机制
-
do_error处理:当部分数据已复制时,仍发送已复制的数据
-
wait_for处理:
-
snd_buff:发送队列数据达到缓冲区上限时触发
-
memory:系统内存不足时触发
-
-
超时处理:内存分配超时跳转到错误处理流程
4)标志位说明
-
MSG_MORE:表示本次发送没有后续数据
-
TCP_NODELAY:禁用Nagle算法立即发送数据
-
SK_MEM_QUERY:内存配额检查标志
总结
1. 套接字分析
-
核心概念:套接字(Socket)是网络通信的基础,提供进程间通信的端点
-
工作机制:通过IP地址和端口号的组合实现不同主机间的数据传输
-
主要功能:建立连接、数据传输、连接释放等网络通信全过程管理
2. 用户数据包协议(UDP)
-
协议特点:无连接、不可靠但高效的传输协议
-
数据单元:以独立的数据包形式传输,每个包包含完整的目标地址信息
-
适用场景:适用于实时性要求高但允许少量丢包的应用,如视频会议、在线游戏等
3. 传输控制协议(TCP)
-
协议特点:面向连接、可靠传输的协议
-
工作机制:通过三次握手建立连接,四次挥手释放连接
-
可靠性保障:采用确认应答、超时重传、流量控制等机制确保数据完整有序传输
-
适用场景:适用于要求数据完整性的应用,如文件传输、网页浏览等