欢迎光临
我们一直在努力

TAP-Windows V9 (NDIS 6.20/6.30)

TAP-Windows V9 (NDIS 6.20/6.30)

1. TAP-Windows V9 到底是什么?

TAP-Windows V9 是OpenVPN项目开发的虚拟以太网适配器驱动,基于微软NDIS (Network Driver Interface Specification) 6.20/6.30框架实现。它的核心文件是 tap0901.sys,通常安装在 C:\\Program Files\\TAP-Windows 目录下。

简单理解:它是一个纯软件实现的"网卡",没有物理硬件,但操作系统认为它就是一块真实的以太网卡。VPN软件(如OpenVPN)通过它来捕获和注入网络数据包,实现虚拟专用网络连接。

为什么叫V9?因为它对应NDIS 6版本(驱动文件版本9.x),而老版本NDIS 5对应的是V8(版本8.x)。


2. 系统怎么驱动 TAP-Windows V9

2.1 INF文件:驱动的"身份证"和"安装说明书"

INF文件是Windows驱动程序安装的核心。TAP-Windows V9的INF文件(通常名为 OemVista.inf 或编译后的 OemVista.inf)包含了所有设备信息、配置参数和安装指令。

完整INF核心段落摘录(基于OpenVPN/tap-windows6源码):

[Version]
Signature = "$Windows NT$"
Class = Net
ClassGUID = {4d36e972-e325-11ce-bfc1-08002be10318} ; 网络适配器类GUID
Provider = %Provider%
CatalogFile = tap0901.cat
DriverVer = 03/15/2024,9.24.2.601

[Strings]
Provider = "OpenVPN Technologies, Inc."
DeviceDescription = "TAP-Windows Adapter V9" ; ← 设备管理器显示的描述!
MfgName = "OpenVPN Technologies, Inc."

[Manufacturer]
%MfgName% = tap0901, NTamd64

[tap0901.NTamd64]
%DeviceDescription% = tap0901.ndi, root\\tap0901 ; Root枚举方式
%DeviceDescription% = tap0901.ndi, {6456E5C9-ABB9-4D23-ACFD-7C9DFB0B1F0C} ; 兼容ID

[tap0901.ndi]
Characteristics = 0x4 ; NCF_VIRTUAL | NCF_HAS_UI
BusType = 0 ; 内部总线
*IfType = 6 ; IF_TYPE_ETHERNET_CSMACD
*MediaType = 0 ; NdisMedium802_3
*PhysicalMediaType = 14 ; 虚拟适配器

[tap0901.ndi.Services]
AddService = tap0901, 0x00000002, tap0901_Service_Inst

[tap0901_Service_Inst]
DisplayName = "TAP-Windows Adapter V9"
ServiceType = 1 ; SERVICE_KERNEL_DRIVER
StartType = 3 ; SERVICE_DEMAND_START
ErrorControl = 1 ; SERVICE_ERROR_NORMAL
ServiceBinary = %12%\\tap0901.sys ; %12% = drivers目录
LoadOrderGroup = NDIS
Dependencies = "NDIS"

[tap0901.ndi.reg] ; 注册表配置
HKR, Ndi, Service, 0, "tap0901"
HKR, Ndi\\Interfaces, UpperRange, 0, "ndis5"
HKR, Ndi\\Interfaces, LowerRange, 0, "ethernet"

[tap0901.ndi.params] ; 高级参数配置
HKR, Ndi\\params\\NetworkAddress, ParamDesc, 0, "MAC Address"
HKR, Ndi\\params\\NetworkAddress, type, 0, "edit"
HKR, Ndi\\params\\NetworkAddress, Default, 0, ""
HKR, Ndi\\params\\NetworkAddress, optional, 0, "1"

HKR, Ndi\\params\\MTU, ParamDesc, 0, "MTU"
HKR, Ndi\\params\\MTU, type, 0, "int"
HKR, Ndi\\params\\MTU, Default, 0, "1500"
HKR, Ndi\\params\\MTU, min, 0, "576"
HKR, Ndi\\params\\MTU, max, 0, "65535"

HKR, Ndi\\params\\MediaStatus, ParamDesc, 0, "Media Status"
HKR, Ndi\\params\\MediaStatus, type, 0, "enum"
HKR, Ndi\\params\\MediaStatus, Default, 0, "0"
HKR, Ndi\\params\\MediaStatus\\enum, "0", 0, "Application Controlled"
HKR, Ndi\\params\\MediaStatus\\enum, "1", 0, "Always Connected"

HKR, Ndi\\params\\AllowNonAdmin, ParamDesc, 0, "Allow non-admin users to access adapter"
HKR, Ndi\\params\\AllowNonAdmin, type, 0, "int"
HKR, Ndi\\params\\AllowNonAdmin, Default, 0, "1"

2.2 安装加载流程

让我们跟随 tapinstall.exe 命令 tapinstall.exe install OemVista.inf tap0901,看看系统是怎么一步步加载驱动的:

图1:系统驱动加载链路

#mermaid-svg-tXzFhGPZe6eQVWI6{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-tXzFhGPZe6eQVWI6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-tXzFhGPZe6eQVWI6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-tXzFhGPZe6eQVWI6 .error-icon{fill:#552222;}#mermaid-svg-tXzFhGPZe6eQVWI6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-tXzFhGPZe6eQVWI6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-tXzFhGPZe6eQVWI6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tXzFhGPZe6eQVWI6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tXzFhGPZe6eQVWI6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-tXzFhGPZe6eQVWI6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tXzFhGPZe6eQVWI6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tXzFhGPZe6eQVWI6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-tXzFhGPZe6eQVWI6 .marker.cross{stroke:#333333;}#mermaid-svg-tXzFhGPZe6eQVWI6 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tXzFhGPZe6eQVWI6 p{margin:0;}#mermaid-svg-tXzFhGPZe6eQVWI6 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-tXzFhGPZe6eQVWI6 .cluster-label text{fill:#333;}#mermaid-svg-tXzFhGPZe6eQVWI6 .cluster-label span{color:#333;}#mermaid-svg-tXzFhGPZe6eQVWI6 .cluster-label span p{background-color:transparent;}#mermaid-svg-tXzFhGPZe6eQVWI6 .label text,#mermaid-svg-tXzFhGPZe6eQVWI6 span{fill:#333;color:#333;}#mermaid-svg-tXzFhGPZe6eQVWI6 .node rect,#mermaid-svg-tXzFhGPZe6eQVWI6 .node circle,#mermaid-svg-tXzFhGPZe6eQVWI6 .node ellipse,#mermaid-svg-tXzFhGPZe6eQVWI6 .node polygon,#mermaid-svg-tXzFhGPZe6eQVWI6 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-tXzFhGPZe6eQVWI6 .rough-node .label text,#mermaid-svg-tXzFhGPZe6eQVWI6 .node .label text,#mermaid-svg-tXzFhGPZe6eQVWI6 .image-shape .label,#mermaid-svg-tXzFhGPZe6eQVWI6 .icon-shape .label{text-anchor:middle;}#mermaid-svg-tXzFhGPZe6eQVWI6 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-tXzFhGPZe6eQVWI6 .rough-node .label,#mermaid-svg-tXzFhGPZe6eQVWI6 .node .label,#mermaid-svg-tXzFhGPZe6eQVWI6 .image-shape .label,#mermaid-svg-tXzFhGPZe6eQVWI6 .icon-shape .label{text-align:center;}#mermaid-svg-tXzFhGPZe6eQVWI6 .node.clickable{cursor:pointer;}#mermaid-svg-tXzFhGPZe6eQVWI6 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-tXzFhGPZe6eQVWI6 .arrowheadPath{fill:#333333;}#mermaid-svg-tXzFhGPZe6eQVWI6 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-tXzFhGPZe6eQVWI6 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-tXzFhGPZe6eQVWI6 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tXzFhGPZe6eQVWI6 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-tXzFhGPZe6eQVWI6 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tXzFhGPZe6eQVWI6 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-tXzFhGPZe6eQVWI6 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-tXzFhGPZe6eQVWI6 .cluster text{fill:#333;}#mermaid-svg-tXzFhGPZe6eQVWI6 .cluster span{color:#333;}#mermaid-svg-tXzFhGPZe6eQVWI6 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-tXzFhGPZe6eQVWI6 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-tXzFhGPZe6eQVWI6 rect.text{fill:none;stroke-width:0;}#mermaid-svg-tXzFhGPZe6eQVWI6 .icon-shape,#mermaid-svg-tXzFhGPZe6eQVWI6 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tXzFhGPZe6eQVWI6 .icon-shape p,#mermaid-svg-tXzFhGPZe6eQVWI6 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-tXzFhGPZe6eQVWI6 .icon-shape rect,#mermaid-svg-tXzFhGPZe6eQVWI6 .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tXzFhGPZe6eQVWI6 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-tXzFhGPZe6eQVWI6 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-tXzFhGPZe6eQVWI6 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

tapinstall.exe 执行 install命令

SetupAPI解析INF文件

创建服务注册表项HKLM\\SYSTEM\\CurrentControlSet\\Services\\tap0901

复制驱动文件到%SystemRoot%\\System32\\drivers\\tap0901.sys

PnP管理器枚举新硬件匹配HardwareID ''root\\tap0901''

加载驱动镜像到内核空间

调用DriverEntry入口函数

NdisMRegisterMiniportDriver注册Miniport

PnP调用AddDevice或MiniportInitializeEx(NDIS6)

创建设备对象\\Device\\TAP{GUID}

创建符号链接\\DosDevices\\Global{GUID}.tap

NDIS注册网络接口

TCP/IP协议栈绑定

网络连接面板显示新网卡

Ready: 等待DeviceIoControl调用

步骤分析:

步骤1-3:文件复制与服务创建

  • tapinstall.exe(实际是DPInst或PnPUtil的变体)调用SetupAPI函数
  • INF文件被解析,提取[Version]、[Manufacturer]、[tap0901.NTamd64]等节
  • 在注册表 HKLM\\SYSTEM\\CurrentControlSet\\Services\\tap0901 创建键:
    • ImagePath = \\SystemRoot\\System32\\drivers\\tap0901.sys
    • Start = 3 (SERVICE_DEMAND_START) – 需要时启动
    • Type = 1 (SERVICE_KERNEL_DRIVER)
    • Group = NDIS – 指定加载顺序组

步骤4-5:设备枚举与硬件ID匹配

  • PnP管理器维护一个内部设备树
  • 当INF告诉系统"这个驱动支持 root\\tap0901"时,PnP会创建一个"根枚举"的设备节点
  • 硬件ID root\\tap0901 在INF的[tap0901.NTamd64]节指定

步骤6-7:驱动加载与DriverEntry

  • I/O管理器调用 ZwLoadDriver 加载tap0901.sys
  • 系统调用驱动入口点 DriverEntry(必须导出)
  • DriverEntry 执行以下关键操作(见第4节源码)

2.3 验证安装的命令行工具

# 查看驱动服务状态
sc query tap0901

# 查看驱动详细信息
sc qc tap0901

# 列出所有网络适配器,包括TAP
pnputil /enum-devices /class net

# 查看TAP设备实例ID
Get-PnpDevice | Where-Object {$_.FriendlyName -like "*TAP*"}

# 查看驱动文件签名
Get-AuthenticodeSignature C:\\Windows\\System32\\drivers\\tap0901.sys

2.4 常见故障

问题现象可能原因解决方案
设备管理器显示黄色感叹号 驱动签名问题 启用测试签名模式或安装签名驱动
OpenVPN报"No TAP adapters found" 驱动未正确安装 运行tapinstall.exe remove,再重新安装
网卡显示"已断开" MediaStatus设置为Application Controlled 修改注册表或INF中MediaStatus默认值
非管理员无法访问 AllowNonAdmin设置为0 修改为1或赋予用户设备访问权限

3. 网卡怎么创建的?

3.1 NDIS 6 Miniport初始化

在NDIS 6中,网卡初始化不再使用传统的AddDevice,而是通过 MiniportInitializeEx 回调函数完成。这个回调是在 DriverEntry 中注册的。

3.2 DriverEntry:一切起点

源码文件:tapdrvr.c

NDIS_STATUS
DriverEntry(
_In_ PDRIVER_OBJECT DriverObject,
_In_ PUNICODE_STRING RegistryPath
)
{
NDIS_STATUS Status;
NDIS_MINIPORT_DRIVER_CHARACTERISTICS MiniportChars;
TAP_DRIVER_GLOBAL_DATA *GlobalData = &g_TapDriverGlobalData;

DEBUGP(("[tap] DriverEntry: Version 9.24.2.601\\n"));

// 初始化全局数据
NdisZeroMemory(GlobalData, sizeof(TAP_DRIVER_GLOBAL_DATA));
GlobalData->Signature = TAP_DRIVER_GLOBAL_SIGNATURE;
GlobalData->RegistryPath = *RegistryPath; // 保存注册表路径

// 初始化NDIS库版本
NdisZeroMemory(&MiniportChars, sizeof(NDIS_MINIPORT_DRIVER_CHARACTERISTICS));
MiniportChars.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_DRIVER_CHARACTERISTICS;
MiniportChars.Header.Size = sizeof(NDIS_MINIPORT_DRIVER_CHARACTERISTICS);
MiniportChars.Header.Revision = NDIS_MINIPORT_DRIVER_CHARACTERISTICS_REVISION_2;

// 设置所有NDIS回调函数 – 这是核心!
MiniportChars.InitializeHandlerEx = TapAdapterCreate; // ← 网卡初始化
MiniportChars.HaltHandlerEx = TapAdapterHalt;
MiniportChars.PauseHandler = TapAdapterPause;
MiniportChars.RestartHandler = TapAdapterRestart;
MiniportChars.OidRequestHandler = TapAdapterOidRequest; // ← OID处理
MiniportChars.SendNetBufferListsHandler = TapAdapterSend; // ← 数据发送
MiniportChars.ReturnNetBufferListsHandler = TapAdapterReturnNetBufferLists;
MiniportChars.CancelSendHandler = TapAdapterCancelSend;
MiniportChars.CheckForHangHandlerEx = TapAdapterCheckForHang;
MiniportChars.ResetHandlerEx = TapAdapterReset;
MiniportChars.DevicePnPEventNotifyHandler = TapAdapterPnPEventNotify;
MiniportChars.ShutdownHandlerEx = TapAdapterShutdown;
MiniportChars.CancelOidRequestHandler = TapAdapterCancelOidRequest;

// 注册Miniport驱动到NDIS
Status = NdisMRegisterMiniportDriver(
DriverObject,
RegistryPath,
NULL, // 不需要上下文
&MiniportChars,
NDIS_SIZEOF_MINIPORT_DRIVER_CHARACTERISTICS_REVISION_2,
&GlobalData->NdisDriverHandle
);

if (Status != NDIS_STATUS_SUCCESS) {
DEBUGP(("[tap] NdisMRegisterMiniportDriver failed: %x\\n", Status));
return Status;
}

DEBUGP(("[tap] DriverEntry: NDIS Miniport driver registered successfully\\n"));
return NDIS_STATUS_SUCCESS;
}

