How to indicate eapol frame form WDI Driver

I use NDIS_WDI_RX_INORDER_DATA_IND_HANDLER and MINIPORT_WDI_RX_GET_MPDUS to indicate NBL to WDI, the format is MacHeader(24B)+LLC(8B)+Payload.

When I connect to an open AP, my driver works normally, I can ping bing, so I think my datapath is ok right?

But when it turns to a personal-WPA2 AP, my driver doesn't work.
As I know, EAPOL frame is a data frame, so I indicate it as usual. After indicating msg1, NDIS should send msg2 down to driver, but it doesn't.

So I wanna know if EAPOL frame needs special handle in my driver? Or my driver can regard EAPOL frame as normal data frame so there must be something wrong in other places?

I have tried to add QoS-control behind MacHeader(SubType=QosData) but it still no use.

PeerId=Any, TID=Unknow. (I check the WDI source code and I found that WDI will get PeerId by addr2 and get TID by QoS-control, so I think they are not the key)

WinDbg logs:

DriverEntry: ==> Build on Jun  5 2025 at 15:40:08 
DriverEntry: <== 

EvtAllocateAdapter: ==> 
Miniport Init Params: 
	IfIndex: 0x00000018 
	NetLuid: 
		Value: 0x0047008000000000 
		Info: 
			Index: 0x0000000000008000 
			IfType: IF_TYPE_IEEE80211 
	DefaultPortAuthStates: 
		SendControlState: NdisPortControlStateUncontrolled 
		RcvControlState: NdisPortControlStateUncontrolled 
		SendAuthorizationState: NdisPortAuthorized 
		RcvAuthorizationState: NdisPortAuthorized 
CreateUsbDevice: 1 interface and 4 pipes detected 
CreateUsbDevice: USB 2.0 (High Speed) device detected
InitThread: Entry 
RunThread: Entry 
NdisThreadCallback: RxHandle thread is started 
NdisThreadCallback: TxHandle thread is started 
RxHandleThreadCallback: Entry 
TxHandleThreadCallback: Entry 
EvtAllocateAdapter: <== 

EvtOpenAdapter: ==> 
EvtOpenAdapter: <== 

EvtTalTxRxInitialize: ==> 
EvtTalTxRxInitialize: <== 

WdiGetAdapterCapabilities: ==> 
WdiGetAdapterCapabilities: <== 

WdiSetAdapterConfiguration: ==> 
WdiSetAdapterConfiguration: <== 

EvtTalTxRxStart: ==> 
Wifi TxRx Configuration: 
    TxRxParams:
        TxRxCapabilities:
            InterconnectType: WDI_INTERCONNECT_MESSAGE_BASED
            TransmitCapabilities:
                TargetPriorityQueueing: TRUE
                MaxScatterGatherElementsPerFrame: 255
                ExplicitSendCompleteFlagRequired: TRUE
                MinEffectiveSize: 0
                FrameSizeGranularity: 1
            ReceiveCapabilities:
                RxTxForwarding: FALSE
                MaxThroughput: 1200
    MaxNumPorts: 5
    MaxNumPeers: 255
EvtTalTxRxStart: <== 

WdiTaskCreatePort: ==> 
Task Create Port Params: 
	CreatePortParameters: 
		OpModeMask: STA_OP_MODE 
		NdisPortNumber: 0 
	MacAddress: (not present) 
WdiTaskCreatePort: Create Port[0] successfully, MacAddress=22-23-24-d4-cf-d8 
WdiTaskCreatePort: <== 

EvtTalTxRxAddPort: TxRx Add Port[0], works on STA_OP_MODE 
EvtTalTxRxAddPort: Add Peer[0] for Port[0] to broadcast frame 
EvtTalTxRxResetPort: TxRx Reset on Port[0] 

WdiTaskDot11Reset: ==> 
Task Dot11 Reset Params: 
   SetDefaultMIB: TRUE 
   ResetMACAddress: (not present) 
WdiTaskDot11Reset: <== 

EvtDevicePnPEventNotify: ==> 
NetDevicePnPEvent:
    PortNumber: 0 
    DevicePnPEvent: NdisDevicePnPEventPowerProfileChanged 
    InformationBufferLength: 4 
EvtDevicePnPEventNotify: <== 

