TCP/IP 协议驱动分析:用户态 Winsock → TDI → DeviceIoControl 调用链
目录
- 概述
- 完整调用链架构
- [用户态 Winsock 层](#用户态 Winsock 层)
- [WSP 服务提供者架构](#WSP 服务提供者架构)
- [内核态 AFD.sys 辅助功能驱动](#内核态 AFD.sys 辅助功能驱动)
- [TDI 传输驱动接口](#TDI 传输驱动接口)
- [DeviceIoControl 内核机制](#DeviceIoControl 内核机制)
- 典型操作调用链分析
- 工作流程图
- 数据流图
- 关键函数索引
1. 概述
本部分分析从用户态 Winsock API 到 内核态 TCP/IP 协议栈 的完整调用路径。一个应用程序调用 send()/recv()/connect() 等网络函数时,数据请求会依次穿过以下层次:
应用程序 (App.exe)
│
▼
Winsock 用户态库 (ws2_32.dll) ← 用户态 API 入口
│
▼
WSP 服务提供者 (AFD Provider) ← 通过 DeviceIoControl 进入内核
│
▼
AFD.sys (辅助功能驱动) ← 内核态 socket 语义实现
│
▼
TDI (传输驱动接口) ← 内核态网络接口标准
│
▼
tcpip.sys (TCP/IP 协议驱动) ← 真正的协议实现
│
▼
NDIS 库 (ndis.sys) → 网卡驱动
三个关键转换点:
| 转换点 | 内容 |
|---|---|
| 用户态 → 内核态 | Winsock 通过 DeviceIoControl() 对 \Device\Afd 设备发起 IOCTL,产生 IRP_MJ_DEVICE_CONTROL IRP |
| AFD → TDI | AFD 将 IOCTL_AFD_* 翻译为 TDI 操作(TDI_CONNECT、TDI_SEND 等),以 IRP_MJ_INTERNAL_DEVICE_CONTROL IRP 发给 \Device\Tcp/\Device\Udp |
| TDI → 协议栈 | tcpip.sys 的 TiDispatchInternal 分发处理 TDI 请求 |
2. 完整调用链架构
2.1 分层总览
┌─────────────────────────────────────────────────────────────────┐
│ 用户模式 (Ring 3) │
│ │
│ ┌──────────────┐ ┌──────────────────┐ ┌─────────────────┐ │
│ │ 应用程序 │ │ ws2_32.dll │ │ 服务提供者 DLL │ │
│ │ (App.exe) │──▶│ (Winsock API) │──▶│ (AFD Provider) │ │
│ │ socket() │ │ WSP 分发/调度 │ │ msafd/自实现 │ │
│ │ send/recv │ │ 句柄→WSSOCKET │ │ 组织 IOCTL 缓冲 │ │
│ └──────────────┘ └──────────────────┘ └────────┬────────┘ │
│ │ │
│ NtDeviceIoControlFile/ │
│ DeviceIoControl │
└──────────────────────────────────────────────────────┬──────────┘
│ IRP_MJ_DEVICE_CONTROL
▼
┌─────────────────────────────────────────────────────────────────┐
│ 内核模式 (Ring 0) │
│ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ AFD.sys --- \Device\Afd (辅助功能驱动) │ │
│ │ │ │
│ │ AfdDispatch() ──switch(IOCTL_AFD_*)──▶ 各处理函数 │ │
│ │ │ IOCTL_AFD_CONNECT → AfdStreamSocketConnect │ │
│ │ │ IOCTL_AFD_SEND → AfdConnectedSocketWriteData │ │
│ │ │ IOCTL_AFD_RECV → AfdConnectedSocketReadData │ │
│ │ │ IOCTL_AFD_BIND → AfdBindSocket │ │
│ │ │ IOCTL_AFD_START_LISTEN → AfdListenSocket │ │
│ │ │ IOCTL_AFD_SELECT → AfdSelect │ │
│ │ │ ... (40+ 个 IOCTL) │ │
│ │ │ │ │
│ │ │ AFD_FCB (FileObject->FsContext) = 每个 socket 的状态 │ │
│ │ └──────────────────────────┬────────────────────────────┘ │
│ │ │ tdihelpers (TdiXxx) │
│ │ ▼ │
│ │ ┌───────────────────────────────────────────────────────┐ │
│ │ │ TDI 调用 → IRP_MJ_INTERNAL_DEVICE_CONTROL │ │
│ │ │ TdiConnect → \Device\Tcp (TDI_CONNECT) │ │
│ │ │ TdiSend → \Device\Tcp (TDI_SEND) │ │
│ │ │ TdiReceive → \Device\Tcp (TDI_RECEIVE) │ │
│ │ │ TdiOpenAddressFile → \Device\Udp (带 EA 的 CREATE) │ │
│ │ └──────────────────────────┬────────────────────────────┘ │
│ │ ▼ │
│ │ ┌───────────────────────────────────────────────────────┐ │
│ │ │ tcpip.sys --- \Device\Tcp / \Device\Udp │ │
│ │ │ TiDispatchInternal() ──switch(MinorFunction)──▶ │ │
│ │ │ TDI_CONNECT → DispTdiConnect │ │
│ │ │ TDI_SEND → DispTdiSend │ │
│ │ │ TDI_RECEIVE → DispTdiReceive │ │
│ │ │ TDI_QUERY_INFORMATION→ DispTdiQueryInformation │ │
│ │ └──────────────────────────┬────────────────────────────┘ │
│ │ ▼ │
│ │ TCP/UDP/IP/ARP/路由实现 │
│ │ ▼ │
│ │ NDIS 协议接口 (lan.c) │
│ └───────────────────────────────────────────────────────────┘ │
│ │ NdisSend / NdisRequest │
│ ▼ │
│ ndis.sys → 微型端口 → 硬件 │
└─────────────────────────────────────────────────────────────────┘
2.2 两种 IOCTL 路径的区分
调用链中存在两级 IOCTL,容易混淆:
| 级别 | 发起者 | 接收者 | IRP 类型 | 设备对象 |
|---|---|---|---|---|
| 用户态→AFD | 用户态 WSP 提供者 | AFD.sys | IRP_MJ_DEVICE_CONTROL |
\Device\Afd |
| AFD→传输驱动 | AFD.sys (内核) | tcpip.sys | IRP_MJ_INTERNAL_DEVICE_CONTROL |
\Device\Tcp/\Device\Udp |
区别要点:
IRP_MJ_DEVICE_CONTROL:可从用户态通过DeviceIoControl直接发起IRP_MJ_INTERNAL_DEVICE_CONTROL:只能在内核态通过IoBuildDeviceIoControlRequest/TdiBuildInternalDeviceControlIrp发起,用于驱动间通信
3. 用户态 Winsock 层
3.1 ws2_32.dll 的架构
ReactOS 的 ws2_32.dll 位于 d:\reactos\dll\win32\ws2_32\,是一个 Winsock 2 API 实现 ,采用 WSP (Winsock Service Provider) 架构:
ws2_32.dll
├── startup.c --- WSAStartup/WSACleanup 初始化
├── socklife.c --- socket/closesocket 生命周期管理
├── sockctrl.c --- connect/listen/getsockopt/setsockopt/ioctlsocket
├── send.c / recv.c --- send/recv/sendto/recvfrom
├── select.c --- select/WSAPoll
├── event.c --- WSAEventSelect
├── dprovide.c --- LoadLibrary + WSPStartup 加载服务提供者
├── dcatalog.c --- Winsock 服务目录管理
├── addrinfo.c --- getaddrinfo 等名称解析
└── ...
3.2 关键机制:WSSOCKET 上下文
每个 socket 句柄在 ws2_32 中对应一个 WSSOCKET 上下文结构,包含:
c
typedef struct _WSSOCKET {
SOCKET Sock; // 原生的 socket 句柄
PVOID Provider; // 指向服务提供者的指针
BOOL NonBlocking; // 非阻塞标记
...
} WSSOCKET, *PWSSOCKET;
当应用调用 connect() 时,ws2_32 通过 WsSockGetSocket(s) 查找句柄对应的 WSSOCKET,然后调用其绑定的服务提供者:
c
Status = Socket->Provider->Service.lpWSPConnect( // WSP 调用
s, name, namelen, NULL, NULL, NULL, NULL, &ErrorCode);
所有 Winsock 2 API 都遵循这个模式:ws2_32 只做参数验证、句柄查找和错误处理,真正的工作委托给服务提供者。
3.3 WSAStartup 初始化流程
WSAStartup() → WsApiProlog()
│
├─ 检查进程内是否已初始化
├─ 枚举 Winsock 服务目录 (WSCEnumProtocols)
├─ 按优先级选择默认服务提供者
└─ 返回 WSADATA (版本 2.2)
4. WSP 服务提供者架构
4.1 服务提供者加载
在 dprovide.c(file:///d:/reactos/dll/win32/ws2_32/src/dprovide.c) 中,LoadTransportProviders 负责加载服务提供者:
LoadTransportProviders()
│
├─ WSCEnumProtocols() 枚举已安装的协议目录
├─ 对每个提供者:
│ ├─ LoadLibrary(ProviderDll) ← 加载提供者 DLL
│ ├─ GetProcAddress(..., "WSPStartup") ← 获取入口
│ └─ WSPStartup() ← 初始化提供者
├─ 建立 SOCKET → Provider 映射表
└─ 返回提供者列表
4.2 WSP 函数表
服务提供者通过 WSPStartup 导出 WSPUPCALLTABLE 和自身的 WSPPROC_TABLE。ws2_32 调用 lpWSPConnect、lpWSPSend、lpWSPRecv 等函数指针。
4.3 AFD 服务提供者的职责
在标准 Windows 架构中,msafd.dll(在 ReactOS 中为 mswsock.dll 的 AFD 提供者)是默认的 Winsock 服务提供者,其核心职责是:
- 维护内核句柄 :
socket()时通过CreateFile("\Device\Afd")获取内核文件句柄 - 组织 IOCTL 缓冲 :将 WSP 调用的参数(sockaddr、WSABUF 数组、标志)填入
AFD_*结构 - 发起 DeviceIoControl :调用
NtDeviceIoControlFile将请求送进内核 - 处理完成事件:通过事件对象接收内核完成通知
ReactOS 现状 :当前 mswsock.dll 的 AFD 提供者部分仍是 stub,AFD.sys 本身已实现了完整的 IOCTL 接口,等待用户态提供者对接。
5. 内核态 AFD.sys 辅助功能驱动
5.1 设备与分发
AFD.sys 位于 d:\reactos\drivers\network\afd\,源码入口在 main.c(file:///d:/reactos/drivers/network/afd/afd/main.c#L1318-L1359)。
DriverEntry 关键动作:
c
UNICODE_STRING wstrDeviceName = RTL_CONSTANT_STRING(L"\\Device\\Afd");
...
// 注册所有 MajorFunction 到统一分发
DriverObject->MajorFunction[IRP_MJ_CLOSE] = AfdDispatch;
DriverObject->MajorFunction[IRP_MJ_CREATE] = AfdDispatch;
DriverObject->MajorFunction[IRP_MJ_CLEANUP] = AfdDispatch;
DriverObject->MajorFunction[IRP_MJ_WRITE] = AfdDispatch;
DriverObject->MajorFunction[IRP_MJ_READ] = AfdDispatch;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = AfdDispatch;
...
// 创建设备对象 (FILE_DEVICE_NAMED_PIPE 类型)
IoCreateDevice(DriverObject, sizeof(AFD_DEVICE_EXTENSION),
&wstrDeviceName, FILE_DEVICE_NAMED_PIPE,
0, FALSE, &DeviceObject);
AFD_DEVICE_EXTENSION 结构:
c
typedef struct _AFD_DEVICE_EXTENSION {
PDEVICE_OBJECT DeviceObject; // 指向本设备对象
LIST_ENTRY Polls; // 活跃 select/poll 请求链表
KSPIN_LOCK Lock; // 保护 Polls 链表
} AFD_DEVICE_EXTENSION, *PAFD_DEVICE_EXTENSION;
5.2 AFDDispatch 统一分发
所有 IRP 都由 AfdDispatch(file:///d:/reactos/drivers/network/afd/afd/main.c#L906-L1094) 处理:
IRP_MJ_CREATE → AfdCreateSocket (创建 socket 上下文)
IRP_MJ_CLOSE → AfdCloseSocket (关闭 socket)
IRP_MJ_CLEANUP → AfdCleanupSocket (清理挂起的 IRP)
IRP_MJ_WRITE → AfdConnectedSocketWriteData
IRP_MJ_READ → AfdConnectedSocketReadData
IRP_MJ_DEVICE_CONTROL → switch(IoControlCode)
IRP_MJ_DEVICE_CONTROL 的 IOCTL 分发表(核心):
| IOCTL 代码 | 处理函数 | 对应 Winsock API |
|---|---|---|
IOCTL_AFD_BIND |
AfdBindSocket |
bind() |
IOCTL_AFD_CONNECT |
AfdStreamSocketConnect |
connect() (阻塞) |
IOCTL_AFD_SUPER_CONNECT |
AfdStreamSocketSuperConnect |
ConnectEx() |
IOCTL_AFD_START_LISTEN |
AfdListenSocket |
listen() |
IOCTL_AFD_WAIT_FOR_LISTEN |
AfdWaitForListen |
accept() 前等待 |
IOCTL_AFD_ACCEPT |
AfdAccept |
accept() |
IOCTL_AFD_RECV |
AfdConnectedSocketReadData |
recv() |
IOCTL_AFD_RECV_DATAGRAM |
AfdPacketSocketReadData |
recvfrom() |
IOCTL_AFD_SEND |
AfdConnectedSocketWriteData |
send() |
IOCTL_AFD_SEND_DATAGRAM |
AfdPacketSocketWriteData |
sendto() |
IOCTL_AFD_SELECT |
AfdSelect |
select() |
IOCTL_AFD_EVENT_SELECT |
AfdEventSelect |
WSAEventSelect() |
IOCTL_AFD_ENUM_NETWORK_EVENTS |
AfdEnumEvents |
WSAEnumNetworkEvents() |
IOCTL_AFD_DISCONNECT |
AfdDisconnect |
shutdown()/closesocket() |
IOCTL_AFD_GET_INFO |
AfdGetInfo |
getsockopt() 部分 |
IOCTL_AFD_SET_INFO |
AfdSetInfo |
setsockopt() 部分 |
IOCTL_AFD_GET_SOCK_NAME |
AfdGetSockName |
getsockname() |
IOCTL_AFD_GET_PEER_NAME |
AfdGetPeerName |
getpeername() |
IOCTL_AFD_GET_TDI_HANDLES |
AfdGetTdiHandles |
诊断工具 (netstat) |
5.3 AFD_FCB:每个 socket 的内核状态
AFD 使用 FCB (File Control Block) 保存每个 socket 的完整状态,存放在 FileObject->FsContext 中:
c
typedef struct _AFD_FCB {
BOOLEAN Locked, Critical, NonBlocking, OobInline;
BOOLEAN TdiReceiveClosed, SendClosed;
...
AFD_TDI_OBJECT AddressFile; // TDI 地址文件对象 (句柄+文件对象)
AFD_TDI_OBJECT Connection; // TDI 连接端点对象
UNICODE_STRING TdiDeviceName; // 传输设备名 (如 \Device\Tcp)
AFD_TDI_OBJECT AcceptQueue; // 接受队列
AFD_IN_FLIGHT_REQUEST ConnectIrp; // 进行中的 TDI_CONNECT IRP
AFD_IN_FLIGHT_REQUEST ReceiveIrp; // 进行中的 TDI_RECEIVE IRP
AFD_IN_FLIGHT_REQUEST SendIrp; // 进行中的 TDI_SEND IRP
PIRP PendingIrpList[MAX_FUNCTIONS]; // 各功能挂起的用户 IRP
AFD_SOCKET_STATE SharedData; // 共享 socket 状态
PVOID ConnectCallInfo; // TDI 连接调用信息
PVOID ConnectReturnInfo; // TDI 连接返回信息
...
} AFD_FCB, *PAFD_FCB;
socket 状态机(AFD 视角):
SOCKET_STATE_CREATED → SOCKET_STATE_BOUND → SOCKET_STATE_CONNECTING → SOCKET_STATE_CONNECTED
↘ SOCKET_STATE_LISTENING
SOCKET_STATE_CLOSED (任何状态的终结)
5.4 挂起 IRP 管理
AFD 实现了标准的异步 IRP 管理模式:
QueueUserModeIrp(FCB, Irp, Function)
│
├─ 获取 socket 状态锁
├─ 将 IRP 挂入 FCB->PendingIrpList[Function] 链表
└─ 返回 STATUS_PENDING
// 当 TDI 操作完成时:
UnlockAndMaybeComplete(FCB, Status, Irp, Count)
├─ 从 PendingIrpList 移除 IRP
├─ 填充 IoStatus
└─ IoCompleteRequest(Irp, IO_NETWORK_INCREMENT)
AfdCancelHandler 实现了 IRP 取消逻辑,支持取消挂起的 recv/send/connect/select 等操作。
6. TDI 传输驱动接口
6.1 TDI 定义
TDI (Transport Driver Interface) 定义在 tdikrnl.h(file:///d:/reactos/sdk/include/ddk/tdikrnl.h),是 Windows 传输驱动(TCP/IP、NetBEUI 等)的统一接口标准。
TDI 设备对象(由 tcpip.sys 创建):
| 设备 | 用途 | 打开方式 |
|---|---|---|
\Device\Tcp |
TCP 传输 | CREATE + EA(地址或连接上下文) |
\Device\Udp |
UDP 传输 | CREATE + EA(地址文件) |
\Device\Ip |
原始 IP | CREATE + EA |
\Device\RawIp |
Raw IP | CREATE + EA(协议号) |
6.2 TDI 文件对象类型
TDI 定义了三种文件对象,通过 EA (Extended Attribute) 在 ZwCreateFile/IRP_MJ_CREATE 时创建:
| 文件对象 | EA 名称 | 用途 |
|---|---|---|
| 地址文件 | TdiTransportAddress |
绑定本地地址和端口 |
| 连接端点 | TdiConnectionContext |
TCP 连接对象 |
| 控制通道 | 无 EA | 管理/查询操作 |
6.3 TDI 操作(IRP Minor Function)
TDI 操作以内核 IRP 的 MinorFunction 形式发送:
c
#define TDI_CONNECT 0x0A
#define TDI_LISTEN 0x0B
#define TDI_ACCEPT 0x0C
#define TDI_DISCONNECT 0x0D
#define TDI_SEND 0x0E
#define TDI_RECEIVE 0x0F
#define TDI_SEND_DATAGRAM 0x10
#define TDI_RECEIVE_DATAGRAM 0x11
#define TDI_SET_EVENT_HANDLER 0x12
#define TDI_QUERY_INFORMATION 0x13
#define TDI_SET_INFORMATION 0x14
#define TDI_ASSOCIATE_ADDRESS 0x17
#define TDI_DISASSOCIATE_ADDRESS 0x18
#define TDI_ACTION 0x19
6.4 tdihelpers 库
AFD 通过 tdihelpers 库(d:\reactos\drivers\network\tdihelpers\)构建 TDI 请求:
TdiOpenAddressFile → 通过 ZwCreateFile 打开地址文件 (带 TdiTransportAddress EA)
TdiOpenConnectionEndpointFile → 通过 ZwCreateFile 打开连接端点 (带 TdiConnectionContext EA)
TdiConnect → 构建 TDI_CONNECT 内部 IOCTL IRP 并 IoCallDriver
TdiSend / TdiReceive → 构建 TDI_SEND / TDI_RECEIVE IRP
TdiAssociateAddressFile → 关联地址文件和连接端点
TdiQueryInformation → 查询传输信息 (MTU、最大数据报等)
TdiBuildConnectionInfo → 构造 TDI_CONNECTION_INFORMATION
核心函数 TdiConnect(file:///d:/reactos/drivers/network/tdihelpers/tdi.c#L311-L380):
c
NTSTATUS TdiConnect(PIRP *Irp, PFILE_OBJECT ConnectionObject, ...)
{
DeviceObject = IoGetRelatedDeviceObject(ConnectionObject);
// 构建 TDI_CONNECT 内部设备控制 IRP
*Irp = TdiBuildInternalDeviceControlIrp(TDI_CONNECT,
DeviceObject,
ConnectionObject, ...);
// 调用传输驱动
return IoCallDriver(DeviceObject, *Irp);
}
IoCallDriver 链 :IoCallDriver 最终调用传输驱动的 MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL],即 tcpip.sys 的 TiDispatchInternal。
7. DeviceIoControl 内核机制
7.1 用户态入口
用户态: DeviceIoControl(hSocket, IOCTL_AFD_XXX, ...)
│
▼
ntdll.dll: NtDeviceIoControlFile(hDevice, event, apc, apcCtx,
&iosb, IoControlCode, ...)
│
▼ (系统调用 → 内核)
7.2 内核 IRP 构造
内核的 IopXxxControlFile 构造 IRP_MJ_DEVICE_CONTROL IRP:
IRP (IRP_MJ_DEVICE_CONTROL)
├── IrpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL
├── IrpSp->Parameters.DeviceIoControl:
│ ├── IoControlCode ← IOCTL_AFD_XXX
│ ├── InputBufferLength
│ ├── OutputBufferLength
│ ├── Type3InputBuffer ← 用户缓冲区 (METHOD_NEITHER)
│ └── (SystemBuffer 指向 MDL/系统缓冲)
├── Irp->AssociatedIrp.SystemBuffer
├── Irp->MdlAddress ← 输出缓冲区 MDL
└── Irp->UserBuffer ← 用户输出缓冲区
缓冲方法:
METHOD_BUFFERED:系统缓冲 + MDL 映射,适合小数据量METHOD_IN_DIRECT/METHOD_OUT_DIRECT:MDL 直连用户内存METHOD_NEITHER:Type3InputBuffer直接引用用户地址,AFD 使用此方式并自己锁定内存
7.3 内核 I/O 管理器调用链
IopXxxControlFile (ntoskrnl)
│
├─ IoBuildDeviceIoControlRequest 或复用 IopAllocateIrp
├─ 设置 IRP 栈位置 (IopAllocateIrp + IoSetNextIrpStackLocation)
├─ IoCallDriver(DeviceObject, Irp)
│ └─ IopfCallDriver → 设备对象栈上的驱动分发
│ └─ AFD: AfdDispatch(DeviceObject, Irp) ← \Device\Afd
│ └─ IRP_MJ_DEVICE_CONTROL → switch(IOCTL_AFD_*)
├─ STATUS_PENDING → 返回给用户态,等待异步完成
└─ 完成后: IoCompleteRequest → 唤醒等待的调用者
8. 典型操作调用链分析
8.1 socket() 创建
socket(AF_INET, SOCK_STREAM, 0)
│
▼
ws2_32: socket() → WsSockGetSocket → 调用 lpWSPSocket
│
▼
AFD 提供者: CreateFile("\Device\Afd") ← 内核句柄
│
▼
内核: IRP_MJ_CREATE → AfdDispatch → AfdCreateSocket
│
├─ 分配 AFD_FCB
├─ FileObject->FsContext = FCB
├─ 初始化 socket 状态机 (SOCKET_STATE_CREATED)
└─ 返回句柄
8.2 bind() 绑定
bind(sockfd, sockaddr, len)
│
▼
ws2_32 → lpWSPBind
│
▼
AFD 提供者: DeviceIoControl(handle, IOCTL_AFD_BIND, ...)
│
▼
AFD: AfdBindSocket
│
├─ 锁定用户 sockaddr 缓冲区
├─ TdiOpenAddressFile(&TdiDeviceName, Address) ← ZwCreateFile \Device\Tcp
│ └─ 构建带 TdiTransportAddress EA 的 CREATE IRP
│ └─ tcpip.sys: TiCreateFileObject → FileOpenAddress
├─ 保存 TDI 地址文件句柄到 FCB->AddressFile
└─ 状态 → SOCKET_STATE_BOUND
8.3 connect() 建立连接
connect(sockfd, sockaddr, len)
│
▼
ws2_32: connect() → Socket->Provider->Service.lpWSPConnect
│ (sockctrl.c 中实现对 WSAEHOSTUNREACH 的重试/自动拨号)
│
▼
AFD 提供者: DeviceIoControl(handle, IOCTL_AFD_CONNECT, ...)
│
▼
AFD: AfdStreamSocketConnect
│
├─ 解析远程地址 → TdiBuildConnectionInfo (FCB->ConnectCallInfo)
├─ 如果连接端点未打开:
│ └─ TdiOpenConnectionEndpointFile(&TdiDeviceName) ← ZwCreateFile \Device\Tcp (TdiConnectionContext EA)
│ └─ TdiAssociateAddressFile(地址文件, 连接对象)
├─ QueueUserModeIrp(FCB, Irp, FUNCTION_CONNECT) ← 挂起用户 IRP
├─ TdiConnect(&ConnectIrp, Connection.Object, ...)
│ └─ TdiBuildInternalDeviceControlIrp(TDI_CONNECT) → IoCallDriver
│ └─ tcpip.sys: TiDispatchInternal → DispTdiConnect → TCPConnect
│ └─ 分配本地端口 → 发送 SYN → 等待 SYN+ACK
├─ (异步) TCP 连接完成 → TDI 完成例程 → AFD 完成回调
│ └─ UnlockAndMaybeComplete(FCB, STATUS_SUCCESS, Irp, 0)
└─ IoCompleteRequest → 用户态 connect() 返回成功
8.4 send() 发送数据
send(sockfd, buf, len, flags)
│
▼
ws2_32 → lpWSPSend (send.c)
│
▼
AFD 提供者: DeviceIoControl(handle, IOCTL_AFD_SEND, ...)
│ (输入: AFD_SEND_INFO { BufferArray, BufferCount, ... })
│
▼
AFD: AfdConnectedSocketWriteData
│
├─ LockRequest: 锁定用户缓冲区 (MmProbeAndLockPages / AFD_MAPBUF)
├─ 构造 NDIS 数据包或直接使用 MDL
├─ 检查发送缓冲区空间 (FCB->Send.Window)
├─ 有空间 → TdiSend(&SendIrp, Connection.Object, ...)
│ └─ TdiBuildInternalDeviceControlIrp(TDI_SEND) → IoCallDriver
│ └─ tcpip.sys: DispTdiSend → TCPSendData
│ └─ TCP 分段 → IP → 邻居 → LAN → NdisSend
├─ 无空间 → 挂起 IRP,等待 TCP 窗口更新
├─ 发送完成 → UnlockAndMaybeComplete(FCB, STATUS_SUCCESS, Irp, BytesSent)
└─ IoCompleteRequest → 用户态 send() 返回
8.5 recv() 接收数据
recv(sockfd, buf, len, flags)
│
▼
ws2_32 → lpWSPRecv (recv.c)
│
▼
AFD 提供者: DeviceIoControl(handle, IOCTL_AFD_RECV, ...)
│
▼
AFD: AfdConnectedSocketReadData
│
├─ 锁定接收缓冲区 (MDL)
├─ TdiReceive(&ReceiveIrp, Connection.Object, TDI_RECEIVE_NORMAL, ...)
│ └─ TdiBuildInternalDeviceControlIrp(TDI_RECEIVE) → IoCallDriver
│ └─ tcpip.sys: DispTdiReceive → TCPReceiveData
│ └─ 数据到达时 HandleSignalledConnection → 拷贝到用户 MDL
│ └─ DispDataRequestComplete → 完成 TDI IRP
├─ AFD 完成例程 → UnlockAndMaybeComplete(FCB, STATUS_SUCCESS, Irp, BytesRecv)
└─ IoCompleteRequest → 用户态 recv() 返回
注意 :recv 还有事件驱动路径------若上层注册了 TDI_EVENT_RECEIVE 事件处理器(通过 TDI_SET_EVENT_HANDLER),则数据到达时直接调用事件回调而不是完成挂起的 IRP。
8.6 listen()/accept() 监听接受
listen(sockfd, backlog)
→ IOCTL_AFD_START_LISTEN → AfdListenSocket
→ TdiAssociateAddressFile + 状态 → SOCKET_STATE_LISTENING
accept(sockfd, addr, len)
→ IOCTL_AFD_WAIT_FOR_LISTEN → AfdWaitForListen (等待新连接事件)
→ IOCTL_AFD_ACCEPT → AfdAccept
→ 为新连接分配新 AFD_FCB
→ TdiAccept / 从 TDI 事件队列取连接
8.7 select() 多路复用
select(nfds, readfds, writefds, exceptfds, timeout)
│
▼
ws2_32 → lpWSPSelect (select.c)
│
▼
AFD 提供者: DeviceIoControl(handle, IOCTL_AFD_SELECT, ...)
│ (输入: AFD_POLL_INFO { Handles[], ... })
│
▼
AFD: AfdSelect
│
├─ 为每个 socket 句柄创建 AFD_ACTIVE_POLL
├─ 将 Poll 加入 DeviceExt->Polls 全局链表
├─ 对每个 socket: TdiSetEventHandler / 查询可读可写状态
├─ 任一 socket 就绪 → 完成 IRP
├─ 超时 (AfdSelectTimeout) → 完成 IRP (无事件)
└─ 返回事件掩码
9. 工作流程图
9.1 socket 生命周期总览
#mermaid-svg-6flFdndYIB0KcMWv{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-6flFdndYIB0KcMWv .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-6flFdndYIB0KcMWv .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-6flFdndYIB0KcMWv .error-icon{fill:#552222;}#mermaid-svg-6flFdndYIB0KcMWv .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6flFdndYIB0KcMWv .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-6flFdndYIB0KcMWv .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6flFdndYIB0KcMWv .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6flFdndYIB0KcMWv .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-6flFdndYIB0KcMWv .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6flFdndYIB0KcMWv .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6flFdndYIB0KcMWv .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6flFdndYIB0KcMWv .marker.cross{stroke:#333333;}#mermaid-svg-6flFdndYIB0KcMWv svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6flFdndYIB0KcMWv p{margin:0;}#mermaid-svg-6flFdndYIB0KcMWv .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-6flFdndYIB0KcMWv .cluster-label text{fill:#333;}#mermaid-svg-6flFdndYIB0KcMWv .cluster-label span{color:#333;}#mermaid-svg-6flFdndYIB0KcMWv .cluster-label span p{background-color:transparent;}#mermaid-svg-6flFdndYIB0KcMWv .label text,#mermaid-svg-6flFdndYIB0KcMWv span{fill:#333;color:#333;}#mermaid-svg-6flFdndYIB0KcMWv .node rect,#mermaid-svg-6flFdndYIB0KcMWv .node circle,#mermaid-svg-6flFdndYIB0KcMWv .node ellipse,#mermaid-svg-6flFdndYIB0KcMWv .node polygon,#mermaid-svg-6flFdndYIB0KcMWv .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-6flFdndYIB0KcMWv .rough-node .label text,#mermaid-svg-6flFdndYIB0KcMWv .node .label text,#mermaid-svg-6flFdndYIB0KcMWv .image-shape .label,#mermaid-svg-6flFdndYIB0KcMWv .icon-shape .label{text-anchor:middle;}#mermaid-svg-6flFdndYIB0KcMWv .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-6flFdndYIB0KcMWv .rough-node .label,#mermaid-svg-6flFdndYIB0KcMWv .node .label,#mermaid-svg-6flFdndYIB0KcMWv .image-shape .label,#mermaid-svg-6flFdndYIB0KcMWv .icon-shape .label{text-align:center;}#mermaid-svg-6flFdndYIB0KcMWv .node.clickable{cursor:pointer;}#mermaid-svg-6flFdndYIB0KcMWv .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-6flFdndYIB0KcMWv .arrowheadPath{fill:#333333;}#mermaid-svg-6flFdndYIB0KcMWv .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-6flFdndYIB0KcMWv .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-6flFdndYIB0KcMWv .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6flFdndYIB0KcMWv .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-6flFdndYIB0KcMWv .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6flFdndYIB0KcMWv .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-6flFdndYIB0KcMWv .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-6flFdndYIB0KcMWv .cluster text{fill:#333;}#mermaid-svg-6flFdndYIB0KcMWv .cluster span{color:#333;}#mermaid-svg-6flFdndYIB0KcMWv div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-6flFdndYIB0KcMWv .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-6flFdndYIB0KcMWv rect.text{fill:none;stroke-width:0;}#mermaid-svg-6flFdndYIB0KcMWv .icon-shape,#mermaid-svg-6flFdndYIB0KcMWv .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6flFdndYIB0KcMWv .icon-shape p,#mermaid-svg-6flFdndYIB0KcMWv .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-6flFdndYIB0KcMWv .icon-shape .label rect,#mermaid-svg-6flFdndYIB0KcMWv .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6flFdndYIB0KcMWv .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-6flFdndYIB0KcMWv .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-6flFdndYIB0KcMWv :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} IRP_MJ_CREATE
是
流式 TCP
数据报 UDP
流式-服务器
socket 创建
AfdCreateSocket
绑定地址?
bind → IOCTL_AFD_BIND
AfdBindSocket → TdiOpenAddressFile
连接型 or 数据报?
connect → IOCTL_AFD_CONNECT
直接 sendto/recvfrom
listen → IOCTL_AFD_START_LISTEN
accept → IOCTL_AFD_WAIT_FOR_LISTEN + IOCTL_AFD_ACCEPT
数据交换: send/recv
shutdown/closesocket → IOCTL_AFD_DISCONNECT
IRP_MJ_CLEANUP → AfdCleanupSocket
IRP_MJ_CLOSE → AfdCloseSocket → 释放 FCB
9.2 用户态 → AFD → TDI 调用链
#mermaid-svg-Rflsk4BzVq1I1be6{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Rflsk4BzVq1I1be6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Rflsk4BzVq1I1be6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Rflsk4BzVq1I1be6 .error-icon{fill:#552222;}#mermaid-svg-Rflsk4BzVq1I1be6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Rflsk4BzVq1I1be6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Rflsk4BzVq1I1be6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Rflsk4BzVq1I1be6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Rflsk4BzVq1I1be6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Rflsk4BzVq1I1be6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Rflsk4BzVq1I1be6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Rflsk4BzVq1I1be6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Rflsk4BzVq1I1be6 .marker.cross{stroke:#333333;}#mermaid-svg-Rflsk4BzVq1I1be6 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Rflsk4BzVq1I1be6 p{margin:0;}#mermaid-svg-Rflsk4BzVq1I1be6 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Rflsk4BzVq1I1be6 .cluster-label text{fill:#333;}#mermaid-svg-Rflsk4BzVq1I1be6 .cluster-label span{color:#333;}#mermaid-svg-Rflsk4BzVq1I1be6 .cluster-label span p{background-color:transparent;}#mermaid-svg-Rflsk4BzVq1I1be6 .label text,#mermaid-svg-Rflsk4BzVq1I1be6 span{fill:#333;color:#333;}#mermaid-svg-Rflsk4BzVq1I1be6 .node rect,#mermaid-svg-Rflsk4BzVq1I1be6 .node circle,#mermaid-svg-Rflsk4BzVq1I1be6 .node ellipse,#mermaid-svg-Rflsk4BzVq1I1be6 .node polygon,#mermaid-svg-Rflsk4BzVq1I1be6 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Rflsk4BzVq1I1be6 .rough-node .label text,#mermaid-svg-Rflsk4BzVq1I1be6 .node .label text,#mermaid-svg-Rflsk4BzVq1I1be6 .image-shape .label,#mermaid-svg-Rflsk4BzVq1I1be6 .icon-shape .label{text-anchor:middle;}#mermaid-svg-Rflsk4BzVq1I1be6 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Rflsk4BzVq1I1be6 .rough-node .label,#mermaid-svg-Rflsk4BzVq1I1be6 .node .label,#mermaid-svg-Rflsk4BzVq1I1be6 .image-shape .label,#mermaid-svg-Rflsk4BzVq1I1be6 .icon-shape .label{text-align:center;}#mermaid-svg-Rflsk4BzVq1I1be6 .node.clickable{cursor:pointer;}#mermaid-svg-Rflsk4BzVq1I1be6 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Rflsk4BzVq1I1be6 .arrowheadPath{fill:#333333;}#mermaid-svg-Rflsk4BzVq1I1be6 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Rflsk4BzVq1I1be6 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Rflsk4BzVq1I1be6 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Rflsk4BzVq1I1be6 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Rflsk4BzVq1I1be6 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Rflsk4BzVq1I1be6 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Rflsk4BzVq1I1be6 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Rflsk4BzVq1I1be6 .cluster text{fill:#333;}#mermaid-svg-Rflsk4BzVq1I1be6 .cluster span{color:#333;}#mermaid-svg-Rflsk4BzVq1I1be6 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Rflsk4BzVq1I1be6 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Rflsk4BzVq1I1be6 rect.text{fill:none;stroke-width:0;}#mermaid-svg-Rflsk4BzVq1I1be6 .icon-shape,#mermaid-svg-Rflsk4BzVq1I1be6 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Rflsk4BzVq1I1be6 .icon-shape p,#mermaid-svg-Rflsk4BzVq1I1be6 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Rflsk4BzVq1I1be6 .icon-shape .label rect,#mermaid-svg-Rflsk4BzVq1I1be6 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Rflsk4BzVq1I1be6 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Rflsk4BzVq1I1be6 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Rflsk4BzVq1I1be6 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} tcpip.sys
TDI 层
内核态 AFD.sys
用户态
有
没有
应用程序: send 数据
ws2_32.dll: send 函数
查找 WSSOCKET → 调用 lpWSPSend
AFD 提供者: 组织 AFD_SEND_INFO
DeviceIoControl\\hSocket, IOCTL_AFD_SEND
NtDeviceIoControlFile
I/O 管理器构造 IRP_MJ_DEVICE_CONTROL
AfdDispatch 分发
AfdConnectedSocketWriteData
LockRequest 锁定用户缓冲区
发送窗口有空间?
TdiSend 调用
挂起 IRP 到 PendingIrpList
TdiBuildInternalDeviceControlIrp TDI_SEND
IoCallDriver 传输驱动
TiDispatchInternal
DispTdiSend
TCPSendData
IP 层发送
9.3 异步完成流程
tcpip.sys tdihelpers AFD.sys AFD 提供者 应用程序 tcpip.sys tdihelpers AFD.sys AFD 提供者 应用程序 #mermaid-svg-PNKibQMHbnjDiJUx{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-PNKibQMHbnjDiJUx .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-PNKibQMHbnjDiJUx .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-PNKibQMHbnjDiJUx .error-icon{fill:#552222;}#mermaid-svg-PNKibQMHbnjDiJUx .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-PNKibQMHbnjDiJUx .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-PNKibQMHbnjDiJUx .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-PNKibQMHbnjDiJUx .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-PNKibQMHbnjDiJUx .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-PNKibQMHbnjDiJUx .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-PNKibQMHbnjDiJUx .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-PNKibQMHbnjDiJUx .marker{fill:#333333;stroke:#333333;}#mermaid-svg-PNKibQMHbnjDiJUx .marker.cross{stroke:#333333;}#mermaid-svg-PNKibQMHbnjDiJUx svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-PNKibQMHbnjDiJUx p{margin:0;}#mermaid-svg-PNKibQMHbnjDiJUx .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-PNKibQMHbnjDiJUx text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-PNKibQMHbnjDiJUx .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-PNKibQMHbnjDiJUx .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-PNKibQMHbnjDiJUx .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-PNKibQMHbnjDiJUx .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-PNKibQMHbnjDiJUx #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-PNKibQMHbnjDiJUx .sequenceNumber{fill:white;}#mermaid-svg-PNKibQMHbnjDiJUx #sequencenumber{fill:#333;}#mermaid-svg-PNKibQMHbnjDiJUx #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-PNKibQMHbnjDiJUx .messageText{fill:#333;stroke:none;}#mermaid-svg-PNKibQMHbnjDiJUx .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-PNKibQMHbnjDiJUx .labelText,#mermaid-svg-PNKibQMHbnjDiJUx .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-PNKibQMHbnjDiJUx .loopText,#mermaid-svg-PNKibQMHbnjDiJUx .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-PNKibQMHbnjDiJUx .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-PNKibQMHbnjDiJUx .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-PNKibQMHbnjDiJUx .noteText,#mermaid-svg-PNKibQMHbnjDiJUx .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-PNKibQMHbnjDiJUx .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-PNKibQMHbnjDiJUx .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-PNKibQMHbnjDiJUx .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-PNKibQMHbnjDiJUx .actorPopupMenu{position:absolute;}#mermaid-svg-PNKibQMHbnjDiJUx .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-PNKibQMHbnjDiJUx .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-PNKibQMHbnjDiJUx .actor-man circle,#mermaid-svg-PNKibQMHbnjDiJUx line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-PNKibQMHbnjDiJUx :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 数据到达/窗口更新... send(buf) DeviceIoControl(IOCTL_AFD_SEND) AfdConnectedSocketWriteData LockRequest (锁定用户内存) TdiSend TDI_SEND IRP (IoCallDriver) TCPSendData 处理 TCP 窗口不足 → 挂起 TDI IRP STATUS_PENDING 发送完成 → 完成 TDI IRP TDI IRP 完成 (IoCompleteRequest) 完成例程 → UnlockAndMaybeComplete 用户 IRP 完成 send() 返回成功
10. 数据流图
10.1 send 数据流(带数据)
用户态 内核态
┌────────────┐ ┌────────────┐ ┌────────────────────────────┐
│ App.buf │──▶│ WSABUF[] │──▶│ AFD_SEND_INFO │
│ (用户内存) │ │ (用户内存) │ │ ├─ BufferArray[0].Buffer ──┼──▶ 用户地址
└────────────┘ └────────────┘ │ ├─ BufferArray[0].Length │
│ ├─ BufferCount │
│ └─ Flags │
└────────────┬───────────────┘
│ LockRequest
│ MmProbeAndLockPages
▼
┌────────────────────────────┐
│ AFD_MAPBUF[] (MDL 数组) │
│ ├─ BufferAddress (用户VA) │
│ └─ Mdl (锁定页) │
└────────────┬───────────────┘
│ TdiSend (MDL → NDIS 包)
▼
┌────────────────────────────┐
│ TDI_SEND IRP │
│ ├─ Parameters.SendLength │
│ └─ MdlAddress (数据MDL) │
└────────────┬───────────────┘
│ IoCallDriver
▼
┌────────────────────────────┐
│ tcpip.sys: TCPSendData │
│ └─ sk_buff 拷贝/引用 │
│ └─ TCP 分段+校验和 │
│ └─ IP → 邻居 → LAN │
└────────────┬───────────────┘
│ NdisSend
▼
网卡
10.2 recv 数据流(带数据)
网卡
│ 数据帧
▼
ndis.sys → 微型端口接收
│
▼
tcpip.sys: LanReceiveWorker → IPReceive → TCPReceive
│
├─ 数据入 TCP 接收队列
├─ HandleSignalledConnection
├─ 查找挂起的 TDI_RECEIVE IRP (FCB->ReceiveIrp)
│ └─ 拷贝数据到 IRP->MdlAddress 指向的用户缓冲区
├─ 完成 TDI IRP (IoCompleteRequest)
│
▼
AFD.sys: TDI 完成例程
│
├─ UnlockAndMaybeComplete(FCB, STATUS_SUCCESS, Irp, BytesReceived)
│ ├─ IoStatus.Information = 实际接收字节数
│ └─ IoCompleteRequest (用户 IRP)
│
▼
用户态: DeviceIoControl 返回
│
▼
AFD 提供者: 从输出缓冲区读取字节数
│
▼
ws2_32: lpWSPRecv 返回
│
▼
应用程序: recv() 返回数据长度
11. 关键函数索引
11.1 用户态 (ws2_32.dll)
| 函数 | 位置 | 说明 |
|---|---|---|
connect |
sockctrl.c(file:///d:/reactos/dll/win32/ws2_32/src/sockctrl.c#L21-L116) | Winsock connect,调用 lpWSPConnect |
socket |
socklife.c(file:///d:/reactos/dll/win32/ws2_32/src/socklife.c) | 创建 socket,调用 lpWSPSocket |
WsSockGetSocket |
dsocket.c(file:///d:/reactos/dll/win32/ws2_32/src/dsocket.c) | 根据 SOCKET 句柄查找 WSSOCKET 上下文 |
LoadTransportProviders |
dprovide.c(file:///d:/reactos/dll/win32/ws2_32/src/dprovide.c) | 加载 WSP 服务提供者 (WSPStartup) |
WSAStartup |
startup.c(file:///d:/reactos/dll/win32/ws2_32/src/startup.c) | Winsock 初始化 |
11.2 内核态 AFD (AFD.sys)
| 函数 | 位置 | 说明 |
|---|---|---|
DriverEntry |
main.c(file:///d:/reactos/drivers/network/afd/afd/main.c#L1318-L1359) | 创建 \Device\Afd,注册统一分发 |
AfdDispatch |
main.c(file:///d:/reactos/drivers/network/afd/afd/main.c#L906-L1094) | 统一 IRP 分发,switch IOCTL_AFD_* |
AfdCreateSocket |
main.c(file:///d:/reactos/drivers/network/afd/afd/main.c#L302) | 创建 AFD_FCB |
AfdCleanupSocket |
main.c(file:///d:/reactos/drivers/network/afd/afd/main.c#L426) | 清理挂起 IRP |
AfdCloseSocket |
main.c(file:///d:/reactos/drivers/network/afd/afd/main.c#L460) | 关闭 socket,释放 FCB |
AfdStreamSocketConnect |
connect.c(file:///d:/reactos/drivers/network/afd/afd/connect.c#L518) | 处理 IOCTL_AFD_CONNECT,调用 TdiConnect |
AfdConnectedSocketWriteData |
write.c(file:///d:/reactos/drivers/network/afd/afd/write.c) | 处理 IOCTL_AFD_SEND,调用 TdiSend |
AfdConnectedSocketReadData |
read.c(file:///d:/reactos/drivers/network/afd/afd/read.c) | 处理 IOCTL_AFD_RECV,调用 TdiReceive |
AfdListenSocket |
listen.c(file:///d:/reactos/drivers/network/afd/afd/listen.c) | 处理 IOCTL_AFD_START_LISTEN |
AfdSelect |
select.c(file:///d:/reactos/drivers/network/afd/afd/select.c) | 处理 IOCTL_AFD_SELECT |
AfdEventSelect |
event.c(file:///d:/reactos/drivers/network/afd/afd/event.c) | 处理 IOCTL_AFD_EVENT_SELECT |
AfdCancelHandler |
main.c(file:///d:/reactos/drivers/network/afd/afd/main.c#L1186) | IRP 取消处理 |
QueueUserModeIrp |
main.c(file:///d:/reactos/drivers/network/afd/afd/main.c) | 挂起用户态 IRP 到 FCB 队列 |
11.3 TDI 辅助 (tdihelpers)
| 函数 | 位置 | 说明 |
|---|---|---|
TdiOpenDevice |
tdi.c(file:///d:/reactos/drivers/network/tdihelpers/tdi.c#L68-L146) | ZwCreateFile 打开 TDI 设备 |
TdiOpenAddressFile |
tdi.c(file:///d:/reactos/drivers/network/tdihelpers/tdi.c#L148-L201) | 打开地址文件 (EA: TdiTransportAddress) |
TdiOpenConnectionEndpointFile |
tdi.c(file:///d:/reactos/drivers/network/tdihelpers/tdi.c#L258-L308) | 打开连接端点 (EA: TdiConnectionContext) |
TdiConnect |
tdi.c(file:///d:/reactos/drivers/network/tdihelpers/tdi.c#L311-L380) | 构建 TDI_CONNECT IRP 并调用传输驱动 |
TdiQueryMaxDatagramLength |
tdi.c(file:///d:/reactos/drivers/network/tdihelpers/tdi.c#L203-L256) | 查询最大数据报长度 |
TdiBuildConnectionInfo |
tdiconn.c(file:///d:/reactos/drivers/network/tdihelpers/tdiconn.c#L246-L254) | 构造 TDI_CONNECTION_INFORMATION |
11.4 TDI 头文件
| 头文件 | 路径 | 说明 |
|---|---|---|
tdikrnl.h |
sdk/include/ddk/tdikrnl.h(file:///d:/reactos/sdk/include/ddk/tdikrnl.h) | TDI 内核接口定义 (TDI_* Minor Function) |
tdi.h |
sdk/include/ddk/tdi.h(file:///d:/reactos/sdk/include/ddk/tdi.h) | TDI 数据结构 (TA_IP_ADDRESS 等) |
tdiinfo.h |
sdk/include/ddk/tdiinfo.h(file:///d:/reactos/sdk/include/ddk/tdiinfo.h) | TDI 信息查询定义 |
附录 A:IOCTL 代码结构
Windows IOCTL 代码的组成:
IoControlCode = CTL_CODE(DeviceType, Function, Method, Access)
│ │ │ │
│ │ │ └── FILE_ANY_ACCESS / FILE_READ/WRITE_DATA
│ │ └──────────── METHOD_BUFFERED/IN_DIRECT/OUT_DIRECT/NEITHER
│ └─────────────────────── 功能序号
└──────────────────────────────────── 设备类型 (FILE_DEVICE_XXX)
示例: IOCTL_AFD_CONNECT = CTL_CODE(FILE_DEVICE_NAMED_PIPE, 0x41, METHOD_NEITHER, FILE_ANY_ACCESS)
附录 B:AFD 与 tcpip.sys 设备对象对照
| 组件 | 设备对象 | 主要 MajorFunction | 发起者 |
|---|---|---|---|
| AFD.sys | \Device\Afd |
IRP_MJ_DEVICE_CONTROL (IOCTL_AFD_*) |
用户态 WSP |
| tcpip.sys | \Device\Tcp |
IRP_MJ_INTERNAL_DEVICE_CONTROL (TDI_*) |
AFD (tdihelpers) |
| tcpip.sys | \Device\Udp |
同上 | AFD (tdihelpers) |
| tcpip.sys | \Device\Ip / \Device\RawIp |
同上 | 其他 TDI 客户端 |
附录 C:相关文档
- TCPIP 驱动分析(file:///d:/reactos/doc/TCPIP_驱动分析.md) --- tcpip.sys 协议栈内部实现
- NDIS 驱动分析(file:///d:/reactos/doc/NDIS_驱动分析.md) --- ndis.sys 中间层实现