3.3 AdapterCreate:网卡对象的诞生

当PnP管理器检测到需要初始化TAP设备时,NDIS调用 TapAdapterCreate 回调。

源码文件:adapter.c

NDIS_STATUS
TapAdapterCreate(
_In_ NDIS_HANDLE MiniportAdapterHandle,
_In_ NDIS_HANDLE MiniportDriverContext,
_In_ PNDIS_MINIPORT_INIT_PARAMETERS MiniportInitParameters
)
{
NDIS_STATUS Status;
PTAP_ADAPTER_CONTEXT Adapter = NULL;
NDIS_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES RegAttributes;
NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES GenAttributes;
NDIS_MINIPORT_ADAPTER_OFFLOAD_ATTRIBUTES OffloadAttributes;
BOOLEAN bLockCreated = FALSE;
BOOLEAN bContextAllocated = FALSE;
BOOLEAN bAdapterRegistered = FALSE;

DEBUGP(("[tap] TapAdapterCreate: Enter\\n"));

// === 步骤1:分配适配器上下文结构 ===
// 这个结构是整个驱动的"心脏",保存所有状态
Adapter = (PTAP_ADAPTER_CONTEXT)ExAllocatePoolWithTag(
NonPagedPoolNx,
sizeof(TAP_ADAPTER_CONTEXT),
TAP_ADAPTER_TAG
);

if (Adapter == NULL) {
Status = NDIS_STATUS_RESOURCES;
goto Cleanup;
}

NdisZeroMemory(Adapter, sizeof(TAP_ADAPTER_CONTEXT));
Adapter->Signature = TAP_ADAPTER_SIGNATURE;
Adapter->MiniportAdapterHandle = MiniportAdapterHandle;
Adapter->DriverContext = MiniportDriverContext;
Adapter->AdapterState = TapAdapterStateUninitialized;
bContextAllocated = TRUE;

// === 步骤2:初始化同步对象 ===
NdisAllocateSpinLock(&Adapter->Lock);
NdisAllocateSpinLock(&Adapter->SendLock);
NdisAllocateSpinLock(&Adapter->RecvLock);
NdisInitializeEvent(&Adapter->ControlEvent);
NdisInitializeEvent(&Adapter->AdapterPausedEvent);
bLockCreated = TRUE;

// === 步骤3:读取注册表配置 ===
// 从HKLM\\SYSTEM\\CurrentControlSet\\Control\\Class\\{4d36e972-…}\\{Instance}读取
Status = TapReadAdapterConfiguration(Adapter, MiniportInitParameters);
if (Status != NDIS_STATUS_SUCCESS) {
DEBUGP(("[tap] TapReadAdapterConfiguration failed: %x\\n", Status));
goto Cleanup;
}

// === 步骤4:设置NDIS注册属性 ===
NdisZeroMemory(&RegAttributes, sizeof(RegAttributes));
RegAttributes.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES;
RegAttributes.Header.Size = sizeof(RegAttributes);
RegAttributes.Header.Revision = NDIS_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES_REVISION_1;

RegAttributes.AttributeFlags = NDIS_MINIPORT_ATTRIBUTES_SURPRISE_REMOVE_OK |
NDIS_MINIPORT_ATTRIBUTES_HARDWARE_DEVICE |
NDIS_MINIPORT_ATTRIBUTES_NDIS_WDM;
RegAttributes.InterfaceType = NdisInterfaceInternal;
RegAttributes.BaseIoMappingAddress = NULL;
RegAttributes.BusType = NdisBusTypeInternal;
RegAttributes.SlotNumber = 0;

Status = NdisMSetMiniportAttributes(MiniportAdapterHandle, &RegAttributes);
if (Status != NDIS_STATUS_SUCCESS) {
goto Cleanup;
}
bAdapterRegistered = TRUE;

// === 步骤5:设置NDIS通用属性 ===
NdisZeroMemory(&GenAttributes, sizeof(GenAttributes));
GenAttributes.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES;
GenAttributes.Header.Size = sizeof(GenAttributes);
GenAttributes.Header.Revision = NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES_REVISION_2;

// MAC地址设置(核心!)
GenAttributes.PermanentMacAddress = &Adapter->PermanentMacAddress;
GenAttributes.CurrentMacAddress = &Adapter->CurrentMacAddress;
GenAttributes.MacAddressLength = ETH_LENGTH_OF_ADDRESS; // 6字节

// 链路速度设置(这里决定100M/1G!)
GenAttributes.MaxXmitLinkSpeed = Adapter->LinkSpeed; // ← 从注册表或INF读取
GenAttributes.XmitLinkSpeed = Adapter->LinkSpeed;
GenAttributes.MaxRcvLinkSpeed = Adapter->LinkSpeed;
GenAttributes.RcvLinkSpeed = Adapter->LinkSpeed;

// 媒体类型和状态
GenAttributes.MediaType = NdisMedium802_3; // 以太网
GenAttributes.PhysicalMediaType = NdisPhysicalMediumUnspecified;
GenAttributes.MediaConnectState = MediaConnectStateConnected;
GenAttributes.MediaDuplexState = MediaDuplexStateFull;
GenAttributes.MtuSize = Adapter->MtuSize; // 默认1500

// 支持的OID列表(非常重要)
GenAttributes.SupportedOidList = TapSupportedOids;
GenAttributes.SupportedOidListLength = sizeof(TapSupportedOids);

Status = NdisMSetMiniportAttributes(MiniportAdapterHandle, &GenAttributes);
if (Status != NDIS_STATUS_SUCCESS) {
goto Cleanup;
}

// === 步骤6:设置硬件卸载属性 ===
// TAP不支持硬件卸载,所以填0
NdisZeroMemory(&OffloadAttributes, sizeof(OffloadAttributes));
OffloadAttributes.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_ADAPTER_OFFLOAD_ATTRIBUTES;
OffloadAttributes.Header.Size = sizeof(OffloadAttributes);
OffloadAttributes.Header.Revision = NDIS_MINIPORT_ADAPTER_OFFLOAD_ATTRIBUTES_REVISION_1;
OffloadAttributes.DefaultOffloadConfiguration = NULL;
OffloadAttributes.HardwareOffloadCapabilities = NULL;

NdisMSetMiniportAttributes(MiniportAdapterHandle, &OffloadAttributes);

// === 步骤7:创建Ring3通信设备 ===
// 这才是TAP的核心!创建用户可以打开的设备接口
Status = TapCreateDevice(Adapter, MiniportInitParameters);
if (Status != NDIS_STATUS_SUCCESS) {
DEBUGP(("[tap] TapCreateDevice failed: %x\\n", Status));
goto Cleanup;
}

// === 步骤8:设置上下文和最终状态 ===
NdisMSetMiniportContext(MiniportAdapterHandle, Adapter);
Adapter->AdapterState = TapAdapterStatePaused; // 初始为暂停状态
Adapter->bInitialized = TRUE;

DEBUGP(("[tap] TapAdapterCreate: Success, MAC: %02x-%02x-%02x-%02x-%02x-%02x\\n",
Adapter->CurrentMacAddress[0], Adapter->CurrentMacAddress[1],
Adapter->CurrentMacAddress[2], Adapter->CurrentMacAddress[3],
Adapter->CurrentMacAddress[4], Adapter->CurrentMacAddress[5]));

return NDIS_STATUS_SUCCESS;

Cleanup:
// 错误清理代码(省略)
DEBUGP(("[tap] TapAdapterCreate: Failed with %x\\n", Status));
return Status;
}

3.4 初始化流程

设备对象注册表tap0901.sysNDIS库PnP管理器设备对象注册表tap0901.sysNDIS库PnP管理器#mermaid-svg-4HCIy0c7efKJit0p{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-4HCIy0c7efKJit0p .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-4HCIy0c7efKJit0p .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-4HCIy0c7efKJit0p .error-icon{fill:#552222;}#mermaid-svg-4HCIy0c7efKJit0p .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-4HCIy0c7efKJit0p .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-4HCIy0c7efKJit0p .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-4HCIy0c7efKJit0p .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-4HCIy0c7efKJit0p .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-4HCIy0c7efKJit0p .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-4HCIy0c7efKJit0p .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-4HCIy0c7efKJit0p .marker{fill:#333333;stroke:#333333;}#mermaid-svg-4HCIy0c7efKJit0p .marker.cross{stroke:#333333;}#mermaid-svg-4HCIy0c7efKJit0p svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-4HCIy0c7efKJit0p p{margin:0;}#mermaid-svg-4HCIy0c7efKJit0p .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-4HCIy0c7efKJit0p text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-4HCIy0c7efKJit0p .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-4HCIy0c7efKJit0p .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-4HCIy0c7efKJit0p .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-4HCIy0c7efKJit0p .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-4HCIy0c7efKJit0p #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-4HCIy0c7efKJit0p .sequenceNumber{fill:white;}#mermaid-svg-4HCIy0c7efKJit0p #sequencenumber{fill:#333;}#mermaid-svg-4HCIy0c7efKJit0p #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-4HCIy0c7efKJit0p .messageText{fill:#333;stroke:none;}#mermaid-svg-4HCIy0c7efKJit0p .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-4HCIy0c7efKJit0p .labelText,#mermaid-svg-4HCIy0c7efKJit0p .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-4HCIy0c7efKJit0p .loopText,#mermaid-svg-4HCIy0c7efKJit0p .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-4HCIy0c7efKJit0p .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-4HCIy0c7efKJit0p .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-4HCIy0c7efKJit0p .noteText,#mermaid-svg-4HCIy0c7efKJit0p .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-4HCIy0c7efKJit0p .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-4HCIy0c7efKJit0p .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-4HCIy0c7efKJit0p .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-4HCIy0c7efKJit0p .actorPopupMenu{position:absolute;}#mermaid-svg-4HCIy0c7efKJit0p .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-4HCIy0c7efKJit0p .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-4HCIy0c7efKJit0p .actor-man circle,#mermaid-svg-4HCIy0c7efKJit0p line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-4HCIy0c7efKJit0p :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}网卡现在出现在网络连接面板检测到新硬件root\\tap0901调用MiniportInitializeEx(TapAdapterCreate)ExAllocatePoolWithTag分配Adapter上下文NdisAllocateSpinLock初始化锁TapReadAdapterConfiguration读取MAC、MTU、速度返回配置值NdisMSetMiniportAttributes(注册属性)NdisMSetMiniportAttributes(通用属性-含速度/MAC)TapCreateDevice创建\\\\Device\\\\TAP{GUID}IoCreateDevice创建设备对象IoCreateSymbolicLink创建符号链接设置Miniport上下文返回NDIS_STATUS_SUCCESS初始化完成


4. 内部驱动模型:Miniport、AdapterContext与NDIS注册

4.1 NDIS Miniport驱动模型

TAP-Windows V9是一个无硬件的NDIS微端口驱动。它不控制任何物理硬件,而是完全模拟网卡行为。