EvtRxRestart: Rx Restart on port[0] 
EvtTxAbort: Tx Abort on Port[0] 
EvtRxStop: Rx Stop on port[0] 
EvtRxFlush: Rx Flush on port[0] 
EvtTalTxRxResetPort: TxRx Reset on Port[0] 

WdiTaskDot11Reset: ==> 
Task Dot11 Reset Params: 
   SetDefaultMIB: TRUE 
   ResetMACAddress: (not present) 
WdiTaskDot11Reset: <== 

WdiGetStatistics: ==> 
WdiGetStatistics: <== 

WdiSetReceivePacketFilter: ==> 
Set Receive Packet Filter Params: 
    Filter WDI_PACKET_FILTER_DIRECTED 
WdiSetReceivePacketFilter: <== 

WdiSetMulticastList: ==> 
WdiSetMulticastList: <== 

EvtRxRestart: Rx Restart on port[0] 

WdiSetMulticastList: ==> 
WdiSetMulticastList: <== 

WdiTaskScan: ==> 
WdiTaskScan: <== 

WdiTaskScan: ==> 
WdiTaskScan: <== 

EvtTxAbort: Tx Abort on Port[0] 
EvtRxStop: Rx Stop on port[0] 
EvtRxFlush: Rx Flush on port[0] 
EvtTalTxRxResetPort: TxRx Reset on Port[0] 
EvtRxRestart: Rx Restart on port[0] 

WdiTaskDot11Reset: ==> 
Task Dot11 Reset Params: 
   SetDefaultMIB: TRUE 
   ResetMACAddress: (not present) 
WdiTaskDot11Reset: <== 

WdiGetStatistics: ==> 
WdiGetStatistics: <== 

WdiSetPrivacyExemptionList: ==> 
Set Privacy ExemptionList Params: 
	PrivacyExemptionEntry[0]: 
		EtherType: 0x8e88 
		ExemptionActionType: WDI_EXEMPT_ON_KEY_MAPPING_KEY_UNAVAILABLE 
		ExemptionPacketType: WDI_EXEMPT_PACKET_TYPE_UNICAST 
WdiSetPrivacyExemptionList: <== 

WdiTaskConnect: ==> 
Connect Request Parameters: 
	ConnectionSettings: 
		RoamRequest: FALSE 
		HiddenNetwork: FALSE 
		ExcludeUnencrypted: TRUE 
		MFPEnabled: TRUE 
		HostFIPSModeEnabled: FALSE 
		RoamNeededReason: 0 
		RoamTrigger: 0 
		BSSTransitionSupported: FALSE 
	SSIDList: 
		SSID[0]: Xiaomi_AX1500 
	AuthenticationAlgorithms: 
		Algorithms[0]: WDI_AUTH_ALGO_RSNA_PSK 
	MulticastCipherAlgorithms: 
		Algorithms[0]: WDI_CIPHER_ALGO_GCMP 
		Algorithms[1]: WDI_CIPHER_ALGO_CCMP 
		Algorithms[2]: WDI_CIPHER_ALGO_NONE 
	UnicastCipherAlgorithms: 
		Algorithms[0]: WDI_CIPHER_ALGO_CCMP 
	HESSIDInfo: (not present) 
	AssociationRequestVendorIE: (not present) 
	ActivePhyTypeList: (not present) 
	DisallowedBSSIDs: (not present) 
	AllowedBSSIDs: 
		BSSID[0]: ff-ff-ff-ff-ff-ff 
	OWEDHIE: (not present) 
	PreferredBSSEntryList[0]: 
		BSSID: 90-fb-5d-58-55-75 
		ProbeResponseFrame: 316 byte 
		BeaconFrame: 236 byte 
		SignalInfo: 
			RSSI: 10 
			LinkQuality: 100 
		ChannelInfo: 
			BandId: WDI_BAND_ID_2400 
			ChannelNumber: 13 
		DeviceSpecificContext: 0 byte 
		PMKID: 0 byte 
		AssociationRequestVendorIE: 0 byte 
		FTInitialAssocParameters: 
			MDE: 0 byte 
		FTReAssocParameters: 
			MDE: 0 byte 
			FTE: 0 byte 
			PMKR0Name: 0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0 
		BSSSelectionParameters: WDI_BSS_SELECTION_HOST_PREFERRED 
UnsolicitedIndicateAssociationResult: Associates 90:fb:5d:58:55:75 with Peer[1] 
EvtTalTxRxPeerConfig: TxRx Config Peer[1] to QosCapable for Port[0] 
UnsolicitedIndicateLinkStateChanged: Peer 90:fb:5d:58:55:75 update TxLinkSpeed=600000, RxLinkSpeed=600000, LinkQuality=100 
WdiTaskConnect: <== 

