Reactos 第 10 章 网络操作 — 10.3 Windows的网络驱动堆叠

第 10 章 网络操作 --- 10.3 Windows的网络驱动堆叠

本节俯瞰 Windows 网络驱动的完整堆叠(stack)。 Windows 网络栈从下到上由 NIC Miniport → NDIS 库 → 协议驱动(LAN Manager)→ TCP/IP 协议栈 → TDI → AFD → Winsock 多层构成。ReactOS 的实现涉及 drivers/network/dd/(file:///d:/reactos/drivers/network/dd/)、drivers/network/lan/(file:///d:/reactos/drivers/network/lan/)、drivers/network/tcpip/(file:///d:/reactos/drivers/network/tcpip/)、drivers/network/afd/(file:///d:/reactos/drivers/network/afd/) 等多个模块。


概述

Windows 网络栈的设计哲学是 分层协议独立

  • 应用层 不直接接触硬件
  • Winsock 提供 BSD 风格的 socket API
  • AFD 把 socket API 转为内核操作
  • TCP/IP 是众多协议之一,可被替换
  • NDIS 抽象 NIC,多协议可共享

本节内容概览

  • 10.3.0 框架图
  • 10.3.1 NIC Miniport 驱动
  • 10.3.2 LAN Manager(协议驱动)
  • 10.3.3 TCP/IP 协议栈
  • 10.3.4 AFD 与 Winsock
  • 10.3.5 数据流:应用 → 硬件
  • 10.3.6 数据流:硬件 → 应用
  • 10.3.7 ReactOS 实现的特殊点
  • 10.3.8 总结与代码索引

学习目标

  • 理解 Windows 网络栈的完整层次
  • 知道 NIC → LAN → TCP/IP → AFD → Winsock 的协作
  • 跟踪一次 socket send 的完整路径
  • 跟踪一次网络包接收的完整路径

涉及的内核子系统

子系统 头文件/源文件 核心作用
NIC DD drivers/network/dd/(file:///d:/reactos/drivers/network/dd/) dc21x4、e1000、rtl8139
NDIS drivers/network/ndis/ndis/(file:///d:/reactos/drivers/network/ndis/ndis/) NDIS 库
LAN Manager drivers/network/lan/lan/(file:///d:/reactos/drivers/network/lan/lan/) NDIS 协议驱动
TCP/IP drivers/network/tcpip/tcpip/(file:///d:/reactos/drivers/network/tcpip/tcpip/) lwIP + 协议栈
AFD drivers/network/afd/afd/(file:///d:/reactos/drivers/network/afd/afd/) 内核 Winsock
Winsock DLL dll/win32/ws2_32/(file:///d:/reactos/dll/win32/ws2_32/) Win32 API

10.3.0 框架图

复制代码
                 应用层
+-------------------------------------+
| Application (浏览器、FTP、...)        |
+-------------------------------------+
              | socket/send/recv
              v
+-------------------------------------+
| Winsock DLL (ws2_32.dll + msafd)    |
+-------------------------------------+
              | DeviceIoControl (IRP)
              v
+-------------------------------------+
| AFD (afd.sys)                       |
| - socket 内核对象                    |
| - bind/connect/accept/listen        |
| - select/异步通知                   |
+-------------------------------------+
              | TDI (IRP_MJ_*, IOCTL_TDI_*)
              v
+-------------------------------------+
| TCP/IP (tcpip.sys)                  |
| - TCP / UDP / IP / ICMP / ARP        |
| - 基于 lwIP                          |
+-------------------------------------+
              | NdisSend/NdisReceive (NDIS Protocol API)
              v
+-------------------------------------+
| LAN Manager (lan.sys)               |
| - NDIS 协议驱动                      |
+-------------------------------------+
              | NdisMSend/NdisMIndicateReceive (NDIS Miniport API)
              v
+-------------------------------------+
| NDIS 库 (ndis.sys)                  |
+-------------------------------------+
              | Miniport 回调
              v
+-------------------------------------+
| NIC Miniport Driver                 |
| (e1000.sys / dc21x4.sys / ...)      |
+-------------------------------------+
              | 寄存器 / DMA
              v
+-------------------------------------+
| 网络硬件 (NIC)                      |
+-------------------------------------+
              |
              v
+-------------------------------------+
| 网络介质 (电缆/光纤/无线)            |
+-------------------------------------+

10.3.1 NIC Miniport 驱动

角色

NIC Miniport 驱动是 网络栈的最底层,直接操作硬件:

  • 配置 NIC(MAC 地址、中断、DMA)
  • 发送数据包(构造 TX 描述符)
  • 接收数据包(解析 RX 描述符)
  • 处理中断
  • 错误处理

典型 Miniport 驱动

ReactOS 自带几种 Miniport:

驱动 设备 来源
dc21x4.sys DEC 21x4x 系列(百兆网卡) 原版
e1000.sys Intel PRO/1000(千兆) Intel 原始
rtl8139.sys Realtek 8139(百兆) 原版
ne2k_pci.sys NE2000 兼容 原版
pcnetnic.sys AMD PCnet 原版

Miniport 入口

c 复制代码
NTSTATUS NTAPI
DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
    NDIS_MINIPORT_CHARACTERISTICS Chars = {0};
    Chars.MajorNdisVersion = 5;
    Chars.MinorNdisVersion = 0;
    Chars.InitializeHandler = MyInit;
    Chars.HaltHandler = MyHalt;
    Chars.SendHandler = MySend;
    Chars.TransferDataHandler = MyTransferData;
    Chars.ResetHandler = MyReset;
    // ...
    NdisMRegisterMiniport(&Status, DriverObject, &Chars, sizeof(Chars));
    return Status;
}

详见 10.3.1 节 dc21x4 实例分析。


10.3.2 LAN Manager(协议驱动)

角色

LAN Manager(lan.sys)是 NDIS 协议驱动,位于 TCP/IP 协议栈和 NDIS 之间:

  • 注册为 NDIS 协议驱动
  • 提供 TDI 接口 给上层(TCP/IP)
  • 调用 NDIS 协议 API 发送/接收包

关键 API

LAN Manager 实现 NdisRegisterProtocol 把自己注册为 NDIS 协议驱动,然后通过 TDI 与 TCP/IP 通信。

lan.sys 的具体实现细节将在 10.3.2 节展开。


10.3.3 TCP/IP 协议栈

角色

tcpip.sys真正的协议栈,实现:

  • IP:分组转发、分片重组
  • ICMP:ping、错误报告
  • ARP:IP → MAC 解析
  • TCP:可靠连接、流量控制
  • UDP:无连接数据报

关键实现

ReactOS 的 TCP/IP 内部使用 lwIP(lightweight IP)。lwIP 是一个开源的 TCP/IP 协议栈实现,专为嵌入式系统设计但功能完整。

上层接口

TCP/IP 暴露 TDI(Transport Driver Interface) 给上层(AFD):

c 复制代码
#define IOCTL_TDI_QUERY_ADDRESSES    0x00030004
#define IOCTL_TDI_CONNECT            0x00030008
#define IOCTL_TDI_DISCONNECT         0x0003000C
#define IOCTL_TDI_SEND_DATAGRAM      0x0003001A
// ... 等等

AFD 通过 CreateFileW 打开 \Device\Tcp / \Device\Udp 并通过 IOCTL 与 TCP/IP 通信。

详见 10.3.3 节。


10.3.4 AFD 与 Winsock

角色

AFD (Ancillary Function Driver,afd.sys)是 内核态 Winsock

  • 维护 socket 状态
  • 实现 bind/connect/accept/send/recv
  • 处理 select/异步通知
  • 把 Winsock 请求翻译为 IRP 发给协议驱动

Winsock DLLws2_32.dll + msafd.dll)是 用户态 Winsock

  • 提供 BSD socket API
  • 把 socket() 等调用转换为对 \Device\Afd 的 IOCTL
  • 处理 WSARecv/WSASend 等异步操作

详见 10.3.4 节。


10.3.5 数据流:应用 → 硬件

一次 send() 调用的完整路径

c 复制代码
// 1. 应用调用 send()
send(socket, buffer, length, 0);

用户态ws2_32.dll):

c 复制代码
int send(SOCKET s, const char *buf, int len, int flags) {
    WSABUF wsaBuf = {len, (char*)buf};
    DWORD bytesSent;
    return WSASend(s, &wsaBuf, 1, &bytesSent, flags, NULL, NULL);
}

int WSASend(SOCKET s, LPWSABUF bufs, DWORD bufCount, LPDWORD bytesSent,
            DWORD flags, LPWSAOVERLAPPED ovl, LPWSAOVERLAPPED_COMPLETION_ROUTINE cr) {
    // 构造请求
    AFD_SEND_REQUEST req = { ... };
    
    // 通过 msafd 翻译
    return WSPStartup_table[s].WSPSend(s, bufs, bufCount, bytesSent, flags, ovl, cr);
}

msafd

c 复制代码
int WSPSend(SOCKET s, ...) {
    // 打开 \Device\Afd 句柄(每个 socket 一个)
    // 构造 AFD_SEND_INFO
    // 调 NtDeviceIoControlFile 发 IOCTL_AFD_SEND
    return NtStatus;
}

AFDafd.sys):

c 复制代码
NTSTATUS AfdSend(...) {
    PAFD_SEND_INFO Info;
    
    // 1. 验证 socket 状态
    if (FCB->State != SOCKET_STATE_CONNECTED) {
        return STATUS_INVALID_CONNECTION;
    }
    
    // 2. 构造 TDI 发送请求
    TDI_REQUEST_KERNEL_SEND DatagramRequest;
    
    // 3. 构造 IRP
    PIRP Irp = TdiBuildInternalDeviceControlIrp(
        TDI_SEND_DATAGRAM,     // 或 TDI_SEND
        TransportDeviceObject,
        ...);
    
    // 4. 调用 TCP/IP
    IoCallDriver(TcpDeviceObject, Irp);
}

TCP/IPtcpip.sys):

c 复制代码
// lwIP 的 tcp_write / udp_send
err_t err = tcp_write(tpcb, data, len, copy);

NDIS 协议 → Miniport

c 复制代码
NdisSend(&Status, BindingHandle, Packet);

Miniport

c 复制代码
NDIS_STATUS MySend(NDIS_HANDLE AdapterContext, PNDIS_PACKET Packet, UINT Flags) {
    // 1. 从 Packet 提取数据
    // 2. 构造 PRDT
    // 3. 启动 DMA
    return NDIS_STATUS_PENDING;
}

硬件

NIC 把帧放到物理介质上。


10.3.6 数据流:硬件 → 应用

一次包接收的完整路径

硬件

NIC 收到帧,触发中断。

Miniport ISR

c 复制代码
VOID MyInterrupt(NDIS_HANDLE MiniportInterruptContext) {
    // 1. 读取中断状态寄存器
    // 2. 识别是接收完成
    // 3. 处理接收到的包
    MyHandleReceive();
}

VOID MyHandleReceive() {
    // 1. 分配包结构
    PNDIS_PACKET Packet;
    NdisAllocatePacket(&Status, &Packet, MyPacketPool);
    
    // 2. 关联缓冲
    NdisChainBufferAtFront(Packet, MyBuffer);
    
    // 3. 通知 NDIS
    NdisMIndicateReceivePacket(MyAdapterHandle, &Packet, 1);
}

NDIS(分发):

c 复制代码
VOID NdisMIndicateReceivePacket(NDIS_HANDLE Adapter, PPNDIS_PACKET Packets, UINT Count) {
    PNDIS_ADAPTER_BLOCK Adapter = ...;
    
    // 对每个协议驱动调用 ReceivePacket
    for (i = 0; i < Adapter->ProtocolCount; i++) {
        Protocol = Adapter->Protocols[i];
        Protocol->ReceivePacketHandler(ProtocolBindingContext, Packets[i]);
    }
}

LAN Manager / TCP/IP

c 复制代码
INT LanReceivePacket(NDIS_HANDLE Context, PNDIS_PACKET Packet) {
    // 1. 检查包类型
    // 2. 交给协议栈(lwIP)
    
    // 通过 TCP/IP 的内部队列传递给 lwIP
    return tcp_input(...);  // lwIP 函数
}

lwIP 内部

c 复制代码
err_t tcp_input(struct pbuf *p, struct netif *inp) {
    // 1. 解析 TCP 头
    // 2. 查找 PCB
    // 3. 数据入队到 socket
}

AFD(wakeup):

TCP/IP 通过 TDI 事件通知 AFD,AFD 唤醒等待的 recv() 线程。

用户态

c 复制代码
recv(socket, buffer, length, 0);
// 之前阻塞的 recv 返回,数据已被复制到 buffer

10.3.7 ReactOS 实现的特殊点

lwIP 集成

ReactOS 的 TCP/IP 协议栈使用 lwIP 1.4.x 作为协议实现。集成方式:

  • 内核线程tcpip_thread 在后台跑 lwIP
  • 消息队列tcpip_inputtcpip_callback
  • 内存管理:使用 NT 内核内存分配器

AFD 实现

afd.sys完全 ReactOS 实现,不依赖任何 Windows 内部结构。它实现:

  • 完整 socket 状态机
  • bind/connect/accept
  • send/recv
  • select
  • WSAEventSelect、WSAAsyncSelect

简化点

  • 不实现 msafd 的 Service Provider 接口(仅 LSP 1)
  • 不实现 QOS / RSVP
  • 不实现 Winsock 2 直连(WSARecvMSG 等部分高级功能)
  • 不实现 IPv6(lwIP 已支持,ReactOS 未集成)

10.3.8 总结

关键要点

  1. 网络栈分层:硬件 → Miniport → NDIS → LAN Manager → TCP/IP → AFD → Winsock → 应用
  2. NIC Miniport 驱动:直接操作硬件
  3. LAN Manager:NDIS 协议驱动 + TDI 接口
  4. TCP/IP 协议栈:基于 lwIP
  5. AFD:内核 Winsock
  6. Winsock DLL:用户态 API
  7. 一次 sendsend → WSASend → msafd → AFD → TCP/IP → NDIS → Miniport → 硬件
  8. 一次 receive硬件 → ISR → Miniport → NDIS → Protocol → TCP/IP → AFD → 应用

Windows 网络驱动堆叠的深度架构分析

Windows 网络驱动堆叠是操作系统设计中最精密的多层架构之一。ReactOS 对这一架构的完整复现,不仅需要对 Windows 内部机制的深入理解,还需要在工程实现上做出大量权衡。本节从架构层面深入分析网络驱动堆叠的设计原理和 ReactOS 的实现策略。

分层架构的设计原则

Windows 网络栈的分层设计遵循以下核心原则:

  1. 信息隐藏:每一层只暴露必要的接口给相邻层,内部实现完全隐藏。例如,Miniport 驱动不需要知道 TCP 协议的任何细节,TCP/IP 协议栈也不需要知道 NIC 硬件的寄存器布局。

  2. 接口标准化:NDIS 定义了 Miniport 接口标准,TDI 定义了传输层接口标准,Winsock 定义了用户态 API 标准。标准化使得不同厂商的组件可以互操作。

  3. 层间解耦:每一层可以独立替换。例如,可以将 TCP/IP 替换为 IPX/SPX,而不影响 Miniport 驱动;可以更换 NIC 硬件,而不影响上层应用。

  4. 数据流双向性:数据可以在栈中上下流动。发送方向自上而下,接收方向自下而上。每一层在发送时添加头部封装,在接收时剥离头部。

ReactOS 中的层间通信机制

在 ReactOS 中,层间通信主要通过两种机制实现:

  1. 函数调用(同层内) :同一层内的模块通过直接函数调用通信。例如,lwIP 内部的 TCP 模块调用 IP 模块的 ip4_output 函数。

  2. IRP(跨层) :跨层通信通过 I/O 请求包(IRP)实现。IRP 是 Windows 内核中所有 I/O 操作的核心数据结构。当用户态应用调用 send() 时,ws2_32.dll 构造一个 IRP,通过 NtDeviceIoControlFile 系统调用将 IRP 发送到 AFD 驱动。AFD 处理完自己的逻辑后,再构造新的 IRP 发送到 TCP/IP 驱动。

这种 IRP 链式传递的模式意味着一次 send() 调用可能在内核中产生多个 IRP,每个 IRP 对应一个层次的处理。这种设计的优点是每一层可以异步处理请求,缺点是 IRP 的创建和传递带来额外开销。

网络驱动堆叠的初始化顺序

系统启动时,网络驱动堆叠按照严格的顺序初始化:

  1. NDIS 库初始化ndis.sysDriverEntry 首先运行,初始化全局列表(ProtocolListHeadMiniportListHeadAdapterListHead)。

  2. Miniport 驱动注册 :各个 NIC 驱动(如 dc21x4.syse1000.sys)的 DriverEntry 调用 NdisMRegisterMiniport,将回调函数注册到 NDIS 库。

  3. PnP 设备枚举:PnP 管理器枚举 PCI 总线上的网络设备,为每个设备创建物理设备对象(PDO)。

  4. 适配器初始化 :NDIS 为每个网络设备创建适配器实例,调用 Miniport 的 MiniportInitialize 初始化硬件。

  5. 协议驱动注册 :TCP/IP(tcpip.sys)的 DriverEntry 调用 NdisRegisterProtocol 注册为 NDIS 协议驱动。

  6. 协议绑定 :协议驱动通过 NdisOpenAdapter 绑定到适配器,建立数据通路。

  7. TDI 设备创建 :TCP/IP 创建 \Device\Tcp\Device\Udp 等设备对象,准备接收上层请求。

  8. AFD 初始化afd.sysDriverEntry 创建 \Device\Afd 设备对象。

  9. Winsock 加载 :当应用首次调用 WSAStartup 时,ws2_32.dll 加载 msafd.dll,建立 SPI 入口表。

数据包封装的层次模型

在网络栈中,数据从应用到硬件的发送过程中,每经过一层都会添加该层的头部封装:

复制代码
应用数据(无头部)
    ↓ AFD 添加 socket 层信息
    ↓ TCP/IP 添加 TCP 头(20 字节)→ TCP 段
    ↓ TCP/IP 添加 IP 头(20 字节)→ IP 数据报
    ↓ NDIS/LAN 添加以太网头(14 字节)→ 以太网帧
    ↓ Miniport 添加前导码和 FCS → 物理帧

接收过程则相反,每层剥离对应的头部:

复制代码
物理帧 → Miniport 剥离前导码和 FCS
    → 以太网帧 → NDIS 剥离以太网头
    → IP 数据报 → TCP/IP 剥离 IP 头
    → TCP 段 → TCP/IP 剥离 TCP 头
    → 应用数据 → AFD 传递给应用

在 ReactOS 中,这种封装/解封装通过 NDIS_PACKETNDIS_BUFFER 链实现。每个 NDIS_PACKET 包含一个缓冲区链表,链表中的每个节点对应一层的头部或数据。这种设计避免了频繁的数据复制,提高了处理效率。

多协议共存的实现

Windows 网络栈支持多个协议同时绑定到同一个 NIC。例如,一个网卡可以同时绑定 TCP/IP 和 IPX/SPX 协议。当 NIC 收到一个帧时,NDIS 库将帧分发给所有绑定的协议,每个协议根据帧的类型字段决定是否接受。

在 ReactOS 中,这种分发由 NdisMIndicateReceivePacket 实现:

c 复制代码
VOID NdisMIndicateReceivePacket(
    NDIS_HANDLE NdisAdapterHandle,
    PPNDIS_PACKET PacketArray,
    UINT NumberOfPackets)
{
    PNDIS_ADAPTER_BLOCK Adapter = NdisAdapterHandle;
    
    // 遍历所有绑定的协议
    for (i = 0; i < Adapter->ProtocolCount; i++) {
        Protocol = Adapter->Protocols[i];
        // 调用每个协议的接收回调
        Protocol->ReceivePacketHandler(
            Protocol->ProtocolBindingContext,
            PacketArray[i]);
    }
}

每个协议在接收回调中检查帧的以太网类型字段(如 0x0800 表示 IP,0x0806 表示 ARP),只处理属于自己的帧。

IRP 完成与异步通知

网络 I/O 的异步特性使得 IRP 完成机制变得复杂。当一个 recv() 调用没有数据可收时,AFD 不会立即完成 IRP,而是将其挂起,等待数据到达。数据到达时,TCP/IP 通过 TDI 事件通知 AFD,AFD 再完成挂起的 IRP。

ReactOS 中的 AFD 使用 FAST_MUTEX 保护端点状态,使用 KEVENT 实现线程等待和唤醒。这种设计确保了异步 I/O 的正确性,同时避免了忙等待带来的 CPU 浪费。

ReactOS 网络栈的性能特征

ReactOS 的网络栈在性能方面有以下特征:

  1. 内存复制次数 :一次 send() 调用至少涉及 3 次内存复制(用户缓冲 → AFD 缓冲 → lwIP pbuf → DMA 缓冲)。Windows 通过使用 MDL(Memory Descriptor List)和直接 DMA 减少复制次数。

  2. 锁竞争:lwIP 的全局锁是性能瓶颈。在高并发场景下,多个线程同时发送数据会导致锁竞争。Windows 的 TCP/IP 栈使用更细粒度的锁(每个连接一个锁)来减少竞争。

  3. 中断处理:ReactOS 的 Miniport 驱动使用传统的中断处理方式。现代 Windows 使用消息信号中断(MSI/MSI-X),可以减少中断延迟和 CPU 开销。

与 Windows 网络栈的架构对比

特性 Windows ReactOS
协议栈实现 专有实现 lwIP 开源实现
NDIS 版本 6.30+ 5.1
IPv6 完整支持 未启用
卸载技术 LSO/RSS/Checksum 不支持
过滤平台 WFP TDI 过滤
名字解析 DNS Client 服务 简单实现
QoS DiffServ/802.1p 不支持

ReactOS 的网络栈虽然在功能和性能上与 Windows 存在差距,但其架构设计完整地反映了 Windows 网络体系的核心思想。这种架构上的忠实复现,使得 ReactOS 能够运行大多数为 Windows 编写的网络应用程序。


本章代码索引

文件 内容
dd/dc21x4(file:///d:/reactos/drivers/network/dd/dc21x4/) DEC 21x4x NIC 驱动
dd/e1000(file:///d:/reactos/drivers/network/dd/e1000/) Intel PRO/1000
dd/rtl8139(file:///d:/reactos/drivers/network/dd/rtl8139/) Realtek 8139
ndis/ndis/(file:///d:/reactos/drivers/network/ndis/ndis/) NDIS 库
lan/lan/(file:///d:/reactos/drivers/network/lan/lan/) LAN Manager
tcpip/tcpip/(file:///d:/reactos/drivers/network/tcpip/tcpip/) TCP/IP 协议栈
afd/afd/(file:///d:/reactos/drivers/network/afd/afd/) AFD 内核 Winsock
ws2_32/(file:///d:/reactos/dll/win32/ws2_32/) 用户态 Winsock
msafd/(file:///d:/reactos/dll/win32/msafd/) Microsoft Winsock