核心组件:

  • DriverObject:WDM驱动对象,由I/O管理器创建
  • MiniportDriverContext:驱动全局上下文(TAP_DRIVER_GLOBAL_DATA)
  • AdapterContext:每个网卡实例的上下文(TAP_ADAPTER_CONTEXT)
  • NdisMiniportHandle:NDIS分配的句柄,标识这个Miniport实例
  • 4.2 TAP_ADAPTER_CONTEXT结构

    这是驱动中最重要的数据结构,保存了所有网卡状态。

    typedef struct _TAP_ADAPTER_CONTEXT
    {
    ULONG Signature; // 签名验证,防内存损坏
    NDIS_HANDLE MiniportAdapterHandle; // NDIS分配的句柄
    NDIS_HANDLE DriverContext; // 指向驱动全局数据

    // 同步对象
    NDIS_SPIN_LOCK Lock; // 主锁
    NDIS_SPIN_LOCK SendLock; // 发送路径锁
    NDIS_SPIN_LOCK RecvLock; // 接收路径锁
    NDIS_EVENT ControlEvent; // 控制事件

    // 设备对象(Ring3通信)
    PDEVICE_OBJECT DeviceObject; // \\Device\\TAP{GUID}
    UNICODE_STRING DeviceName; // 设备名称
    UNICODE_STRING SymLinkName; // 符号链接名称

    // 网络配置
    UCHAR PermanentMacAddress[6]; // 永久MAC(来自注册表)
    UCHAR CurrentMacAddress[6]; // 当前MAC(可修改)
    ULONG MtuSize; // MTU大小
    ULONG64 LinkSpeed; // 链路速度(100M/1G)

    // 状态
    TAP_ADAPTER_STATE AdapterState; // 当前状态
    BOOLEAN bInitialized; // 初始化标志
    BOOLEAN bPaused; // 暂停状态
    BOOLEAN bSurpriseRemoved; // 意外移除

    // 队列和缓冲区
    LIST_ENTRY SendPacketQueue; // 待发送包队列
    LIST_ENTRY ReceivePacketQueue; // 待接收包队列
    ULONG QueuedSendPackets; // 队列计数

    // DHCP MASQ配置(见第14节)
    UCHAR DhcpServerMac[6]; // DHCP服务器MAC
    ULONG DhcpAddress; // DHCP分配的IP
    ULONG DhcpMask; // 子网掩码
    ULONG DhcpLeaseTime; // 租约时间
    BOOLEAN bDhcpEnabled; // DHCP MASQ启用

    // Ring3访问控制
    ULONG AllowedAccess; // 允许的访问权限
    BOOLEAN bNonAdminAllowed; // 允许非管理员

    } TAP_ADAPTER_CONTEXT, *PTAP_ADAPTER_CONTEXT;

    4.3 状态机转换

    [未初始化]
    ↓ TapAdapterCreate
    [初始化中]
    ↓ 资源分配完成
    [暂停(Paused)] ←─────────┐
    ↓ NdisMRestart │ NdisMPause
    [运行(Running)] ────────→┘
    ↓ 意外移除
    [移除中]
    ↓ 清理资源
    [已终止]


    5. IRP请求:从Ring3 to Ring0

    5.1 IRP是什么?

    IRP (I/O Request Packet) 是Windows内核中用于所有I/O操作的统一数据结构。当Ring3程序调用 CreateFile、ReadFile、WriteFile、DeviceIoControl 等API时,I/O管理器会创建相应的IRP,并发送到驱动。

    5.2 TAP驱动支持的主要IRP类型

    主功能代码对应Ring3调用TAP处理函数用途
    IRP_MJ_CREATE CreateFile TapCreateClose 打开设备
    IRP_MJ_CLOSE CloseHandle TapCreateClose 关闭设备
    IRP_MJ_READ ReadFile TapRead 从网卡读取数据包
    IRP_MJ_WRITE WriteFile TapWrite 向网卡写入数据包
    IRP_MJ_DEVICE_CONTROL DeviceIoControl TapDeviceControl 控制操作(核心)
    IRP_MJ_CLEANUP 进程终止 TapCleanup 清理进程资源
    IRP_MJ_PNP PnP管理器 TapPnpHandler PnP事件
    IRP_MJ_POWER 电源管理器 TapPowerHandler 电源管理

    5.3 IRP处理流程(以DeviceIoControl为例)

    AdapterContexttap0901.sysI/O管理器OpenVPN (Ring3)AdapterContexttap0901.sysI/O管理器OpenVPN (Ring3)#mermaid-svg-5r7lLBI56e3tYJK3{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-5r7lLBI56e3tYJK3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5r7lLBI56e3tYJK3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5r7lLBI56e3tYJK3 .error-icon{fill:#552222;}#mermaid-svg-5r7lLBI56e3tYJK3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5r7lLBI56e3tYJK3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5r7lLBI56e3tYJK3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5r7lLBI56e3tYJK3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5r7lLBI56e3tYJK3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5r7lLBI56e3tYJK3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5r7lLBI56e3tYJK3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5r7lLBI56e3tYJK3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5r7lLBI56e3tYJK3 .marker.cross{stroke:#333333;}#mermaid-svg-5r7lLBI56e3tYJK3 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5r7lLBI56e3tYJK3 p{margin:0;}#mermaid-svg-5r7lLBI56e3tYJK3 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-5r7lLBI56e3tYJK3 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-5r7lLBI56e3tYJK3 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-5r7lLBI56e3tYJK3 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-5r7lLBI56e3tYJK3 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-5r7lLBI56e3tYJK3 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-5r7lLBI56e3tYJK3 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-5r7lLBI56e3tYJK3 .sequenceNumber{fill:white;}#mermaid-svg-5r7lLBI56e3tYJK3 #sequencenumber{fill:#333;}#mermaid-svg-5r7lLBI56e3tYJK3 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-5r7lLBI56e3tYJK3 .messageText{fill:#333;stroke:none;}#mermaid-svg-5r7lLBI56e3tYJK3 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-5r7lLBI56e3tYJK3 .labelText,#mermaid-svg-5r7lLBI56e3tYJK3 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-5r7lLBI56e3tYJK3 .loopText,#mermaid-svg-5r7lLBI56e3tYJK3 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-5r7lLBI56e3tYJK3 .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-5r7lLBI56e3tYJK3 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-5r7lLBI56e3tYJK3 .noteText,#mermaid-svg-5r7lLBI56e3tYJK3 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-5r7lLBI56e3tYJK3 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-5r7lLBI56e3tYJK3 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-5r7lLBI56e3tYJK3 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-5r7lLBI56e3tYJK3 .actorPopupMenu{position:absolute;}#mermaid-svg-5r7lLBI56e3tYJK3 .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-5r7lLBI56e3tYJK3 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-5r7lLBI56e3tYJK3 .actor-man circle,#mermaid-svg-5r7lLBI56e3tYJK3 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-5r7lLBI56e3tYJK3 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}处理TAP_WIN_IOCTL_CONFIG_DHCP_MASQDeviceIoControl(hDevice,TAP_IOCTL_CONFIG_DHCP_MASQ,…)分配IRP并初始化调用DriverObject->>MajorFunction[IRP_MJ_DEVICE_CONTROL]TapDispatchDeviceControl(IRP)IoGetCurrentIrpStackLocation获取控制码switch(ControlCode)验证输入缓冲区更新Adapter->>DhcpAddress等设置bDhcpEnabled = TRUEIoCompleteRequest(IRP,IO_NO_INCREMENT)返回STATUS_SUCCESS

    5.4 IRP分发函数设置

    源码文件:device.c

    NTSTATUS
    TapCreateDevice(
    _In_ PTAP_ADAPTER_CONTEXT Adapter,
    _In_ PNDIS_MINIPORT_INIT_PARAMETERS InitParams
    )
    {
    NTSTATUS Status;
    PDEVICE_OBJECT DeviceObject = NULL;
    UNICODE_STRING DeviceName;
    UNICODE_STRING SymLinkName;
    WCHAR DeviceNameBuffer[64];
    WCHAR SymLinkNameBuffer[64];

    // 生成立即GUID名称:\\Device\\TAP\\{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}
    // 从InitParams->NetLuid或注册表读取GUID
    swprintf_s(DeviceNameBuffer, 64, L"\\\\Device\\\\TAP\\\\{%s}", Adapter->NetCfgInstanceId);
    RtlInitUnicodeString(&DeviceName, DeviceNameBuffer);

    // 创建设备对象
    Status = IoCreateDevice(
    Adapter->DriverObject, // DriverObject
    0, // DeviceExtension大小(使用Adapter)
    &DeviceName, // 设备名称
    FILE_DEVICE_UNKNOWN, // 设备类型
    FILE_DEVICE_SECURE_OPEN, // 特性
    FALSE, // 独占?NO
    &DeviceObject // 返回设备对象
    );

    if (!NT_SUCCESS(Status)) {
    return Status;
    }

    // 设置标志 – 支持缓冲和直接I/O
    DeviceObject->Flags |= DO_DIRECT_IO; // 使用MDL进行读写
    DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;

    // 存储Adapter到DeviceExtension
    DeviceObject->DeviceExtension = Adapter;
    Adapter->DeviceObject = DeviceObject;

    // === 设置IRP分发函数(核心!) ===
    DeviceObject->MajorFunction[IRP_MJ_CREATE] = TapCreateClose;
    DeviceObject->MajorFunction[IRP_MJ_CLOSE] = TapCreateClose;
    DeviceObject->MajorFunction[IRP_MJ_READ] = TapRead;
    DeviceObject->MajorFunction[IRP_MJ_WRITE] = TapWrite;
    DeviceObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = TapDeviceControl;
    DeviceObject->MajorFunction[IRP_MJ_CLEANUP] = TapCleanup;
    DeviceObject->MajorFunction[IRP_MJ_PNP] = TapPnpHandler;
    DeviceObject->MajorFunction[IRP_MJ_POWER] = TapPowerHandler;

    // 创建符号链接:\\DosDevices\\Global\\{GUID}.tap
    swprintf_s(SymLinkNameBuffer, 64, L"\\\\DosDevices\\\\Global\\\\{%s}.tap",
    Adapter->NetCfgInstanceId);
    RtlInitUnicodeString(&SymLinkName, SymLinkNameBuffer);

    Status = IoCreateSymbolicLink(&SymLinkName, &DeviceName);
    if (!NT_SUCCESS(Status)) {
    IoDeleteDevice(DeviceObject);
    return Status;
    }

    // 保存符号链接名用于清理
    Adapter->SymLinkName = SymLinkName;

    return STATUS_SUCCESS;
    }


    6. DeviceIoControl怎么处理来自客户端的调用?

    6.1 支持的IOCTL控制码

    TAP驱动定义了十几个IOCTL控制码,用于配置和控制虚拟网卡。这些控制码在 tapioctl.h 中定义。

    核心控制码:

    // tapioctl.h 摘录
    #define TAP_WIN_IOCTL_GET_MAC CTL_CODE(FILE_DEVICE_UNKNOWN, 1, METHOD_BUFFERED, FILE_ANY_ACCESS)
    #define TAP_WIN_IOCTL_GET_VERSION CTL_CODE(FILE_DEVICE_UNKNOWN, 2, METHOD_BUFFERED, FILE_ANY_ACCESS)
    #define TAP_WIN_IOCTL_GET_MTU CTL_CODE(FILE_DEVICE_UNKNOWN, 3, METHOD_BUFFERED, FILE_ANY_ACCESS)
    #define TAP_WIN_IOCTL_GET_INFO CTL_CODE(FILE_DEVICE_UNKNOWN, 4, METHOD_BUFFERED, FILE_ANY_ACCESS)
    #define TAP_WIN_IOCTL_CONFIG_POINT_TO_POINT CTL_CODE(FILE_DEVICE_UNKNOWN, 5, METHOD_BUFFERED, FILE_ANY_ACCESS)
    #define TAP_WIN_IOCTL_SET_MEDIA_STATUS CTL_CODE(FILE_DEVICE_UNKNOWN, 6, METHOD_BUFFERED, FILE_ANY_ACCESS)
    #define TAP_WIN_IOCTL_CONFIG_DHCP_MASQ CTL_CODE(FILE_DEVICE_UNKNOWN, 7, METHOD_BUFFERED, FILE_ANY_ACCESS)
    #define TAP_WIN_IOCTL_GET_LOG_LINE CTL_CODE(FILE_DEVICE_UNKNOWN, 8, METHOD_BUFFERED, FILE_ANY_ACCESS)
    #define TAP_WIN_IOCTL_CONFIG_DHCP_SET_OPT CTL_CODE(FILE_DEVICE_UNKNOWN, 9, METHOD_BUFFERED, FILE_ANY_ACCESS)
    #define TAP_WIN_IOCTL_CONFIG_TUN CTL_CODE(FILE_DEVICE_UNKNOWN, 10, METHOD_BUFFERED, FILE_ANY_ACCESS)

    6.2 TapDeviceControl实现

    源码文件:device.c

    NTSTATUS
    TapDeviceControl(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ PIRP Irp
    )
    {
    PTAP_ADAPTER_CONTEXT Adapter = (PTAP_ADAPTER_CONTEXT)DeviceObject->DeviceExtension;
    PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
    NTSTATUS Status = STATUS_SUCCESS;
    ULONG ControlCode = IrpSp->Parameters.DeviceIoControl.IoControlCode;
    ULONG InputBufferLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
    ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
    PVOID InputBuffer = NULL;
    PVOID OutputBuffer = Irp->AssociatedIrp.SystemBuffer; // METHOD_BUFFERED
    ULONG BytesReturned = 0;

    DEBUGP(("[tap] TapDeviceControl: Code 0x%08x\\n", ControlCode));

    // 验证适配器状态
    if (Adapter->bSurpriseRemoved) {
    Status = STATUS_DEVICE_NOT_CONNECTED;
    goto Complete;
    }

    // 根据方法类型获取输入缓冲区
    if (ControlCode & METHOD_BUFFERED) {
    InputBuffer = Irp->AssociatedIrp.SystemBuffer;
    } else if ((ControlCode & METHOD_MASK) == METHOD_NEITHER) {
    InputBuffer = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
    }

    // === 核心switch:处理不同控制码 ===
    switch (ControlCode) {

    // 获取版本信息
    case TAP_WIN_IOCTL_GET_VERSION:
    {
    PTAP_VERSION pVersion = (PTAP_VERSION)OutputBuffer;
    if (OutputBufferLength < sizeof(TAP_VERSION)) {
    Status = STATUS_BUFFER_TOO_SMALL;
    break;
    }
    pVersion->Major = TAP_DRIVER_MAJOR_VERSION; // 9
    pVersion->Minor = TAP_DRIVER_MINOR_VERSION; // 24
    pVersion->Debug = 0;
    BytesReturned = sizeof(TAP_VERSION);
    Status = STATUS_SUCCESS;
    break;
    }

    // 获取MAC地址
    case TAP_WIN_IOCTL_GET_MAC:
    {
    if (OutputBufferLength >= ETH_LENGTH_OF_ADDRESS) {
    NdisCopyMemory(OutputBuffer, Adapter->CurrentMacAddress, ETH_LENGTH_OF_ADDRESS);
    BytesReturned = ETH_LENGTH_OF_ADDRESS;
    Status = STATUS_SUCCESS;
    } else {
    Status = STATUS_BUFFER_TOO_SMALL;
    }
    break;
    }

    // 获取MTU
    case TAP_WIN_IOCTL_GET_MTU:
    {
    if (OutputBufferLength >= sizeof(ULONG)) {
    *(PULONG)OutputBuffer = Adapter->MtuSize;
    BytesReturned = sizeof(ULONG);
    Status = STATUS_SUCCESS;
    } else {
    Status = STATUS_BUFFER_TOO_SMALL;
    }
    break;
    }

    // === 关键:设置媒体状态 ===
    // OpenVPN调用这个IOCTL来"连接"或"断开"网卡
    case TAP_WIN_IOCTL_SET_MEDIA_STATUS:
    {
    PULONG pMediaStatus = (PULONG)InputBuffer;
    if (InputBufferLength >= sizeof(ULONG)) {
    BOOLEAN bConnected = (*pMediaStatus != 0);
    TapSetMediaStatus(Adapter, bConnected);
    Status = STATUS_SUCCESS;
    } else {
    Status = STATUS_INVALID_PARAMETER;
    }
    break;
    }

    // === 核心:配置DHCP MASQ ===
    // 这是实现"自问自答"的关键(见第14节)
    case TAP_WIN_IOCTL_CONFIG_DHCP_MASQ:
    {
    PTAP_DHCP_CONFIG pDhcpConfig = (PTAP_DHCP_CONFIG)InputBuffer;
    if (InputBufferLength >= sizeof(TAP_DHCP_CONFIG)) {
    // 保存DHCP配置到Adapter上下文
    Adapter->DhcpAddress = pDhcpConfig->AdapterAddress;
    Adapter->DhcpMask = pDhcpConfig->NetworkMask;
    Adapter->DhcpLeaseTime = pDhcpConfig->LeaseTime;
    Adapter->bDhcpEnabled = TRUE;

    // 生成DHCP服务器MAC (00-FF-…)
    TapGenerateDhcpServerMac(Adapter);

    DEBUGP(("[tap] DHCP MASQ configured: IP %d.%d.%d.%d, Mask %d.%d.%d.%d\\n",
    (Adapter->DhcpAddress >> 0) & 0xFF,
    (Adapter->DhcpAddress >> 8) & 0xFF,
    (Adapter->DhcpAddress >> 16) & 0xFF,
    (Adapter->DhcpAddress >> 24) & 0xFF,
    (Adapter->DhcpMask >> 0) & 0xFF,
    (Adapter->DhcpMask >> 8) & 0xFF,
    (Adapter->DhcpMask >> 16) & 0xFF,
    (Adapter->DhcpMask >> 24) & 0xFF));

    Status = STATUS_SUCCESS;
    } else {
    Status = STATUS_INVALID_PARAMETER;
    }
    break;
    }

    // 配置TUN模式(点对点)
    case TAP_WIN_IOCTL_CONFIG_POINT_TO_POINT:
    case TAP_WIN_IOCTL_CONFIG_TUN:
    {
    // 配置TUN模式,处理IP包而非以太网帧
    PTAP_TUN_CONFIG pTunConfig = (PTAP_TUN_CONFIG)InputBuffer;
    if (InputBufferLength >= sizeof(TAP_TUN_CONFIG)) {
    Adapter->bTunMode = TRUE;
    Adapter->TunLocalIp = pTunConfig->LocalIp;
    Adapter->TunRemoteIp = pTunConfig->RemoteIp;
    Status = STATUS_SUCCESS;
    } else {
    Status = STATUS_INVALID_PARAMETER;
    }
    break;
    }

    // 默认:不支持的IOCTL
    default:
    Status = STATUS_INVALID_DEVICE_REQUEST;
    break;
    }

    Complete:
    // 完成IRP请求
    Irp->IoStatus.Status = Status;
    Irp->IoStatus.Information = BytesReturned;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);

    return Status;
    }


    7. 怎么注册上能接受来自RING3的驱动处理?

    7.1 设备对象与符号链接

    Ring3程序无法直接通过名称如 \\Device\\TAP\\{GUID} 访问设备,因为这是内核对象命名空间。Windows提供了符号链接机制,将内核设备名称映射到DOS设备名称空间(如 \\\\.\\Global\\{GUID}.tap)。

    7.2 注册流程

    Ring3访问注册流程

    #mermaid-svg-SoJZX7t0Tln50WwB{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-SoJZX7t0Tln50WwB .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-SoJZX7t0Tln50WwB .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-SoJZX7t0Tln50WwB .error-icon{fill:#552222;}#mermaid-svg-SoJZX7t0Tln50WwB .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-SoJZX7t0Tln50WwB .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-SoJZX7t0Tln50WwB .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-SoJZX7t0Tln50WwB .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-SoJZX7t0Tln50WwB .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-SoJZX7t0Tln50WwB .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-SoJZX7t0Tln50WwB .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-SoJZX7t0Tln50WwB .marker{fill:#333333;stroke:#333333;}#mermaid-svg-SoJZX7t0Tln50WwB .marker.cross{stroke:#333333;}#mermaid-svg-SoJZX7t0Tln50WwB svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-SoJZX7t0Tln50WwB p{margin:0;}#mermaid-svg-SoJZX7t0Tln50WwB .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-SoJZX7t0Tln50WwB .cluster-label text{fill:#333;}#mermaid-svg-SoJZX7t0Tln50WwB .cluster-label span{color:#333;}#mermaid-svg-SoJZX7t0Tln50WwB .cluster-label span p{background-color:transparent;}#mermaid-svg-SoJZX7t0Tln50WwB .label text,#mermaid-svg-SoJZX7t0Tln50WwB span{fill:#333;color:#333;}#mermaid-svg-SoJZX7t0Tln50WwB .node rect,#mermaid-svg-SoJZX7t0Tln50WwB .node circle,#mermaid-svg-SoJZX7t0Tln50WwB .node ellipse,#mermaid-svg-SoJZX7t0Tln50WwB .node polygon,#mermaid-svg-SoJZX7t0Tln50WwB .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-SoJZX7t0Tln50WwB .rough-node .label text,#mermaid-svg-SoJZX7t0Tln50WwB .node .label text,#mermaid-svg-SoJZX7t0Tln50WwB .image-shape .label,#mermaid-svg-SoJZX7t0Tln50WwB .icon-shape .label{text-anchor:middle;}#mermaid-svg-SoJZX7t0Tln50WwB .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-SoJZX7t0Tln50WwB .rough-node .label,#mermaid-svg-SoJZX7t0Tln50WwB .node .label,#mermaid-svg-SoJZX7t0Tln50WwB .image-shape .label,#mermaid-svg-SoJZX7t0Tln50WwB .icon-shape .label{text-align:center;}#mermaid-svg-SoJZX7t0Tln50WwB .node.clickable{cursor:pointer;}#mermaid-svg-SoJZX7t0Tln50WwB .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-SoJZX7t0Tln50WwB .arrowheadPath{fill:#333333;}#mermaid-svg-SoJZX7t0Tln50WwB .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-SoJZX7t0Tln50WwB .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-SoJZX7t0Tln50WwB .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-SoJZX7t0Tln50WwB .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-SoJZX7t0Tln50WwB .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-SoJZX7t0Tln50WwB .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-SoJZX7t0Tln50WwB .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-SoJZX7t0Tln50WwB .cluster text{fill:#333;}#mermaid-svg-SoJZX7t0Tln50WwB .cluster span{color:#333;}#mermaid-svg-SoJZX7t0Tln50WwB 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-SoJZX7t0Tln50WwB .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-SoJZX7t0Tln50WwB rect.text{fill:none;stroke-width:0;}#mermaid-svg-SoJZX7t0Tln50WwB .icon-shape,#mermaid-svg-SoJZX7t0Tln50WwB .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-SoJZX7t0Tln50WwB .icon-shape p,#mermaid-svg-SoJZX7t0Tln50WwB .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-SoJZX7t0Tln50WwB .icon-shape rect,#mermaid-svg-SoJZX7t0Tln50WwB .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-SoJZX7t0Tln50WwB .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-SoJZX7t0Tln50WwB .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-SoJZX7t0Tln50WwB :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    后续操作

    Ring3程序打开设备

    驱动加载时

    IoCreateDevice

    创建Device对象\\Device\\TAP{GUID}

    IoCreateSymbolicLink创建\\DosDevices\\Global{GUID}.tap

    设置MajorFunction数组注册所有IRP处理函数

    CreateFile''\\.\\Global{GUID}.tap''

    对象管理器解析符号链接

    定位到\\Device\\TAP{GUID}

    I/O管理器创建IRP_MJ_CREATE

    调用驱动TapCreateClose

    返回句柄给Ring3

    DeviceIoControl/ReadFile/WriteFile通过句柄调用

    驱动分发函数处理

    7.3 访问控制与安全性

    TAP驱动实现了精细的访问控制,通过INF中的 AllowNonAdmin 参数控制:

    NTSTATUS
    TapCreateClose(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ PIRP Irp
    )
    {
    PTAP_ADAPTER_CONTEXT Adapter = (PTAP_ADAPTER_CONTEXT)DeviceObject->DeviceExtension;
    PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
    NTSTATUS Status = STATUS_SUCCESS;

    if (IrpSp->MajorFunction == IRP_MJ_CREATE) {
    // 检查是否是管理员请求
    BOOLEAN bIsAdmin = SeSinglePrivilegeCheck(SeExRestorePrivilege, Irp->RequestorMode);

    // 如果要求非管理员禁止访问且当前不是管理员,则拒绝
    if (!Adapter->bNonAdminAllowed && !bIsAdmin) {
    Status = STATUS_ACCESS_DENIED;
    DEBUGP(("[tap] Access denied for non-admin user\\n"));
    } else {
    // 允许访问,增加打开计数
    InterlockedIncrement(&Adapter->OpenCount);
    DEBUGP(("[tap] Device opened, count=%d\\n", Adapter->OpenCount));
    }
    } else { // IRP_MJ_CLOSE
    InterlockedDecrement(&Adapter->OpenCount);
    DEBUGP(("[tap] Device closed, count=%d\\n", Adapter->OpenCount));
    }

    Irp->IoStatus.Status = Status;
    Irp->IoStatus.Information = 0;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
    return Status;
    }


    8. OID是什么?

    8.1 OID基本概念

    OID (Object Identifier) 是NDIS中用于查询和设置网络适配器属性的统一接口。可以理解为网卡的"属性字典"——上层协议驱动(如TCP/IP)通过OID来获取或设置网卡的各种参数。

    OID分类:

    • 通用OID (OID_GEN_*):适用于所有网卡,如链路速度、MAC地址
    • 802.3 OID (OID_802_3_*):专用于以太网,如多播地址列表
    • 统计OID (OID_GEN_STATISTICS_*):获取发送/接收统计
    • PnP OID (OID_PNP_*):电源管理和PnP事件

    8.2 TAP支持的OID列表

    // adapter.c 中定义的SupportedOids数组
    NDIS_OID TapSupportedOids[] =
    {
    // 通用OID
    OID_GEN_SUPPORTED_LIST,
    OID_GEN_HARDWARE_STATUS,
    OID_GEN_MEDIA_SUPPORTED,
    OID_GEN_MEDIA_IN_USE,
    OID_GEN_MAXIMUM_LOOKAHEAD,
    OID_GEN_MAXIMUM_FRAME_SIZE,
    OID_GEN_LINK_SPEED, // ← 链路速度查询
    OID_GEN_TRANSMIT_BUFFER_SPACE,
    OID_GEN_RECEIVE_BUFFER_SPACE,
    OID_GEN_TRANSMIT_BLOCK_SIZE,
    OID_GEN_RECEIVE_BLOCK_SIZE,
    OID_GEN_VENDOR_ID,
    OID_GEN_VENDOR_DESCRIPTION, // ← "TAP-Windows Adapter V9"
    OID_GEN_VENDOR_DRIVER_VERSION,
    OID_GEN_CURRENT_PACKET_FILTER, // ← 包过滤(接收哪些包)
    OID_GEN_MAXIMUM_TOTAL_SIZE,
    OID_GEN_PROTOCOL_OPTIONS,
    OID_GEN_MAC_OPTIONS,
    OID_GEN_MEDIA_CONNECT_STATUS, // ← 连接状态
    OID_GEN_MAXIMUM_SEND_PACKETS,
    OID_GEN_XMIT_OK,
    OID_GEN_RCV_OK,
    OID_GEN_STATISTICS,

    // 802.3特定OID
    OID_802_3_PERMANENT_ADDRESS, // ← 永久MAC地址
    OID_802_3_CURRENT_ADDRESS, // ← 当前MAC地址
    OID_802_3_MULTICAST_LIST,
    OID_802_3_MAXIMUM_LIST_SIZE,

    // PnP OID
    OID_PNP_CAPABILITIES,
    OID_PNP_SET_POWER,
    OID_PNP_QUERY_POWER,
    OID_PNP_ADD_WAKE_UP_PATTERN,
    OID_PNP_REMOVE_WAKE_UP_PATTERN,
    OID_PNP_ENABLE_WAKE_UP,

    // NDIS 6.0+ OID
    OID_GEN_PHYSICAL_MEDIUM,
    OID_GEN_LINK_STATE,
    OID_GEN_RECEIVE_FILTER_HARDWARE_CAPABILITIES,
    };

    8.3 OID请求处理实现

    源码文件:oid.c

    NDIS_STATUS
    TapAdapterOidRequest(
    _In_ NDIS_HANDLE MiniportAdapterContext,
    _In_ PNDIS_OID_REQUEST NdisRequest
    )
    {
    PTAP_ADAPTER_CONTEXT Adapter = (PTAP_ADAPTER_CONTEXT)MiniportAdapterContext;
    NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
    NDIS_OID Oid = NdisRequest->DATA.QUERY_INFORMATION.Oid;

    DEBUGP(("[tap] OidRequest: Oid=0x%08x, Type=%s\\n", Oid,
    (NdisRequest->RequestType == NdisRequestQueryInformation) ? "Query" :
    (NdisRequest->RequestType == NdisRequestSetInformation) ? "Set" : "Other"));

    // 根据请求类型分发
    switch (NdisRequest->RequestType)
    {
    case NdisRequestQueryInformation:
    case NdisRequestQueryStatistics:
    Status = TapQueryInformation(Adapter, NdisRequest);
    break;

    case NdisRequestSetInformation:
    Status = TapSetInformation(Adapter, NdisRequest);
    break;

    default:
    Status = NDIS_STATUS_NOT_SUPPORTED;
    break;
    }

    // 完成请求(NDIS 6要求调用NdisMOidRequestComplete)
    NdisMOidRequestComplete(Adapter->MiniportAdapterHandle, NdisRequest, Status);
    return Status;
    }

    // 查询处理
    NDIS_STATUS
    TapQueryInformation(
    _In_ PTAP_ADAPTER_CONTEXT Adapter,
    _In_ PNDIS_OID_REQUEST NdisRequest
    )
    {
    NDIS_OID Oid = NdisRequest->DATA.QUERY_INFORMATION.Oid;
    PVOID InfoBuffer = NdisRequest->DATA.QUERY_INFORMATION.InformationBuffer;
    ULONG InfoBufferLength = NdisRequest->DATA.QUERY_INFORMATION.InformationBufferLength;
    PULONG BytesWritten = &NdisRequest->DATA.QUERY_INFORMATION.BytesWritten;
    PULONG BytesNeeded = &NdisRequest->DATA.QUERY_INFORMATION.BytesNeeded;

    ULONG RequiredSize = 0;
    NDIS_STATUS Status = NDIS_STATUS_SUCCESS;

    *BytesWritten = 0;
    *BytesNeeded = 0;

    // 根据OID类型处理
    switch (Oid)
    {
    // === MAC地址相关 ===
    case OID_802_3_PERMANENT_ADDRESS:
    case OID_802_3_CURRENT_ADDRESS:
    RequiredSize = ETH_LENGTH_OF_ADDRESS;
    if (InfoBufferLength >= RequiredSize) {
    NdisMoveMemory(InfoBuffer, Adapter->CurrentMacAddress, RequiredSize);
    *BytesWritten = RequiredSize;
    } else {
    *BytesNeeded = RequiredSize;
    Status = NDIS_STATUS_BUFFER_TOO_SHORT;
    }
    break;

    // === 链路速度(100M/1G!) ===
    case OID_GEN_LINK_SPEED:
    RequiredSize = sizeof(ULONG);
    if (InfoBufferLength >= RequiredSize) {
    // 注意:NDIS速度单位是100bps,所以100Mbps = 1000000
    // TAP_XMIT_SPEED定义在constants.h
    *(PULONG)InfoBuffer = (ULONG)(Adapter->LinkSpeed / 100);
    *BytesWritten = RequiredSize;
    } else {
    *BytesNeeded = RequiredSize;
    Status = NDIS_STATUS_BUFFER_TOO_SHORT;
    }
    break;

    // === 供应商描述(INF中配置) ===
    case OID_GEN_VENDOR_DESCRIPTION:
    {
    PCHAR Desc = "TAP-Windows Adapter V9";
    RequiredSize = (ULONG)strlen(Desc) + 1;
    if (InfoBufferLength >= RequiredSize) {
    NdisMoveMemory(InfoBuffer, Desc, RequiredSize);
    *BytesWritten = RequiredSize;
    } else {
    *BytesNeeded = RequiredSize;
    Status = NDIS_STATUS_BUFFER_TOO_SHORT;
    }
    break;
    }

    // === 最大帧大小(MTU) ===
    case OID_GEN_MAXIMUM_FRAME_SIZE:
    case OID_GEN_MAXIMUM_TOTAL_SIZE:
    RequiredSize = sizeof(ULONG);
    if (InfoBufferLength >= RequiredSize) {
    *(PULONG)InfoBuffer = Adapter->MtuSize;
    *BytesWritten = RequiredSize;
    } else {
    *BytesNeeded = RequiredSize;
    Status = NDIS_STATUS_BUFFER_TOO_SHORT;
    }
    break;

    // === 媒体连接状态 ===
    case OID_GEN_MEDIA_CONNECT_STATUS:
    RequiredSize = sizeof(ULONG);
    if (InfoBufferLength >= RequiredSize) {
    // 返回NdisMediaStateConnected或NdisMediaStateDisconnected
    *(PULONG)InfoBuffer = Adapter->bConnected ?
    NdisMediaStateConnected : NdisMediaStateDisconnected;
    *BytesWritten = RequiredSize;
    } else {
    *BytesNeeded = RequiredSize;
    Status = NDIS_STATUS_BUFFER_TOO_SHORT;
    }
    break;

    // === 支持的OID列表 ===
    case OID_GEN_SUPPORTED_LIST:
    RequiredSize = sizeof(TapSupportedOids);
    if (InfoBufferLength >= RequiredSize) {
    NdisMoveMemory(InfoBuffer, TapSupportedOids, RequiredSize);
    *BytesWritten = RequiredSize;
    } else {
    *BytesNeeded = RequiredSize;
    Status = NDIS_STATUS_BUFFER_TOO_SHORT;
    }
    break;

    // === 包过滤(重要!) ===
    case OID_GEN_CURRENT_PACKET_FILTER:
    RequiredSize = sizeof(ULONG);
    if (InfoBufferLength >= RequiredSize) {
    *(PULONG)InfoBuffer = Adapter->PacketFilter;
    *BytesWritten = RequiredSize;
    } else {
    *BytesNeeded = RequiredSize;
    Status = NDIS_STATUS_BUFFER_TOO_SHORT;
    }
    break;

    // === 统计信息 ===
    case OID_GEN_STATISTICS:
    {
    NDIS_STATISTICS_INFO Stats;
    RequiredSize = sizeof(NDIS_STATISTICS_INFO);
    if (InfoBufferLength >= RequiredSize) {
    NdisZeroMemory(&Stats, RequiredSize);
    Stats.Header.Type = NDIS_OBJECT_TYPE_STATISTICS_INFO;
    Stats.Header.Size = RequiredSize;
    Stats.Header.Revision = NDIS_STATISTICS_INFO_REVISION_1;

    Stats.IfInOctets = Adapter->Stats.IfInOctets;
    Stats.IfOutOctets = Adapter->Stats.IfOutOctets;
    Stats.IfInErrors = Adapter->Stats.IfInErrors;
    Stats.IfOutErrors = Adapter->Stats.IfOutErrors;

    NdisMoveMemory(InfoBuffer, &Stats, RequiredSize);
    *BytesWritten = RequiredSize;
    } else {
    *BytesNeeded = RequiredSize;
    Status = NDIS_STATUS_BUFFER_TOO_SHORT;
    }
    break;
    }

    default:
    Status = NDIS_STATUS_NOT_SUPPORTED;
    break;
    }

    return Status;
    }

    // 设置处理
    NDIS_STATUS
    TapSetInformation(
    _In_ PTAP_ADAPTER_CONTEXT Adapter,
    _In_ PNDIS_OID_REQUEST NdisRequest
    )
    {
    NDIS_OID Oid = NdisRequest->DATA.SET_INFORMATION.Oid;
    PVOID InfoBuffer = NdisRequest->DATA.SET_INFORMATION.InformationBuffer;
    ULONG InfoBufferLength = NdisRequest->DATA.SET_INFORMATION.InformationBufferLength;
    PULONG BytesRead = &NdisRequest->DATA.SET_INFORMATION.BytesRead;
    PULONG BytesNeeded = &NdisRequest->DATA.SET_INFORMATION.BytesNeeded;

    NDIS_STATUS Status = NDIS_STATUS_SUCCESS;

    *BytesRead = 0;
    *BytesNeeded = 0;

    switch (Oid)
    {
    // 设置当前MAC地址
    case OID_802_3_CURRENT_ADDRESS:
    if (InfoBufferLength >= ETH_LENGTH_OF_ADDRESS) {
    NdisMoveMemory(Adapter->CurrentMacAddress, InfoBuffer, ETH_LENGTH_OF_ADDRESS);
    *BytesRead = ETH_LENGTH_OF_ADDRESS;

    // 更新注册表持久化
    TapWriteMacAddressToRegistry(Adapter);

    DEBUGP(("[tap] MAC address changed to %02x-%02x-%02x-%02x-%02x-%02x\\n",
    Adapter->CurrentMacAddress[0], Adapter->CurrentMacAddress[1],
    Adapter->CurrentMacAddress[2], Adapter->CurrentMacAddress[3],
    Adapter->CurrentMacAddress[4], Adapter->CurrentMacAddress[5]));
    } else {
    *BytesNeeded = ETH_LENGTH_OF_ADDRESS;
    Status = NDIS_STATUS_BUFFER_TOO_SHORT;
    }
    break;

    // 设置包过滤(协议栈告诉我们要接收哪些包)
    case OID_GEN_CURRENT_PACKET_FILTER:
    if (InfoBufferLength >= sizeof(ULONG)) {
    Adapter->PacketFilter = *(PULONG)InfoBuffer;
    *BytesRead = sizeof(ULONG);

    DEBUGP(("[tap] Packet filter set to 0x%08x\\n", Adapter->PacketFilter));

    // 如果设置了定向包过滤,可能需要更新接收逻辑
    if (Adapter->PacketFilter & NDIS_PACKET_TYPE_DIRECTED) {
    // 准备接收发往本机MAC的包
    }
    } else {
    *BytesNeeded = sizeof(ULONG);
    Status = NDIS_STATUS_BUFFER_TOO_SHORT;
    }
    break;

    // 电源状态设置
    case OID_PNP_SET_POWER:
    // 处理D0/D3状态转换
    // …(代码省略)
    break;

    default:
    Status = NDIS_STATUS_NOT_SUPPORTED;
    break;
    }

    return Status;
    }

    8.4 OID查询流程

    AdapterContexttap0901.sysNDIS库TCP/IP协议栈AdapterContexttap0901.sysNDIS库TCP/IP协议栈#mermaid-svg-MrI6j93BvTQ6RijW{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-MrI6j93BvTQ6RijW .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-MrI6j93BvTQ6RijW .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-MrI6j93BvTQ6RijW .error-icon{fill:#552222;}#mermaid-svg-MrI6j93BvTQ6RijW .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-MrI6j93BvTQ6RijW .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-MrI6j93BvTQ6RijW .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-MrI6j93BvTQ6RijW .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-MrI6j93BvTQ6RijW .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-MrI6j93BvTQ6RijW .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-MrI6j93BvTQ6RijW .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-MrI6j93BvTQ6RijW .marker{fill:#333333;stroke:#333333;}#mermaid-svg-MrI6j93BvTQ6RijW .marker.cross{stroke:#333333;}#mermaid-svg-MrI6j93BvTQ6RijW svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-MrI6j93BvTQ6RijW p{margin:0;}#mermaid-svg-MrI6j93BvTQ6RijW .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-MrI6j93BvTQ6RijW text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-MrI6j93BvTQ6RijW .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-MrI6j93BvTQ6RijW .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-MrI6j93BvTQ6RijW .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-MrI6j93BvTQ6RijW .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-MrI6j93BvTQ6RijW #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-MrI6j93BvTQ6RijW .sequenceNumber{fill:white;}#mermaid-svg-MrI6j93BvTQ6RijW #sequencenumber{fill:#333;}#mermaid-svg-MrI6j93BvTQ6RijW #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-MrI6j93BvTQ6RijW .messageText{fill:#333;stroke:none;}#mermaid-svg-MrI6j93BvTQ6RijW .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-MrI6j93BvTQ6RijW .labelText,#mermaid-svg-MrI6j93BvTQ6RijW .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-MrI6j93BvTQ6RijW .loopText,#mermaid-svg-MrI6j93BvTQ6RijW .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-MrI6j93BvTQ6RijW .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-MrI6j93BvTQ6RijW .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-MrI6j93BvTQ6RijW .noteText,#mermaid-svg-MrI6j93BvTQ6RijW .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-MrI6j93BvTQ6RijW .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-MrI6j93BvTQ6RijW .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-MrI6j93BvTQ6RijW .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-MrI6j93BvTQ6RijW .actorPopupMenu{position:absolute;}#mermaid-svg-MrI6j93BvTQ6RijW .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-MrI6j93BvTQ6RijW .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-MrI6j93BvTQ6RijW .actor-man circle,#mermaid-svg-MrI6j93BvTQ6RijW line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-MrI6j93BvTQ6RijW :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}ipconfig显示速度NdisOidRequest (OID_GEN_LINK_SPEED)TapAdapterOidRequest识别RequestType=Queryswitch(OID) ->> OID_GEN_LINK_SPEED读取Adapter->>LinkSpeed返回100000000 (100Mbps)转换为NDIS单位(1000)NdisMOidRequestComplete返回速度值


    9. MAC地址:来源、处理、修改与常见问题

    9.1 MAC地址的来源

    TAP驱动的MAC地址来自三个可能的地方:

  • INF文件默认值(如果指定了NetworkAddress)
  • 随机生成:如果INF未指定,驱动会生成一个随机的本地管理地址
  • 注册表设置:用户修改后保存在注册表中
  • 9.2 MAC地址生成算法

    VOID
    TapGenerateRandomMacAddress(
    _Out_ PUCHAR MacAddress
    )
    {
    // 生成符合规范的MAC地址
    // 字节0: 设置为0x00(本地管理,单播)
    // 字节1-5: 随机数

    LARGE_INTEGER TickCount;
    ULONG RandomSeed;
    ULONG i;

    // 获取随机种子
    KeQueryTickCount(&TickCount);
    RandomSeed = (ULONG)(TickCount.QuadPart & 0xFFFFFFFF);

    // 伪随机生成
    for (i = 0; i < ETH_LENGTH_OF_ADDRESS; i++) {
    RandomSeed = (RandomSeed * 1103515245 + 12345) & 0x7FFFFFFF;
    MacAddress[i] = (UCHAR)(RandomSeed & 0xFF);
    }

    // 确保是本地管理地址且不是多播
    MacAddress[0] &= 0xFC; // 清除多播位(bit0)和全局位(bit1)
    MacAddress[0] |= 0x02; // 设置为本地管理地址(bit1=1)

    DEBUGP(("[tap] Generated random MAC: %02x-%02x-%02x-%02x-%02x-%02x\\n",
    MacAddress[0], MacAddress[1], MacAddress[2],
    MacAddress[3], MacAddress[4], MacAddress[5]));
    }

    9.3 从注册表读取MAC地址

    NDIS_STATUS
    TapReadAdapterConfiguration(
    _In_ PTAP_ADAPTER_CONTEXT Adapter,
    _In_ PNDIS_MINIPORT_INIT_PARAMETERS InitParams
    )
    {
    NDIS_STATUS Status;
    HANDLE RegistryHandle;
    ULONG ValueType;
    UCHAR MacBuffer[ETH_LENGTH_OF_ADDRESS];
    ULONG ValueSize;

    // 打开适配器实例的注册表键
    Status = NdisMOpenConfigurationParameterArray(
    Adapter->MiniportAdapterHandle,
    &RegistryHandle,
    InitParams->ConfigParameters
    );

    if (Status != NDIS_STATUS_SUCCESS) {
    return Status;
    }

    // === 读取MAC地址 ===
    ValueSize = sizeof(MacBuffer);
    Status = NdisReadConfiguration(
    RegistryHandle,
    &ValueType,
    &MacBuffer,
    &ValueSize,
    NDIS_STRING_CONST("NetworkAddress")
    );

    if (Status == NDIS_STATUS_SUCCESS && ValueSize == ETH_LENGTH_OF_ADDRESS) {
    // 使用注册表中的MAC
    NdisMoveMemory(Adapter->PermanentMacAddress, MacBuffer, ETH_LENGTH_OF_ADDRESS);
    } else {
    // 生成随机MAC
    TapGenerateRandomMacAddress(Adapter->PermanentMacAddress);

    // 将生成的MAC写回注册表
    NdisWriteConfiguration(
    RegistryHandle,
    NDIS_STRING_CONST("NetworkAddress"),
    REG_BINARY,
    Adapter->PermanentMacAddress,
    ETH_LENGTH_OF_ADDRESS
    );
    }

    // 初始化当前MAC为永久MAC
    NdisMoveMemory(Adapter->CurrentMacAddress, Adapter->PermanentMacAddress, ETH_LENGTH_OF_ADDRESS);

    // === 读取MTU ===
    ValueSize = sizeof(Adapter->MtuSize);
    Status = NdisReadConfiguration(
    RegistryHandle,
    &ValueType,
    &Adapter->MtuSize,
    &ValueSize,
    NDIS_STRING_CONST("MTU")
    );

    if (Status != NDIS_STATUS_SUCCESS || Adapter->MtuSize < 576 || Adapter->MtuSize > 65535) {
    Adapter->MtuSize = 1500; // 默认值
    }

    // === 读取链路速度 ===
    ValueSize = sizeof(Adapter->LinkSpeed);
    Status = NdisReadConfiguration(
    RegistryHandle,
    &ValueType,
    &Adapter->LinkSpeed,
    &ValueSize,
    NDIS_STRING_CONST("LinkSpeed")
    );

    if (Status != NDIS_STATUS_SUCCESS) {
    #ifdef TAP_1GBPS_SUPPORT
    Adapter->LinkSpeed = 1000000000; // 1Gbps
    #else
    Adapter->LinkSpeed = 100000000; // 100Mbps
    #endif
    }

    // === 读取非管理员访问标志 ===
    ValueSize = sizeof(Adapter->bNonAdminAllowed);
    Status = NdisReadConfiguration(
    RegistryHandle,
    &ValueType,
    &Adapter->bNonAdminAllowed,
    &ValueSize,
    NDIS_STRING_CONST("AllowNonAdmin")
    );

    if (Status != NDIS_STATUS_SUCCESS) {
    Adapter->bNonAdminAllowed = TRUE; // 默认允许
    }

    NdisMCloseConfiguration(RegistryHandle);

    return NDIS_STATUS_SUCCESS;
    }

    9.4 常见MAC地址问题

    问题原因解决方案
    修改MAC后无效 驱动未更新CurrentMacAddress 重启适配器或使用TapSetInformation
    MAC地址重复 随机算法碰撞 手动设置唯一MAC
    多播地址被拒绝 违反以太网规范 确保bit0=0(单播)
    修改后重启还原 未写入注册表 驱动应在Set时持久化

    10. ARP的处理:TAP模式 vs TUN模式

    10.1 ARP协议简介

    ARP(地址解析协议)用于将IP地址解析为MAC地址。在以太网中,发送IP包前必须先知道目标MAC地址。

    10.2 TAP模式下的ARP处理

    TAP模式是二层设备,处理完整的以太网帧,包括ARP请求和响应。

    // 接收路径处理(简化)
    BOOLEAN
    TapReceivePacket(
    _In_ PTAP_ADAPTER_CONTEXT Adapter,
    _In_ PVOID PacketData,
    _In_ ULONG PacketLength
    )
    {
    PETHERNET_HEADER EthHeader = (PETHERNET_HEADER)PacketData;

    // 检查是否是发往本机的包
    if (NdisEqualMemory(EthHeader->Destination, Adapter->CurrentMacAddress, 6) ||
    EthHeader->Destination[0] == 0xFF) // 广播
    {
    // 检查是否是ARP包(以太网类型0x0806)
    if (EthHeader->EtherType == ntohs(0x0806))
    {
    // === ARP处理 ===
    PARP_PACKET ArpPacket = (PARP_PACKET)((PUCHAR)PacketData + sizeof(ETHERNET_HEADER));

    // 检查是否是请求本机IP的ARP
    if (ArpPacket->Operation == ntohs(ARP_REQUEST) &&
    ArpPacket->TargetProtocolAddress == Adapter->CurrentIpAddress)
    {
    // 构建ARP响应
    TapSendArpResponse(Adapter, ArpPacket);
    return TRUE; // 已处理,不向上传递
    }
    }

    // 非ARP包或不是请求本机的ARP,传递给上层协议
    NdisMIndicateReceiveNetBufferLists(Adapter->MiniportAdapterHandle, ...);
    }

    return TRUE;
    }

    // 发送ARP响应
    VOID
    TapSendArpResponse(
    _In_ PTAP_ADAPTER_CONTEXT Adapter,
    _In_ PARP_PACKET ArpRequest
    )
    {
    UCHAR ResponseBuffer[sizeof(ETHERNET_HEADER) + sizeof(ARP_PACKET)];
    PETHERNET_HEADER EthHeader = (PETHERNET_HEADER)ResponseBuffer;
    PARP_PACKET ArpResponse = (PARP_PACKET)(ResponseBuffer + sizeof(ETHERNET_HEADER));

    // 填充以太网头
    NdisMoveMemory(EthHeader->Destination, ArpRequest->SenderHardwareAddress, 6);
    NdisMoveMemory(EthHeader->Source, Adapter->CurrentMacAddress, 6);
    EthHeader->EtherType = htons(0x0806);

    // 填充ARP响应
    ArpResponse->HardwareType = htons(1); // 以太网
    ArpResponse->ProtocolType = htons(0x0800); // IP
    ArpResponse->HardwareSize = 6;
    ArpResponse->ProtocolSize = 4;
    ArpResponse->Operation = htons(ARP_REPLY);

    // 发送者硬件地址 = 本机MAC
    NdisMoveMemory(ArpResponse->SenderHardwareAddress, Adapter->CurrentMacAddress, 6);
    // 发送者协议地址 = 本机IP
    ArpResponse->SenderProtocolAddress = Adapter->CurrentIpAddress;
    // 目标硬件地址 = 请求者MAC
    NdisMoveMemory(ArpResponse->TargetHardwareAddress, ArpRequest->SenderHardwareAddress, 6);
    // 目标协议地址 = 请求者IP
    ArpResponse->TargetProtocolAddress = ArpRequest->SenderProtocolAddress;

    // 发送响应
    TapSendPacket(Adapter, ResponseBuffer, sizeof(ResponseBuffer));
    }

    10.3 TUN模式下的ARP处理

    TUN模式是三层设备,只处理IP包,不处理以太网帧。ARP请求不会被传递到TUN设备,而是由协议栈处理。

    // TUN模式下,写入的是原始IP包,没有以太网头
    NTSTATUS
    TapWrite(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ PIRP Irp
    )
    {
    PTAP_ADAPTER_CONTEXT Adapter = (PTAP_ADAPTER_CONTEXT)DeviceObject->DeviceExtension;

    if (Adapter->bTunMode) {
    // TUN模式:直接处理IP包,不关心ARP
    // 从IRP获取IP包数据
    PVOID IpPacket = ...;
    ULONG IpLength = ...;

    // 直接注入到协议栈(模拟从网络收到IP包)
    TapInjectIpPacket(Adapter, IpPacket, IpLength);
    } else {
    // TAP模式:处理完整的以太网帧
    // …
    }
    }

    10.4 ARP表与邻居发现

    Windows维护一个ARP缓存表,可以通过命令查看:

    arp -a
    netsh interface ip show neighbors

    TAP驱动不需要直接管理ARP表——这是TCP/IP协议栈的工作。驱动只需正确响应ARP请求即可。


    11. TAP-Windows V9驱动描述信息从哪里来?

    11.1 描述信息的来源链

    驱动描述 “TAP-Windows Adapter V9” 出现在三个地方:

  • 设备管理器中显示的名称
  • OID_GEN_VENDOR_DESCRIPTION查询结果
  • 网络连接面板中的适配器名称
  • 11.2 INF文件中的描述定义

    [Strings]
    DeviceDescription = "TAP-Windows Adapter V9" ; ← 源头在这里!

    [tap0901.NTamd64]
    %DeviceDescription% = tap0901.ndi, root\\tap0901

    [tap0901.ndi.reg]
    HKR, Ndi, DeviceDescription, 0, %DeviceDescription% ; ← 写入注册表

    11.3 注册表中的位置

    安装后,描述信息存储在:

    HKLM\\SYSTEM\\CurrentControlSet\\Control\\Class\\{4d36e972-e325-11ce-bfc1-08002be10318}\\{InstanceId}
    DriverDesc = "TAP-Windows Adapter V9"

    11.4 如何修改描述信息?

    如果要自定义描述信息(例如改成公司名称),需要:

  • 修改INF文件:更改[Strings]节中的DeviceDescription
  • 重新编译驱动(或直接修改注册表临时测试)
  • 重新安装驱动
  • # 临时修改(不推荐,可能被驱动覆盖)
    Set-ItemProperty Path "HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Class\\{4d36e972-…}\\{InstanceId}" Name DriverDesc Value "My Custom Adapter"


    12. 怎么设置网卡连接速度是100Mbps还是1Gbps?

    12.1 速度设置的核心:LinkSpeed参数

    链路速度在驱动中有两个关键位置:

  • 初始化属性:在NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES中设置
  • OID查询响应:响应OID_GEN_LINK_SPEED查询
  • 12.2 源码级修改(两种方法)

    方法一:修改constants.h中的宏定义

    // constants.h
    #ifdef TAP_1GBPS_SUPPORT
    #define TAP_XMIT_SPEED 1000000000ULL // 1 Gbps
    #define TAP_RCV_SPEED 1000000000ULL
    #else
    #define TAP_XMIT_SPEED 100000000ULL // 100 Mbps
    #define TAP_RCV_SPEED 100000000ULL
    #endif

    方法二:从注册表读取(更灵活)

    // adapter.c 中
    Status = NdisReadConfiguration(
    RegistryHandle,
    &ValueType,
    &Adapter->LinkSpeed,
    &ValueSize,
    NDIS_STRING_CONST("LinkSpeed")
    );

    if (Status != NDIS_STATUS_SUCCESS) {
    // 读取失败,使用默认值
    Adapter->LinkSpeed = 100000000; // 100Mbps
    }

    12.3 在INF中设置默认值

    [tap0901.ndi.params]
    HKR, Ndi\\params\\LinkSpeed, ParamDesc, 0, "Link Speed"
    HKR, Ndi\\params\\LinkSpeed, type, 0, "int"
    HKR, Ndi\\params\\LinkSpeed, Default, 0, "100000000" ; 默认100M
    HKR, Ndi\\params\\LinkSpeed\\enum, "100000000", 0, "100 Mbps"
    HKR, Ndi\\params\\LinkSpeed\\enum, "1000000000", 0, "1 Gbps"

    12.4 编译时切换速度

    使用编译指令定义宏:

    # 编译1Gbps版本
    cl /DTAP_1GBPS_SUPPORT tap*.c

    # 编译100Mbps版本(默认)
    cl tap*.c

    12.5 验证当前速度

    # 使用PowerShell查看速度
    Get-NetAdapter Name "*TAP*" | Select Name, LinkSpeed

    # 使用wmic
    wmic nic where "Description like '%TAP%'" get Name, Speed

    # 编程方式查询OID


    13. 驱动上面怎么实现网卡IP/MASK/GatewayIP的配置?

    13.1 IP配置的两种方式

    在Windows中,IP地址配置不是在驱动层完成的,而是在协议层(TCP/IP协议栈)。驱动只负责提供收发数据包的通道。

    13.2 方式一:netsh命令(推荐)

    # 设置静态IP
    netsh interface ip set address name="TAP-Windows Adapter V9" static 10.8.0.2 255.255.255.0 10.8.0.1

    # 设置DNS
    netsh interface ip set dns name="TAP-Windows Adapter V9" static 8.8.8.8

    13.3 方式二:DHCP MASQ(见第14章)

    通过IOCTL配置DHCP MASQ参数,驱动模拟DHCP服务器给协议栈分配IP。

    13.4 方式三:直接调用NDIS API(不推荐)

    协议栈(TCP/IP.sys)通过OID请求配置IP,但驱动通常不直接参与:

    // 协议栈发送这些OID来设置IP(驱动只需响应查询)
    OID_TCP_OFFLOAD_PARAMETERS
    OID_IP4_OFFLOAD_STATS

    13.5 驱动层是否需要知道IP?

    TAP驱动实际上不需要知道IP地址——它只处理以太网帧(TAP模式)或IP包(TUN模式),不关心IP地址。但DHCP MASQ功能需要知道分配的IP,因此驱动保存了IP信息。


    14. DHCP MASQ自问自答

    14.1 什么是DHCP MASQ?

    DHCP MASQ (DHCP Masquerade) 是TAP驱动的一个特殊功能:驱动假装自己是DHCP服务器,对来自协议栈的DHCP请求进行"自问自答",从而自动给虚拟网卡分配IP地址,而无需外部DHCP服务器。

    为什么需要这个功能?

    • OpenVPN等VPN软件需要自动给TAP适配器分配IP
    • 避免依赖外部DHCP服务器(可能不存在或延迟高)
    • 简化配置,实现即插即用

    14.2 DHCP协议基础

    DHCP使用四个主要包类型:

    • DHCPDISCOVER:客户端广播寻找DHCP服务器
    • DHCPOFFER:服务器响应,提供IP地址
    • DHCPREQUEST:客户端确认接受IP
    • DHCPACK:服务器确认分配

    14.3 DHCP MASQ工作原理

    自问自答流程

    OpenVPNTAP驱动TCP/IP协议栈OpenVPNTAP驱动TCP/IP协议栈#mermaid-svg-Ev272ZJPDPFc4dhs{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-Ev272ZJPDPFc4dhs .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Ev272ZJPDPFc4dhs .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Ev272ZJPDPFc4dhs .error-icon{fill:#552222;}#mermaid-svg-Ev272ZJPDPFc4dhs .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Ev272ZJPDPFc4dhs .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Ev272ZJPDPFc4dhs .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Ev272ZJPDPFc4dhs .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Ev272ZJPDPFc4dhs .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Ev272ZJPDPFc4dhs .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Ev272ZJPDPFc4dhs .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Ev272ZJPDPFc4dhs .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Ev272ZJPDPFc4dhs .marker.cross{stroke:#333333;}#mermaid-svg-Ev272ZJPDPFc4dhs svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Ev272ZJPDPFc4dhs p{margin:0;}#mermaid-svg-Ev272ZJPDPFc4dhs .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Ev272ZJPDPFc4dhs text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-Ev272ZJPDPFc4dhs .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-Ev272ZJPDPFc4dhs .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-Ev272ZJPDPFc4dhs .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-Ev272ZJPDPFc4dhs .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-Ev272ZJPDPFc4dhs #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-Ev272ZJPDPFc4dhs .sequenceNumber{fill:white;}#mermaid-svg-Ev272ZJPDPFc4dhs #sequencenumber{fill:#333;}#mermaid-svg-Ev272ZJPDPFc4dhs #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-Ev272ZJPDPFc4dhs .messageText{fill:#333;stroke:none;}#mermaid-svg-Ev272ZJPDPFc4dhs .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Ev272ZJPDPFc4dhs .labelText,#mermaid-svg-Ev272ZJPDPFc4dhs .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-Ev272ZJPDPFc4dhs .loopText,#mermaid-svg-Ev272ZJPDPFc4dhs .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-Ev272ZJPDPFc4dhs .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-Ev272ZJPDPFc4dhs .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-Ev272ZJPDPFc4dhs .noteText,#mermaid-svg-Ev272ZJPDPFc4dhs .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-Ev272ZJPDPFc4dhs .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Ev272ZJPDPFc4dhs .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Ev272ZJPDPFc4dhs .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Ev272ZJPDPFc4dhs .actorPopupMenu{position:absolute;}#mermaid-svg-Ev272ZJPDPFc4dhs .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-Ev272ZJPDPFc4dhs .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Ev272ZJPDPFc4dhs .actor-man circle,#mermaid-svg-Ev272ZJPDPFc4dhs line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-Ev272ZJPDPFc4dhs :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}1. 配置DHCP MASQ2. 协议栈发送DHCP请求3. 驱动模拟服务器响应4. IP配置完成!IOCTL_CONFIG_DHCP_MASQ(IP=10.8.0.2, Mask=255.255.255.0)保存配置到Adapter发送DHCPDISCOVER广播包检测到DHCP包,源MAC是本机构建DHCPOFFER包(包含预配置IP)注入OFFER包到接收路径发送DHCPREQUEST确认构建DHCPACK包注入ACK包设置IP为10.8.0.2/24

    14.4 源实现

    源码文件:dhcp.c 和 device.c

    // dhcp.c – DHCP包结构定义
    #pragma pack(push, 1)
    typedef struct _DHCP_PACKET
    {
    UCHAR OpCode; // 1=请求, 2=响应
    UCHAR HardwareType; // 1=以太网
    UCHAR HardwareLen; // 6
    UCHAR Hops;
    ULONG TransactionId; // 事务ID
    USHORT Seconds;
    USHORT Flags;
    ULONG ClientIpAddress;
    ULONG YourIpAddress; // 分配给客户端的IP
    ULONG ServerIpAddress; // DHCP服务器IP
    ULONG GatewayIpAddress;
    UCHAR ClientHardwareAddress[16];
    UCHAR ServerHostName[64];
    UCHAR BootFilename[128];
    ULONG MagicCookie; // 0x63538263 (DHCP魔数)
    UCHAR Options[312]; // DHCP选项
    } DHCP_PACKET, *PDHCP_PACKET;
    #pragma pack(pop)

    // DHCP选项定义
    #define DHCP_OPTION_SUBNET_MASK 1
    #define DHCP_OPTION_ROUTER 3
    #define DHCP_OPTION_DNS_SERVER 6
    #define DHCP_OPTION_DOMAIN_NAME 15
    #define DHCP_OPTION_LEASE_TIME 51
    #define DHCP_OPTION_MESSAGE_TYPE 53
    #define DHCP_OPTION_SERVER_ID 54
    #define DHCP_OPTION_REQUESTED_IP 50
    #define DHCP_OPTION_END 255

    // DHCP消息类型
    #define DHCP_DISCOVER 1
    #define DHCP_OFFER 2
    #define DHCP_REQUEST 3
    #define DHCP_DECLINE 4
    #define DHCP_ACK 5
    #define DHCP_NAK 6
    #define DHCP_RELEASE 7

    // 接收路径中的DHCP检测
    BOOLEAN
    TapProcessDhcpPacket(
    _In_ PTAP_ADAPTER_CONTEXT Adapter,
    _In_ PVOID PacketData,
    _In_ ULONG PacketLength
    )
    {
    PETHERNET_HEADER EthHeader = (PETHERNET_HEADER)PacketData;
    PIPV4_HEADER IpHeader;
    PUDP_HEADER UdpHeader;
    PDHCP_PACKET DhcpPacket;
    ULONG IpHeaderLen;

    // 1. 检查是否启用了DHCP MASQ
    if (!Adapter->bDhcpEnabled) {
    return FALSE;
    }

    // 2. 检查是否是UDP包发往67端口(DHCP服务器端口)
    if (EthHeader->EtherType != ntohs(0x0800)) { // 不是IP包
    return FALSE;
    }

    IpHeader = (PIPV4_HEADER)((PUCHAR)PacketData + sizeof(ETHERNET_HEADER));
    IpHeaderLen = (IpHeader->VersionAndHeaderLen & 0x0F) * 4;

    if (IpHeader->Protocol != 17) { // 不是UDP
    return FALSE;
    }

    UdpHeader = (PUDP_HEADER)((PUCHAR)IpHeader + IpHeaderLen);

    // DHCP使用67(服务器)和68(客户端)端口
    if (ntohs(UdpHeader->DestinationPort) != 67) {
    return FALSE;
    }

    // 3. 定位DHCP包
    DhcpPacket = (PDHCP_PACKET)((PUCHAR)UdpHeader + sizeof(UDP_HEADER));

    // 4. 验证DHCP魔数
    if (ntohl(DhcpPacket->MagicCookie) != 0x63538263) {
    return FALSE;
    }

    // 5. 获取DHCP消息类型
    UCHAR DhcpMsgType = 0;
    PUCHAR Options = DhcpPacket->Options;
    while (*Options != DHCP_OPTION_END) {
    if (*Options == DHCP_OPTION_MESSAGE_TYPE) {
    DhcpMsgType = *(Options + 2); // 选项值
    break;
    }
    Options += *(Options + 1) + 2; // 跳到下一个选项
    }

    DEBUGP(("[tap] DHCP packet detected, type=%d\\n", DhcpMsgType));

    // 6. 根据消息类型处理
    switch (DhcpMsgType)
    {
    case DHCP_DISCOVER:
    TapSendDhcpOffer(Adapter, DhcpPacket);
    return TRUE; // 已处理,不向上传递

    case DHCP_REQUEST:
    TapSendDhcpAck(Adapter, DhcpPacket);
    return TRUE;

    case DHCP_DECLINE:
    case DHCP_RELEASE:
    // 可选:记录释放信息
    return TRUE;
    }

    return FALSE;
    }

    // 发送DHCP OFFER
    VOID
    TapSendDhcpOffer(
    _In_ PTAP_ADAPTER_CONTEXT Adapter,
    _In_ PDHCP_PACKET DiscoverPacket
    )
    {
    UCHAR ResponseBuffer[2048];
    PETHERNET_HEADER EthHeader = (PETHERNET_HEADER)ResponseBuffer;
    PIPV4_HEADER IpHeader = (PIPV4_HEADER)(ResponseBuffer + sizeof(ETHERNET_HEADER));
    PUDP_HEADER UdpHeader = (PUDP_HEADER)((PUCHAR)IpHeader + sizeof(IPV4_HEADER));
    PDHCP_PACKET OfferPacket = (PDHCP_PACKET)((PUCHAR)UdpHeader + sizeof(UDP_HEADER));
    ULONG TotalLength;

    // 构建以太网头
    NdisMoveMemory(EthHeader->Destination, DiscoverPacket->ClientHardwareAddress, 6);
    NdisMoveMemory(EthHeader->Source, Adapter->DhcpServerMac, 6);
    EthHeader->EtherType = htons(0x0800);

    // 构建IP头
    IpHeader->VersionAndHeaderLen = 0x45; // IPv4, 20字节头
    IpHeader->TypeOfService = 0;
    // 长度稍后计算
    IpHeader->Identification = 0;
    IpHeader->FragmentOffset = 0;
    IpHeader->TimeToLive = 128;
    IpHeader->Protocol = 17; // UDP
    IpHeader->SourceAddress = Adapter->DhcpServerAddress; // DHCP服务器IP
    IpHeader->DestAddress = 0xFFFFFFFF; // 广播255.255.255.255

    // 构建UDP头
    UdpHeader->SourcePort = htons(67);
    UdpHeader->DestPort = htons(68);
    // UDP长度稍后计算

    // 构建DHCP OFFER包
    NdisZeroMemory(OfferPacket, sizeof(DHCP_PACKET));
    OfferPacket->OpCode = 2; // 响应
    OfferPacket->HardwareType = 1;
    OfferPacket->HardwareLen = 6;
    OfferPacket->TransactionId = DiscoverPacket->TransactionId;
    OfferPacket->YourIpAddress = Adapter->DhcpAddress; // 分配的IP
    OfferPacket->ServerIpAddress = Adapter->DhcpServerAddress;
    NdisMoveMemory(OfferPacket->ClientHardwareAddress,
    DiscoverPacket->ClientHardwareAddress, 6);
    OfferPacket->MagicCookie = htonl(0x63538263);

    // 添加DHCP选项
    PUCHAR Options = OfferPacket->Options;

    *Options++ = DHCP_OPTION_MESSAGE_TYPE;
    *Options++ = 1;
    *Options++ = DHCP_OFFER;

    *Options++ = DHCP_OPTION_SERVER_ID;
    *Options++ = 4;
    *(PULONG)Options = Adapter->DhcpServerAddress;
    Options += 4;

    *Options++ = DHCP_OPTION_SUBNET_MASK;
    *Options++ = 4;
    *(PULONG)Options = Adapter->DhcpMask;
    Options += 4;

    *Options++ = DHCP_OPTION_LEASE_TIME;
    *Options++ = 4;
    *(PULONG)Options = htonl(Adapter->DhcpLeaseTime);
    Options += 4;

    *Options++ = DHCP_OPTION_ROUTER;
    *Options++ = 4;
    *(PULONG)Options = Adapter->DhcpGateway;
    Options += 4;

    *Options++ = DHCP_OPTION_END;

    // 计算长度
    ULONG DhcpSize = (ULONG)(Options (PUCHAR)OfferPacket);
    ULONG UdpSize = sizeof(UDP_HEADER) + DhcpSize;
    ULONG IpSize = sizeof(IPV4_HEADER) + UdpSize;
    TotalLength = sizeof(ETHERNET_HEADER) + IpSize;

    // 设置长度字段
    IpHeader->TotalLength = htons((USHORT)IpSize);
    UdpHeader->Length = htons((USHORT)UdpSize);

    // 计算校验和(简化示例,实际需要完整计算)
    IpHeader->HeaderChecksum = TapCalculateIpChecksum(IpHeader);
    UdpHeader->Checksum = TapCalculateUdpChecksum(IpHeader, UdpHeader, DhcpPacket);

    // 发送响应
    TapSendPacket(Adapter, ResponseBuffer, TotalLength);

    DEBUGP(("[tap] DHCP OFFER sent for IP %d.%d.%d.%d\\n",
    (Adapter->DhcpAddress >> 0) & 0xFF,
    (Adapter->DhcpAddress >> 8) & 0xFF,
    (Adapter->DhcpAddress >> 16) & 0xFF,
    (Adapter->DhcpAddress >> 24) & 0xFF));
    }

    // 发送DHCP ACK(类似OFFER,但消息类型不同)
    VOID
    TapSendDhcpAck(
    _In_ PTAP_ADAPTER_CONTEXT Adapter,
    _In_ PDHCP_PACKET RequestPacket
    )
    {
    // 类似OFFER,但设置消息类型为DHCP_ACK
    // …(代码类似,省略)
    }

    14.5 DHCP MASQ的用途

  • 自动IP配置:无需用户手动设置IP
  • 简化VPN配置:OpenVPN服务器可以推送IP配置
  • 隔离环境:不需要依赖外部DHCP服务器
  • 快速重连:驱动内处理,比外部DHCP快
  • 14.6 配置示例(OpenVPN)

    # OpenVPN客户端配置
    dev tap
    ipconfig 10.8.0.2 255.255.255.0
    dhcp-option DNS 8.8.8.8

    OpenVPN内部会调用:

    TAP_DHCP_CONFIG config;
    config.AdapterAddress = inet_addr("10.8.0.2");
    config.NetworkMask = inet_addr("255.255.255.0");
    config.LeaseTime = 86400; // 1天
    DeviceIoControl(hTap, TAP_WIN_IOCTL_CONFIG_DHCP_MASQ, &config, sizeof(config), NULL, 0, &len, NULL);


    15. WinDbg调试、自行编译

    15.1 WinDbg调试TAP驱动

    准备工作:

  • 启用测试签名:bcdedit /set testsigning on
  • 安装WinDbg和符号
  • 设置内核调试(网络或串口)
  • 常用调试命令:

    # 加载NDIS调试扩展
    .load ndis

    # 列出所有Miniport
    !ndiskd.miniport

    # 查看特定适配器信息
    !ndiskd.miniport ffffe0015f4a1a70 -adapters

    # 查看OID处理
    !ndiskd.oid ffffe0015f4a1a70

    # 设置断点
    bp tap0901!TapAdapterOidRequest
    bp tap0901!TapDeviceControl

    # 查看IRP
    !irp ffffe0015f4a1a70

    # 查看内存
    dt tap0901!TAP_ADAPTER_CONTEXT ffffe0015f4a1a70

    15.2 自行编译TAP Driver

    从源码编译(OpenVPN/tap-windows6):

    # 克隆源码
    git clone https://github.com/OpenVPN/tap-windows6.git
    cd tap-windows6

    # 安装WDK(Windows Driver Kit)
    # 使用Visual Studio Developer Command Prompt

    # 编译x64版本
    msbuild tap.sln /p:Configuration="Win10 Release" /p:Platform=x64

    # 编译x86版本
    msbuild tap.sln /p:Configuration="Win10 Release" /p:Platform=Win32

    # 输出文件在 .\\x64\\Win10 Release\\tap0901.sys

    15.3 常见问题

    故障现象调试方法修复措施
    驱动无法加载 WinDbg查看!analyze -v 检查签名、版本兼容性
    无法创建设备 断点TapCreateDevice 检查GUID生成、权限
    OID查询失败 断点TapQueryInformation 检查SupportedOids列表
    DHCP MASQ不工作 抓包查看DHCP包 检查DhcpAddress配置
    性能低 检查队列长度 调整MTU、缓冲区大小

    16. 数据流处理

    16.1 数据流向

    TAP虚拟网卡的本质是内核与用户态之间的数据交换通道。两个方向的数据流截然不同:

    • 出站方向(内核 → 用户态):TCP/IP协议栈有数据包要发送(如应用程序的Socket发包),通过NDIS下发到TAP驱动,驱动将数据包转发给等待的用户态程序(如OpenVPN)。
    • 入站方向(用户态 → 内核):用户态程序(如OpenVPN从远端收到数据包)通过WriteFile写入设备,驱动将这些数据包构造为NDIS接收指示,注入回协议栈,仿佛它们刚从“网络”到达。

    这两个方向通过不同的NDIS回调与IRP处理函数实现,下面分别说明。


    16.2 出站方向:从内核获取待发送的数据包

    16.2.1 触发时机

    当上层协议(TCP/IP)有数据要发送时,它会调用NdisSendNetBufferLists,NDIS随后调用驱动在DriverEntry中注册的SendNetBufferListsHandler回调——即TapAdapterSend函数。

    16.2.2 发送回调函数

    // adapter.c 或 send.c
    VOID TapAdapterSend(
    NDIS_HANDLE MiniportAdapterContext,
    PNET_BUFFER_LIST NetBufferLists,
    NDIS_PORT_NUMBER PortNumber,
    ULONG SendFlags
    )
    {
    PTAP_ADAPTER_CONTEXT adapter = (PTAP_ADAPTER_CONTEXT)MiniportAdapterContext;
    PNET_BUFFER_LIST currentNbl;
    PNET_BUFFER nb;
    ULONG totalLength;
    PMDL mdl;
    PVOID data;

    // 遍历传入的NBL链表
    currentNbl = NetBufferLists;
    while (currentNbl != NULL)
    {
    // 对每个NBL,遍历其包含的NET_BUFFER(支持大数据包分片)
    nb = NET_BUFFER_LIST_FIRST_NB(currentNbl);
    while (nb != NULL)
    {
    totalLength = NET_BUFFER_DATA_LENGTH(nb);
    mdl = NET_BUFFER_CURRENT_MDL(nb);
    data = MmGetSystemAddressForMdlSafe(mdl, NormalPagePriority);
    if (data == NULL)
    {
    // 资源不足,标记失败
    currentNbl->Status = NDIS_STATUS_RESOURCES;
    break;
    }

    // 将数据包放入队列,等待用户态读取
    TapQueuePacketToUser(adapter, data, totalLength);

    nb = NET_BUFFER_NEXT_NB(nb);
    }

    currentNbl = currentNbl->Next;
    }

    // 注意:此处不能立即完成所有NBL,因为用户态可能尚未读取。
    // 正确的做法是:将NBL暂存,直到用户态通过ReadFile取走数据后再调用NdisMSendNetBufferListsComplete。
    // 但为了简化,许多TAP驱动设计为立即完成,认为数据已“成功”转交给用户态。
    // 我们采用立即完成策略,并将实际发送责任交给用户态程序。
    NdisMSendNetBufferListsComplete(adapter->MiniportAdapterHandle,
    NetBufferLists,
    NDIS_STATUS_SUCCESS);
    }

    16.2.3 将数据转发给用户态

    TapQueuePacketToUser 负责将数据包放入一个环形缓冲区(或链表),并唤醒任何正在等待IRP_MJ_READ的用户态线程。驱动在处理读IRP时,如果队列中有数据,则立即拷贝数据并完成IRP;否则将IRP挂起到一个等待队列,直到有数据到达时再完成。

    // 读IRP处理函数(TapRead)
    NTSTATUS TapRead(PDEVICE_OBJECT devObj, PIRP irp)
    {
    PTAP_ADAPTER_CONTEXT adapter = ...;
    PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(irp);
    ULONG bytesToRead = irpSp->Parameters.Read.Length;
    NTSTATUS status;

    // 尝试从队列中取出一个数据包
    status = TapDequeuePacket(adapter, irp->UserBuffer, &bytesToRead);
    if (status == STATUS_SUCCESS)
    {
    // 已有数据,立即完成
    irp->IoStatus.Information = bytesToRead;
    irp->IoStatus.Status = STATUS_SUCCESS;
    IoCompleteRequest(irp, IO_NO_INCREMENT);
    return STATUS_SUCCESS;
    }

    // 无数据,将IRP挂起到队列
    status = TapQueueIrpForLater(adapter, irp);
    if (status != STATUS_SUCCESS)
    {
    irp->IoStatus.Status = status;
    IoCompleteRequest(irp, IO_NO_INCREMENT);
    }
    return status;
    }

    当TapAdapterSend将数据包入队后,它会调用TapWakeupReaders,遍历挂起的读IRP列表,将数据拷贝到每个IRP的缓冲区(注意:一个包只能被一个用户态程序读取,通常只有一个读IRP在等待),并完成该IRP。

    16.2.4 关键点:发送完成时机

    由于数据包的实际发送由用户态程序(如OpenVPN)负责,TAP驱动在将数据交给用户态后即认为发送“成功”。因此立即调用NdisMSendNetBufferListsComplete是合理的,这符合NDIS语义。如果用户态程序后续发送失败(例如网络不通),那是上层逻辑的问题,驱动不再负责。


    16.3 入站方向:从用户态接收数据并注入内核

    16.3.1 触发时机

    用户态程序(如OpenVPN从远端收到加密数据并解密后)获得一个原始以太网帧(TAP模式)或IP包(TUN模式),它通过WriteFile向设备写入数据。I/O管理器创建IRP_MJ_WRITE并调用驱动的TapWrite函数。

    16.3.2 写IRP处理函数

    // device.c
    NTSTATUS TapWrite(PDEVICE_OBJECT devObj, PIRP irp)
    {
    PTAP_ADAPTER_CONTEXT adapter = ...;
    PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(irp);
    ULONG length = irpSp->Parameters.Write.Length;
    PVOID data = irp->AssociatedIrp.SystemBuffer; // 假设使用METHOD_BUFFERED
    NTSTATUS status;

    // 根据模式检查数据包格式
    if (adapter->bTunMode)
    {
    // TUN模式:数据应为IP包(无以太网头),需验证IP头合法性
    status = TapValidateIpPacket(data, length);
    }
    else
    {
    // TAP模式:数据应为完整以太网帧
    status = TapValidateEthernetFrame(data, length);
    }

    if (status != STATUS_SUCCESS)
    {
    irp->IoStatus.Status = status;
    IoCompleteRequest(irp, IO_NO_INCREMENT);
    return status;
    }

    // 将数据包注入内核协议栈
    status = TapInjectPacketToProtocolStack(adapter, data, length);

    irp->IoStatus.Status = status;
    irp->IoStatus.Information = (status == STATUS_SUCCESS) ? length : 0;
    IoCompleteRequest(irp, IO_NO_INCREMENT);
    return status;
    }

    16.3.3 数据注入:NdisMIndicateReceiveNetBufferLists(内核注入)

    TapInjectPacketToProtocolStack的核心是构建一个NET_BUFFER_LIST,并调用NDIS接收指示函数,让协议栈认为有数据从“网络”到达。

    NDIS_STATUS TapInjectPacketToProtocolStack(
    PTAP_ADAPTER_CONTEXT adapter,
    PVOID data,
    ULONG length
    )
    {
    PNET_BUFFER_LIST nbl;
    PNET_BUFFER nb;
    PMDL mdl;
    NDIS_STATUS status;

    // 分配MDL描述数据缓冲区(注意:必须使用非分页内存或正确锁定用户缓冲区)
    mdl = IoAllocateMdl(data, length, FALSE, FALSE, NULL);
    if (mdl == NULL)
    return NDIS_STATUS_RESOURCES;
    MmBuildMdlForNonPagedPool(mdl); // 假设data来自非分页池,或已锁定

    // 分配NET_BUFFER
    nb = NET_BUFFER_FIRST_NB(nbl);
    if (nb == NULL)
    {
    IoFreeMdl(mdl);
    return NDIS_STATUS_RESOURCES;
    }
    NET_BUFFER_FIRST_MDL(nb) = mdl;
    NET_BUFFER_DATA_LENGTH(nb) = length;

    // 分配NET_BUFFER_LIST
    nbl = NdisAllocateNetBufferList(adapter->NblPool, 0, 0);
    if (nbl == NULL)
    {
    IoFreeMdl(mdl);
    return NDIS_STATUS_RESOURCES;
    }
    NET_BUFFER_LIST_FIRST_NB(nbl) = nb;
    NET_BUFFER_LIST_STATUS(nbl) = NDIS_STATUS_SUCCESS;

    // 设置接收标志(例如:这是从介质上收到的包)
    ULONG receiveFlags = 0;
    if (adapter->bTunMode)
    {
    // TUN模式可能不需要指示以太网帧,但NDIS仍然期望以太网封装?实际上TUN模式注入时,数据是IP包,但协议栈期望的是以太网帧?
    // 注意:在TUN模式下,网卡仍以以太网适配器形式存在,但用户态写入的是IP包,驱动需要添加一个虚拟的以太网头吗?
    // 标准TUN驱动(如Linux tun)在TUN模式下直接注入IP包,但Windows NDIS要求最小端口以以太网帧形式传递。因此TAP-Windows在TUN模式下,驱动会自动添加一个以太网头(目的MAC为广播或特定),并在接收时剥离。这部分逻辑在TapWrite中需处理。
    // 为简化,这里假设数据已经是完整的以太网帧(包括头)。
    }

    // 指示接收
    NdisMIndicateReceiveNetBufferLists(adapter->MiniportAdapterHandle,
    nbl,
    NDIS_DEFAULT_PORT_NUMBER,
    receiveFlags);

    // 注意:协议栈处理完数据后会通过ReturnNetBufferListsHandler回调返还NBL,
    // 驱动需在那里释放MDL和NBL。
    return NDIS_STATUS_SUCCESS;
    }

    16.3.4 接收完成回调:释放资源

    当协议栈处理完接收的数据包后,NDIS会调用驱动注册的ReturnNetBufferListsHandler(即TapAdapterReturnNetBufferLists),驱动应在此释放之前分配的MDL和NBL。

    VOID TapAdapterReturnNetBufferLists(
    NDIS_HANDLE MiniportAdapterContext,
    PNET_BUFFER_LIST NetBufferLists,
    ULONG ReturnFlags
    )
    {
    PNET_BUFFER_LIST nbl, nextNbl;
    PNET_BUFFER nb;
    PMDL mdl;

    nbl = NetBufferLists;
    while (nbl != NULL)
    {
    nextNbl = nbl->Next;
    nb = NET_BUFFER_LIST_FIRST_NB(nbl);
    if (nb != NULL)
    {
    mdl = NET_BUFFER_FIRST_MDL(nb);
    if (mdl != NULL)
    IoFreeMdl(mdl);
    // 注意:NET_BUFFER本身是NBL的一部分,无需单独释放
    }
    NdisFreeNetBufferList(nbl);
    nbl = nextNbl;
    }
    }


    16.4 数据流

    #mermaid-svg-3v954tq6o2GTn5nM{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-3v954tq6o2GTn5nM .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-3v954tq6o2GTn5nM .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-3v954tq6o2GTn5nM .error-icon{fill:#552222;}#mermaid-svg-3v954tq6o2GTn5nM .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-3v954tq6o2GTn5nM .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-3v954tq6o2GTn5nM .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3v954tq6o2GTn5nM .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3v954tq6o2GTn5nM .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-3v954tq6o2GTn5nM .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3v954tq6o2GTn5nM .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3v954tq6o2GTn5nM .marker{fill:#333333;stroke:#333333;}#mermaid-svg-3v954tq6o2GTn5nM .marker.cross{stroke:#333333;}#mermaid-svg-3v954tq6o2GTn5nM svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3v954tq6o2GTn5nM p{margin:0;}#mermaid-svg-3v954tq6o2GTn5nM .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-3v954tq6o2GTn5nM .cluster-label text{fill:#333;}#mermaid-svg-3v954tq6o2GTn5nM .cluster-label span{color:#333;}#mermaid-svg-3v954tq6o2GTn5nM .cluster-label span p{background-color:transparent;}#mermaid-svg-3v954tq6o2GTn5nM .label text,#mermaid-svg-3v954tq6o2GTn5nM span{fill:#333;color:#333;}#mermaid-svg-3v954tq6o2GTn5nM .node rect,#mermaid-svg-3v954tq6o2GTn5nM .node circle,#mermaid-svg-3v954tq6o2GTn5nM .node ellipse,#mermaid-svg-3v954tq6o2GTn5nM .node polygon,#mermaid-svg-3v954tq6o2GTn5nM .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-3v954tq6o2GTn5nM .rough-node .label text,#mermaid-svg-3v954tq6o2GTn5nM .node .label text,#mermaid-svg-3v954tq6o2GTn5nM .image-shape .label,#mermaid-svg-3v954tq6o2GTn5nM .icon-shape .label{text-anchor:middle;}#mermaid-svg-3v954tq6o2GTn5nM .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-3v954tq6o2GTn5nM .rough-node .label,#mermaid-svg-3v954tq6o2GTn5nM .node .label,#mermaid-svg-3v954tq6o2GTn5nM .image-shape .label,#mermaid-svg-3v954tq6o2GTn5nM .icon-shape .label{text-align:center;}#mermaid-svg-3v954tq6o2GTn5nM .node.clickable{cursor:pointer;}#mermaid-svg-3v954tq6o2GTn5nM .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-3v954tq6o2GTn5nM .arrowheadPath{fill:#333333;}#mermaid-svg-3v954tq6o2GTn5nM .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-3v954tq6o2GTn5nM .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-3v954tq6o2GTn5nM .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3v954tq6o2GTn5nM .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-3v954tq6o2GTn5nM .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3v954tq6o2GTn5nM .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-3v954tq6o2GTn5nM .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-3v954tq6o2GTn5nM .cluster text{fill:#333;}#mermaid-svg-3v954tq6o2GTn5nM .cluster span{color:#333;}#mermaid-svg-3v954tq6o2GTn5nM 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-3v954tq6o2GTn5nM .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-3v954tq6o2GTn5nM rect.text{fill:none;stroke-width:0;}#mermaid-svg-3v954tq6o2GTn5nM .icon-shape,#mermaid-svg-3v954tq6o2GTn5nM .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3v954tq6o2GTn5nM .icon-shape p,#mermaid-svg-3v954tq6o2GTn5nM .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-3v954tq6o2GTn5nM .icon-shape rect,#mermaid-svg-3v954tq6o2GTn5nM .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3v954tq6o2GTn5nM .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-3v954tq6o2GTn5nM .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-3v954tq6o2GTn5nM :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    用户态

    内核态

    NdisSendNetBufferLists

    SendNetBufferListsHandler

    TapAdapterSend

    唤醒读IRP

    返回数据

    IRP_MJ_WRITE

    TapWrite

    NdisMIndicateReceiveNetBufferLists

    指示接收

    处理完

    释放资源

    TCP/IP协议栈

    NDIS库

    tap0901.sys 发送处理

    数据包队列

    读IRP完成

    用户态ReadFile

    用户态WriteFile

    tap0901.sys 写入处理

    构造NET_BUFFER_LIST

    NDIS库

    ReturnNetBufferListsHandler

    释放MDL/NBL

    OpenVPN等应用


    17 备注

  • 系统驱动:通过INF安装,PnP加载,DriverEntry注册Miniport
  • 网卡创建:AdapterCreate分配上下文,设置属性,创建设备
  • IRP处理:通过MajorFunction分发,DeviceIoControl处理配置
  • OID机制:属性查询/设置,支持30+OID
  • MAC地址:随机生成或注册表读取,可修改
  • ARP处理:TAP模式完整处理,TUN模式透传
  • 速度设置:LinkSpeed参数,可编译或注册表配置
  • IP配置:netsh或DHCP MASQ
  • DHCP MASQ:驱动内自问自答,自动分配IP
  • 赞(0)
    未经允许不得转载:171主机测评 » TAP-Windows V9 (NDIS 6.20/6.30)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址