WdiGetStatistics: ==> 
WdiGetStatistics: <== 

WdiSetReceivePacketFilter: ==> 
Set Receive Packet Filter Params: 
    Filter WDI_PACKET_FILTER_DIRECTED 
    Filter WDI_PACKET_FILTER_MULTICAST 
    Filter WDI_PACKET_FILTER_BROADCAST 
WdiSetReceivePacketFilter: <== 

WdiGetStatistics: ==> 
WdiGetStatistics: <== 

WdiSetReceivePacketFilter: ==> 
Set Receive Packet Filter Params: 
    Filter WDI_PACKET_FILTER_DIRECTED 
    Filter WDI_PACKET_FILTER_MULTICAST 
    Filter WDI_PACKET_FILTER_BROADCAST 
WdiSetReceivePacketFilter: <== 

WdiGetStatistics: ==> 
WdiGetStatistics: <== 

WdiGetStatistics: ==> 
WdiGetStatistics: <== 

RxHandleThreadCallback: Rx EAPoL Frame, Ethtype=0x8e88 
RxHandleThreadCallback: Rx EAPoL Frame, Ethtype=0x8e88 
RxHandleThreadCallback: Rx EAPoL Frame, Ethtype=0x8e88 

WdiTaskDisconnect: ==> 
Disconnect Request Parameters: 
	MacAddress: 90-fb-5d-58-55-75 
	Disassociation80211Reason: LEAVING_NETWORK_DEAUTH (Deauthenticated because sending STA is leaving (or has left) the BSS) 
WdiTaskDisconnect: <== 

EvtTxAbort: Tx Abort on Port[0] 
EvtRxStop: Rx Stop on port[0] 
EvtRxFlush: Rx Flush on port[0] 
EvtTalTxRxResetPort: TxRx Reset on Port[0] 
EvtRxRestart: Rx Restart on port[0] 

WdiTaskDot11Reset: ==> 
Task Dot11 Reset Params: 
   SetDefaultMIB: FALSE 
   ResetMACAddress: (not present) 
WdiTaskDot11Reset: <== 

WdiSetReceivePacketFilter: ==> 
Set Receive Packet Filter Params: 
    Filter WDI_PACKET_FILTER_DIRECTED 
WdiSetReceivePacketFilter: <== 

WdiTaskScan: ==> 
WdiTaskScan: <== 

.

It is the raw data of msg1 I indicate to WDI (with QoS filed, I also have try without QoS, see the following code):

88 02 7A 01 22 23 24 35 DC DC 90 FB 5D 58 55 75
90 FB 5D 58 55 75 10 00 07 00 AA AA 03 00 00 00
88 8E 02 03 00 5F 02 00 8A 00 10 00 00 00 00 00
00 00 02 17 ED 4C 7E 4C 0B 5C 9D 20 10 72 5D AB
2F 2F A9 9B B6 BB A9 E2 DC E1 CF BB 7A 8E 67 E2
DC 1D E8 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00

Source Code:

VOID
RxHandleThreadCallback(
	_In_  PTHREAD_INFO pThreadInfo
) {
	KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "%s: Entry \n", __FUNCTION__));

	NTSTATUS status;
	PADAPTER pAdapter = pThreadInfo->pAdapter;
	PMGMT_INFO pMgmtInfo = pAdapter->pMgmtInfo;
	PTX_RX_MANAGER pTxRxManager = &pMgmtInfo->TxRxManager;
	PRFD pRfd;

	ETHERNET_HEADER EthHeader = { 0 };
	DOT11_DATA_SHORT_HEADER Dot11Header = { 0 };
	LLC_HEADER LLCHeader = { .DSAP = 0xAA, .SSAP = 0xAA, .Control = 0x03 };
	UCHAR QosControl[2] = { 0 };

	PNET_BUFFER_LIST pNetBufferList;
	PMDL pMdl;
	PWDI_FRAME_METADATA	pWdiFrameMeta;

	WDI_RX_INDICATION_LEVEL IndicationLevel = WDI_RX_INDICATION_PASSIVE;
	WDI_PEER_ID PeerId = WDI_PEER_ANY;
	WDI_EXTENDED_TID ExTid = WDI_EXT_TID_UNKNOWN;
	BOOLEAN bRxFrameClassificationSupported = FALSE;// TODO

	while (TRUE) {
		status = KeWaitForSingleObject(&pTxRxManager->RfdSemaphore, Executive, KernelMode, TRUE, NULL);
		if (!NT_SUCCESS(status)) {
			KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "%s: %s thread waits for semaphore failed with status=%#x \n", __FUNCTION__, pThreadInfo->ThreadName, status));
			break;
		}

		NdisAcquireSpinLock(&pTxRxManager->RfdListLock);
		pRfd = (PRFD)RemoveHeadList(&pTxRxManager->RfdListHead);
		NdisReleaseSpinLock(&pTxRxManager->RfdListLock);

		struct wlan_sw_rx_data_hdr* rx_hdr = (struct wlan_sw_rx_data_hdr*)pRfd->Buffer;
		struct hw_rxhdr* hw_rxhdr = (struct hw_rxhdr*)(pRfd->Buffer + sizeof(struct wlan_sw_rx_data_hdr));

		PUCHAR ptr = pRfd->Buffer + sizeof(struct hw_rxhdr) + sizeof(struct wlan_sw_rx_data_hdr);
		ASSERT(hw_rxhdr->hwvect.len == pRfd->BufferLength - sizeof(struct hw_rxhdr) - sizeof(struct wlan_sw_rx_data_hdr));

		//
		// RxEngine can choose how to handle incoming data while paused. 
		// If possible, it should just buffer the data. 
		// Dropping data is also acceptable.
		//
		NdisAcquireSpinLock(&pTxRxManager->RxDataPathStateLock);
		if (pTxRxManager->RxDataPathState == RX_PAUSE) {
			NdisReleaseSpinLock(&pTxRxManager->RxDataPathStateLock);
			goto ReturnRfd;
		}
		NdisReleaseSpinLock(&pTxRxManager->RxDataPathStateLock);

		//
		// Since the firmware was originally designed for the Linux system
		// ptr is point to a 802.3 header
		//
		NdisMoveMemory(&EthHeader, ptr, sizeof(ETHERNET_HEADER));
		if (EthHeader.Protocol == 0x888e || EthHeader.Protocol == 0x8e88) {
			KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "%s: Rx EAPoL Frame, Ethtype=%#x \n", __FUNCTION__, EthHeader.Protocol));
		}

		//
		// Construct 802.11 Header
		//
		Dot11Header.FrameControl.Type = DOT11_FRAME_TYPE_DATA;
		Dot11Header.FrameControl.Subtype = DOT11_DATA_SUBTYPE_DATA;
		Dot11Header.FrameControl.FromDS = TRUE;
		NdisMoveMemory(Dot11Header.Address1, EthHeader.DestMAC, 6);
		NdisMoveMemory(Dot11Header.Address2, pMgmtInfo->AssociationResult.BSSID, 6);
		NdisMoveMemory(Dot11Header.Address3, EthHeader.SrcMAC, 6);

		LLCHeader.EtherType = EthHeader.Protocol;

		NdisMoveMemory(pRfd->Header, &Dot11Header, MAC_HEADER_LENGTH);
		NdisMoveMemory(pRfd->Header + MAC_HEADER_LENGTH, &LLCHeader, LLC_HEADER_LENGTH);
		pRfd->HeaderLength = MAC_HEADER_LENGTH + LLC_HEADER_LENGTH;

		Dot11Header.SequenceControl.SequenceNumber++;

		pRfd->Payload = ptr + sizeof(ETHERNET_HEADER);
		pRfd->PayloadLength = hw_rxhdr->hwvect.len - sizeof(ETHERNET_HEADER);

		pMdl = NdisAllocateMdl(
			pAdapter->NdisMiniportHandle,
			pRfd->Header,
			pRfd->HeaderLength
		);
		if (pMdl == NULL) {
			KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "%s: NdisAllocateMdl for header failed \n", __FUNCTION__));
			goto ReturnRfd;
		}

		pMdl->Next = NdisAllocateMdl(
			pAdapter->NdisMiniportHandle,
			pRfd->Payload,
			pRfd->PayloadLength
		);
		if (pMdl->Next == NULL) {
			KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "%s: NdisAllocateMdl for payload failed \n", __FUNCTION__));
			NdisFreeMdl(pMdl);
			goto ReturnRfd;
		}

		pNetBufferList = NdisAllocateNetBufferAndNetBufferList(
			pAdapter->RxNetBufferListPool,
			0,
			0,
			pMdl,
			0, // DataOffset
			pRfd->HeaderLength + pRfd->PayloadLength
		);
		if (pNetBufferList == NULL) {
			KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "%s: NdisAllocateNetBufferAndNetBufferList failed \n", __FUNCTION__));
			NdisFreeMdl(pMdl->Next);
			NdisFreeMdl(pMdl);
			goto ReturnRfd;
		}
		pNetBufferList->SourceHandle = pAdapter->NdisMiniportHandle;
		pRfd->pNetBufferList = pNetBufferList;

		pWdiFrameMeta = pAdapter->WdiDataApi.AllocateWiFiFrameMetaData(pAdapter->NdisMiniportDataPathHandle);
		if (pWdiFrameMeta == NULL) {
			KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "%s: AllocateWiFiFrameMetaData failed \n", __FUNCTION__));
			NdisFreeNetBufferList(pNetBufferList);
			NdisFreeMdl(pMdl->Next);
			NdisFreeMdl(pMdl);
			goto ReturnRfd;
		}
		pWdiFrameMeta->u.rxMetaData.PayloadType = WDI_FRAME_MSDU;
		pWdiFrameMeta->pNBL = pNetBufferList;

		NET_BUFFER_LIST_MINIPORT_RESERVED(pNetBufferList)[0] = pWdiFrameMeta;
		NET_BUFFER_LIST_MINIPORT_RESERVED(pNetBufferList)[1] = pRfd;

		NdisAcquireSpinLock(&pTxRxManager->ToIndRfdListLock);
		InsertTailList(&pTxRxManager->ToIndRfdListHead, &pRfd->ListEntry);
		NdisReleaseSpinLock(&pTxRxManager->ToIndRfdListLock);

		if (KeGetCurrentIrql() == PASSIVE_LEVEL) {
			IndicationLevel = WDI_RX_INDICATION_PASSIVE;
		}
		else if (KeGetCurrentIrql() == DISPATCH_LEVEL) {
			KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "%s: TODO: Irql=DISPATCH_LEVEL \n", __FUNCTION__));
		}
		else {
			KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "%s: TODO: Irql=%d \n", __FUNCTION__, KeGetCurrentIrql()));
		}

		if (!bRxFrameClassificationSupported) {
			PeerId = WDI_PEER_ANY;
			ExTid = WDI_EXT_TID_UNKNOWN;
		}

		pAdapter->WdiDataApi.RxInorderDataIndication(
			pAdapter->NdisMiniportDataPathHandle,
			IndicationLevel,
			PeerId,
			ExTid,
			NULL,
			&status
		);
		if (status == NDIS_STATUS_PAUSED) {
			KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "%s: Rx Manager pause frame and wait for it to be ready \n", __FUNCTION__));
			NdisAcquireSpinLock(&pTxRxManager->RxDataPathStateLock);
			pAdapter->pMgmtInfo->TxRxManager.RxDataPathState = RX_PAUSE;
			NdisReleaseSpinLock(&pTxRxManager->RxDataPathStateLock);
		}

		continue;

	ReturnRfd:
		pRfd->BufferLength = 0;
		NdisAcquireSpinLock(&pTxRxManager->IdleRfdListLock);
		InsertTailList(&pTxRxManager->IdleRfdListHead, &pRfd->ListEntry);
		NdisReleaseSpinLock(&pTxRxManager->IdleRfdListLock);
	}
}

_Use_decl_annotations_
VOID
EvtRxGetMpdus(
	_In_ TAL_TXRX_HANDLE	MiniportTalTxRxContext,
	_In_ WDI_PEER_ID	PeerId,
	_In_ WDI_EXTENDED_TID	ExTid,
	_Out_ PNET_BUFFER_LIST* ppNbl
) {
	PADAPTER pAdapter = MiniportTalTxRxContext;
	PTX_RX_MANAGER pTxRxManager = &pAdapter->pMgmtInfo->TxRxManager;

	NdisAcquireSpinLock(&pTxRxManager->ToIndRfdListLock);
	PRFD pRfd = (PRFD)RemoveHeadList(&pTxRxManager->ToIndRfdListHead);
	NdisReleaseSpinLock(&pTxRxManager->ToIndRfdListLock);

	*ppNbl = pRfd->pNetBufferList;
}