Sample Code
Windows Driver Samples/ Native Wi-Fi Miniport Sample Driver/ C++/ extap/ ap_assocmgr.c/
/*++ Copyright (c) Microsoft Corporation. All rights reserved. Module Name: ap_assocmgr.c Abstract: Implements the association manager for ExtAP Revision History: When What ---------- ---------------------------------------------- 09-24-2007 Created Notes: --*/ #include "precomp.h" #if DOT11_TRACE_ENABLED #include "ap_assocmgr.tmh" #endif /** DOT11 broadcast address */ DOT11_MAC_ADDRESS Dot11BroadcastAddress = {0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF}; // Default Privacy Exemption List DOT11_PRIVACY_EXEMPTION_LIST DefaultPrivacyExemptionList = { // Header { NDIS_OBJECT_TYPE_DEFAULT, // Type DOT11_PRIVACY_EXEMPTION_LIST_REVISION_1, // Revision sizeof(DOT11_PRIVACY_EXEMPTION_LIST) // Size }, // uNumOfEntries 0, // uTotalNumOfEntries 0, // PrivacyExemptionEntries {0} }; PDOT11_PRIVACY_EXEMPTION_LIST pDefaultPrivacyExemptionList = &DefaultPrivacyExemptionList; /** Internal supporting functions */ NDIS_STATUS AmMatchRsnInfo( _In_ PAP_ASSOC_MGR AssocMgr, _In_ PRSN_IE_INFO pRsnIeInfo, _Out_ PUSHORT pStatusCode ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; USHORT statusCode = DOT11_FRAME_STATUS_SUCCESSFUL; DOT11_CIPHER_ALGORITHM cipher; DOT11_AUTH_ALGORITHM authAlgo; ULONG index; do { if (pRsnIeInfo->Version != 1) { ndisStatus = NDIS_STATUS_FAILURE; statusCode = DOT11_FRAME_STATUS_UNSUPPORTED_RSN_IE_VERSION; break; } // // check group cipher // cipher = Dot11GetGroupCipherFromRSNIEInfo(pRsnIeInfo); if (cipher != AssocMgr->MulticastCipherAlgorithm) { ndisStatus = NDIS_STATUS_FAILURE; statusCode = DOT11_FRAME_STATUS_INVALID_GROUP_CIPHER; break; } // // check unicast cipher // for (index = 0; index < pRsnIeInfo->PairwiseCipherCount; index++) { cipher = Dot11GetPairwiseCipherFromRSNIEInfo(pRsnIeInfo, (USHORT)index); if (cipher == AssocMgr->UnicastCipherAlgorithm) break; } if (index == pRsnIeInfo->PairwiseCipherCount) { ndisStatus = NDIS_STATUS_FAILURE; statusCode = DOT11_FRAME_STATUS_INVALID_PAIRWISE_CIPHER; break; } // // check AKM suite // for (index = 0; index < pRsnIeInfo->AKMSuiteCount; index++) { authAlgo = Dot11GetAKMSuiteFromRSNIEInfo(pRsnIeInfo, (USHORT)index); if (authAlgo == AssocMgr->AuthAlgorithm) break; } if (index == pRsnIeInfo->AKMSuiteCount) { ndisStatus = NDIS_STATUS_FAILURE; statusCode = DOT11_FRAME_STATUS_INVALID_AKMP; break; } } while(FALSE); *pStatusCode = statusCode; return ndisStatus; } /** Enable WPS */ VOID AmEnableWps( _In_ PAP_ASSOC_MGR AssocMgr ) { AssocMgr->EnableWps = TRUE; MpTrace(COMP_ASSOC_MGR, DBG_NORMAL, ("Port(%u): WPS is enabled.", AP_GET_PORT_NUMBER(AssocMgr->ApPort))); } /** Disable WPS */ VOID AmDisableWps( _In_ PAP_ASSOC_MGR AssocMgr ) { AssocMgr->EnableWps = FALSE; MpTrace(COMP_ASSOC_MGR, DBG_NORMAL, ("Port(%u): WPS is disabled.", AP_GET_PORT_NUMBER(AssocMgr->ApPort))); } /** Get peer info */ VOID AmGetPeerInfo( _In_ PAP_STA_ENTRY StaEntry, _Out_ PDOT11_PEER_INFO PeerInfo ) { // clear association info NdisZeroMemory(PeerInfo, sizeof(DOT11_PEER_INFO)); // MAC address RtlCopyMemory( PeerInfo->MacAddress, StaEntry->MacHashEntry.MacKey, sizeof(DOT11_MAC_ADDRESS) ); // capability information PeerInfo->usCapabilityInformation = StaEntry->CapabilityInformation.usValue; // auth/cipher PeerInfo->AuthAlgo = StaEntry->AuthAlgo; PeerInfo->UnicastCipherAlgo = StaEntry->UnicastCipher; PeerInfo->MulticastCipherAlgo = StaEntry->MulticastCipher; // WPS enabled PeerInfo->bWpsEnabled = StaEntry->WpsEnabled; // listen interval PeerInfo->usListenInterval = StaEntry->ListenInterval; // supported rates RtlCopyMemory( PeerInfo->ucSupportedRates, StaEntry->SupportedRateSet.ucRateSet, sizeof(UCHAR) * StaEntry->SupportedRateSet.uRateSetLength ); // association ID PeerInfo->usAssociationID = StaEntry->Aid; // association state PeerInfo->AssociationState = StaEntry->AssocState; // power mode PeerInfo->PowerMode = StaEntry->PowerMode; // association up time PeerInfo->liAssociationUpTime = StaEntry->AssocUpTime; // statistics RtlCopyMemory( &PeerInfo->Statistics, &StaEntry->Statistics, sizeof(DOT11_PEER_STATISTICS) ); } /** * MAC hash table enum callback function to get peer information * The caller must hold a read lock and make sure the buffer is big enough. */ BOOLEAN AmGetPeerInfoCallback( _In_ PMAC_HASH_TABLE Table, _In_ PMAC_HASH_ENTRY MacEntry, _In_ PVOID CallbackCtxt ) { PDOT11_PEER_INFO_LIST peerInfoList = (PDOT11_PEER_INFO_LIST)CallbackCtxt; PAP_STA_ENTRY staEntry = CONTAINING_RECORD(MacEntry, AP_STA_ENTRY, MacHashEntry); UNREFERENCED_PARAMETER(Table); MPASSERT(peerInfoList->uNumOfEntries < peerInfoList->uTotalNumOfEntries); // get association info AmGetPeerInfo(staEntry, &peerInfoList->PeerInfo[peerInfoList->uNumOfEntries++]); return TRUE; } _Success_(return == NDIS_STATUS_SUCCESS) NDIS_STATUS AmAllocateStaEntry( _In_ const PAP_ASSOC_MGR AssocMgr, _In_ const DOT11_MAC_ADDRESS * StaMacAddr, _Outptr_ PAP_STA_ENTRY * StaEntry ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; PAP_STA_ENTRY staEntry = NULL; NDIS_TIMER_CHARACTERISTICS timerChar; do { // // Allocate the structure // MP_ALLOCATE_MEMORY( AP_GET_MP_HANDLE(AssocMgr->ApPort), &staEntry, sizeof(AP_STA_ENTRY), EXTAP_MEMORY_TAG ); if (NULL == staEntry) { MpTrace(COMP_ASSOC_MGR, DBG_SERIOUS, ("Port(%u): Failed to allocate %d bytes for station info.", AP_GET_PORT_NUMBER(AssocMgr->ApPort), sizeof(AP_STA_ENTRY))); ndisStatus = NDIS_STATUS_RESOURCES; break; } // // clear everything // NdisZeroMemory(staEntry, sizeof(AP_STA_ENTRY)); // // initialize association timer // NdisZeroMemory(&timerChar, sizeof(NDIS_TIMER_CHARACTERISTICS)); timerChar.Header.Type = NDIS_OBJECT_TYPE_TIMER_CHARACTERISTICS; timerChar.Header.Revision = NDIS_TIMER_CHARACTERISTICS_REVISION_1; timerChar.Header.Size = sizeof(NDIS_TIMER_CHARACTERISTICS); timerChar.AllocationTag = EXTAP_MEMORY_TAG; timerChar.TimerFunction = AmStaAssocTimeoutCallback; timerChar.FunctionContext = staEntry; ndisStatus = NdisAllocateTimerObject( AP_GET_MP_HANDLE(AssocMgr->ApPort), &timerChar, &staEntry->AssocTimer ); if (ndisStatus != NDIS_STATUS_SUCCESS) { MpTrace(COMP_ASSOC_MGR, DBG_SERIOUS, ("Failed to allocate station association timer.")); break; } // // initialize MAC hash entry // InitalizeMacHashEntry( &staEntry->MacHashEntry, StaMacAddr ); // // set pointer to the association manager // staEntry->AssocMgr = AssocMgr; // // set an invalid AID // staEntry->Aid = AP_INVALID_AID; // // initialize reference count // staEntry->RefCount = 1; // TODO: anything else to do? *StaEntry = staEntry; } while (FALSE); if (ndisStatus != NDIS_STATUS_SUCCESS) { MP_FREE_MEMORY(staEntry); } return ndisStatus; } /** * MAC hash table enum callback function to free station entry * The caller must hold a write lock. */ BOOLEAN AmRemoveStaEntryCallback( _In_ PMAC_HASH_TABLE Table, _In_ PMAC_HASH_ENTRY MacEntry, _In_ PVOID CallbackCtxt ) { LIST_ENTRY * head = (LIST_ENTRY *)CallbackCtxt; // list of removed station entries // remove the entry from the table RemoveMacHashEntry(Table, MacEntry); // insert it into the list InsertTailList(head, &MacEntry->Linkage); return TRUE; } VOID AmFreeAllStaEntries( _In_ PAP_ASSOC_MGR AssocMgr ) { LIST_ENTRY head; // list of stations to free LIST_ENTRY * entry; PMAC_HASH_ENTRY macEntry; PAP_STA_ENTRY staEntry; MP_RW_LOCK_STATE lockState; MPASSERT(AP_ASSOC_MGR_STATE_STOPPING == AssocMgr->State); // // Initialize the list head // InitializeListHead(&head); // // Acquire a write lock on the MAC hash table // MP_ACQUIRE_WRITE_LOCK(&AssocMgr->MacHashTableLock, &lockState); // // Remove all remaining entries // EnumMacEntry( &AssocMgr->MacHashTable, AmRemoveStaEntryCallback, &head ); // // Release lock // MP_RELEASE_WRITE_LOCK(&AssocMgr->MacHashTableLock, &lockState); // // Free all entries // while(!IsListEmpty(&head)) { entry = RemoveHeadList(&head); macEntry = CONTAINING_RECORD(entry, MAC_HASH_ENTRY, Linkage); staEntry = CONTAINING_RECORD(macEntry, AP_STA_ENTRY, MacHashEntry); // // Deref the station. // The station entry is freed when ref count reaches zero. // ApDerefSta(staEntry); } } /** Filter out unsupported rates */ VOID AmFilterUnsupportedRates( _In_ PAP_ASSOC_MGR AssocMgr, _In_ PDOT11_RATE_SET StaRateSet ) { ULONG i, j; // // Filter out any rates that are not supported by AP // i = 0; while (i < StaRateSet->uRateSetLength) { for (j = 0; j < AssocMgr->OperationalRateSet.uRateSetLength; j++) { if ((StaRateSet->ucRateSet[i] & 0x7f) == (AssocMgr->OperationalRateSet.ucRateSet[j] & 0x7f)) break; } // // remove the rate if it is not in AP's rate set // if (j == AssocMgr->OperationalRateSet.uRateSetLength) { StaRateSet->uRateSetLength--; StaRateSet->ucRateSet[i] = StaRateSet->ucRateSet[StaRateSet->uRateSetLength]; } else { i++; } } } /** Initialize Association Manager */ NDIS_STATUS ApInitializeAssocMgr( _In_ PAP_ASSOC_MGR AssocMgr, _In_ PMP_EXTAP_PORT ApPort ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; NDIS_TIMER_CHARACTERISTICS timerChar; do { // // association manager must not be initialized already // MPASSERT(AP_ASSOC_MGR_STATE_NOT_INITIALIZED == AssocMgr->State); if (AssocMgr->State != AP_ASSOC_MGR_STATE_NOT_INITIALIZED) { ndisStatus = NDIS_STATUS_INVALID_STATE; MpTrace(COMP_ASSOC_MGR, DBG_NORMAL, ("Port(%u): Association manager is already initialized.", AP_GET_PORT_NUMBER(AssocMgr->ApPort))); break; } // // set the pointer to the port // AssocMgr->ApPort = ApPort; // // initialize RW lock for MAC hash table // ndisStatus = MP_ALLOCATE_READ_WRITE_LOCK(&AssocMgr->MacHashTableLock, AP_GET_MP_HANDLE(AssocMgr->ApPort)); if (ndisStatus != NDIS_STATUS_SUCCESS) { MpTrace(COMP_ASSOC_MGR, DBG_NORMAL, ("Port(%u): Failed to allocate read write lock for association manager.", AP_GET_PORT_NUMBER(AssocMgr->ApPort))); break; } // // initialize station inactive timer // NdisZeroMemory(&timerChar, sizeof(NDIS_TIMER_CHARACTERISTICS)); timerChar.Header.Type = NDIS_OBJECT_TYPE_TIMER_CHARACTERISTICS; timerChar.Header.Revision = NDIS_TIMER_CHARACTERISTICS_REVISION_1; timerChar.Header.Size = sizeof(NDIS_TIMER_CHARACTERISTICS); timerChar.AllocationTag = EXTAP_MEMORY_TAG; timerChar.TimerFunction = AmStaInactiveTimeoutCallback; timerChar.FunctionContext = AssocMgr; ndisStatus = NdisAllocateTimerObject( AP_GET_MP_HANDLE(AssocMgr->ApPort), &timerChar, &AssocMgr->StaInactiveTimer ); if (ndisStatus != NDIS_STATUS_SUCCESS) { MpTrace(COMP_ASSOC_MGR, DBG_NORMAL, ("Failed to allocate station inactive timer.")); MP_FREE_READ_WRITE_LOCK(&AssocMgr->MacHashTableLock); break; } // // initialize events for handling asynchronous BSS start/stop calls // NdisInitializeEvent(&AssocMgr->StartBSSCompletionEvent); NdisInitializeEvent(&AssocMgr->StopBSSCompletionEvent); NdisInitializeEvent(&AssocMgr->SetChannelCompletionEvent); // // set inactive timer counter to zero // AssocMgr->StaInactiveTimerCounter = 0; // // initialize MAC table // InitMacHashTable(&AssocMgr->MacHashTable); // // AID table shall all be 0s // // TODO: anything needs to be done here? AssocMgr->State = AP_ASSOC_MGR_STATE_STOPPED; // // set defaults // AmSetDefault(AssocMgr); } while (FALSE); return ndisStatus; } /** Deinitialize Association Manager */ VOID ApDeinitializeAssocMgr( _In_ PAP_ASSOC_MGR AssocMgr ) { MPASSERT(AssocMgr != NULL); do { // // association manager must be in stopped state // MPASSERT(AP_ASSOC_MGR_STATE_STOPPED == AssocMgr->State); if (AssocMgr->State != AP_ASSOC_MGR_STATE_STOPPED) { MpTrace(COMP_ASSOC_MGR, DBG_NORMAL, ("Port(%u): Cannot deinitialize association manager when it is not in stopped state.", AP_GET_PORT_NUMBER(AssocMgr->ApPort))); break; } // // clean up first // AmCleanup(AssocMgr); // // deinit MAC table // DeinitializeMacHashTable(&AssocMgr->MacHashTable); // // Free the inactivity timer // if (AssocMgr->StaInactiveTimer) { NdisFreeTimerObject(AssocMgr->StaInactiveTimer); AssocMgr->StaInactiveTimer = NULL; } // // free locks // MP_FREE_READ_WRITE_LOCK(&AssocMgr->MacHashTableLock); AssocMgr->State = AP_ASSOC_MGR_STATE_NOT_INITIALIZED; } while (FALSE); } /** Start Association Manager */ NDIS_STATUS ApStartAssocMgr( _In_ PAP_ASSOC_MGR AssocMgr ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; USHORT i; MPASSERT(AssocMgr != NULL); do { // association shall be in stopped state MPASSERT(AP_ASSOC_MGR_STATE_STOPPED == AssocMgr->State); if (AssocMgr->State != AP_ASSOC_MGR_STATE_STOPPED) { ndisStatus = NDIS_STATUS_INVALID_STATE; MpTrace(COMP_ASSOC_MGR, DBG_NORMAL, ("Port(%u): Association manager is not in stopped state when a starting request is received.", AP_GET_PORT_NUMBER(AssocMgr->ApPort))); break; } AssocMgr->State = AP_ASSOC_MGR_STATE_STARTING; // TODO: validate AP settings if ( AssocMgr->bUseDefaultAlgorithms ) { AmSetDefaultAuthAndCipherAlgorithms( AssocMgr ); } // // Reset AID // for (i = 0; i < AP_AID_TABLE_SIZE; i++) { AssocMgr->AidTable[i] = 0; } AssocMgr->StaInactiveTimerCounter = 0; AssocMgr->State = AP_ASSOC_MGR_STATE_STARTED; MpTrace(COMP_ASSOC_MGR, DBG_NORMAL, ("Port(%u): Association manager started.", AP_GET_PORT_NUMBER(AssocMgr->ApPort))); } while (FALSE); return ndisStatus; } /** * Stop Association Manager * 1. Stop accepting new association requests * 2. Disassociate all stations and send corresponding indications * 3. Doesn't have to wait for the pending association request decision from IM * 4. Cancel on-going scan and wait for it to complete */ VOID ApStopAssocMgr( _In_ PAP_ASSOC_MGR AssocMgr ) { BOOLEAN timerCancelled = FALSE; MPASSERT(AssocMgr != NULL); do { // Do nothing if association manager is not in started or starting state MPASSERT(AP_ASSOC_MGR_STATE_STARTED == AssocMgr->State || AP_ASSOC_MGR_STATE_STARTING == AssocMgr->State); if (AssocMgr->State != AP_ASSOC_MGR_STATE_STARTED && AssocMgr->State != AP_ASSOC_MGR_STATE_STARTING) { break; } AssocMgr->State = AP_ASSOC_MGR_STATE_STOPPING; // TODO: stop association manager // // Disassociate all stations // AmDisassociatePeerRequest( AssocMgr, &Dot11BroadcastAddress, DOT11_MGMT_REASON_UPSPEC_REASON // TODO: a better reason code ); // // Station inactive timer counter shall be zero // MPASSERT(0 == AssocMgr->StaInactiveTimerCounter); if (AssocMgr->StaInactiveTimerCounter != 0) { MpTrace(COMP_ASSOC_MGR, DBG_SERIOUS, ("Port(%u): Station inactive timer counter is not zero.", AP_GET_PORT_NUMBER(AssocMgr->ApPort))); AssocMgr->StaInactiveTimerCounter = 0; } // // Stop the periodic timer // timerCancelled = NdisCancelTimerObject(AssocMgr->StaInactiveTimer); if (timerCancelled) { MpTrace(COMP_ASSOC_MGR, DBG_LOUD, ("Port(%u): Station inactive timer cancelled successfully.", AP_GET_PORT_NUMBER(AssocMgr->ApPort))); } // // Cancel on-going scan // if (ApGetScanState(AssocMgr)) { Mp11CancelScan(AP_GET_ADAPTER(AssocMgr->ApPort), AP_GET_MP_PORT(AssocMgr->ApPort)); // // Wait for scan to complete // while (ApGetScanState(AssocMgr)) { NdisMSleep(10000); } } AssocMgr->State = AP_ASSOC_MGR_STATE_STOPPED; MpTrace(COMP_ASSOC_MGR, DBG_NORMAL, ("Port(%u): Association manager stopped.", AP_GET_PORT_NUMBER(AssocMgr->ApPort))); } while (FALSE); } /** Restart Association Manager */ NDIS_STATUS ApRestartAssocMgr( _In_ PAP_ASSOC_MGR AssocMgr ) { UNREFERENCED_PARAMETER(AssocMgr); // TODO: anything to do? return NDIS_STATUS_SUCCESS; } /** * Pause Association Manager * 1. Cancel on-going scan and wait for it to complete */ NDIS_STATUS ApPauseAssocMgr( _In_ PAP_ASSOC_MGR AssocMgr ) { if (ApGetScanState(AssocMgr)) { // // Cancel on-going scan // Mp11CancelScan(AP_GET_ADAPTER(AssocMgr->ApPort), AP_GET_MP_PORT(AssocMgr->ApPort)); // // Wait for scan to complete // while (ApGetScanState(AssocMgr)) { NdisMSleep(10000); } } // TODO: anything else? return NDIS_STATUS_SUCCESS; } /** * Set association manager to its default based on the hardware capability * and registry settings */ VOID AmSetDefault( _In_ PAP_ASSOC_MGR AssocMgr ) { BOOLEAN set; DOT11_PHY_TYPE phyType; PVNIC vnic = AP_GET_VNIC(AssocMgr->ApPort); MPASSERT(AssocMgr != NULL); // Setting default SSID RtlZeroMemory( &(AssocMgr->Ssid), sizeof(AssocMgr->Ssid) ); // default BSSID is the hardware address RtlCopyMemory( AssocMgr->Bssid, VNic11QueryMACAddress(AP_GET_VNIC(AssocMgr->ApPort)), sizeof(DOT11_MAC_ADDRESS) ); AssocMgr->AuthAlgorithm = AP_DEFAULT_AUTHENTICATION_ALGORITHM; AssocMgr->UnicastCipherAlgorithm = AP_DEFAULT_UNICAST_CIPHER_ALGORITHM; AssocMgr->MulticastCipherAlgorithm = AP_DEFAULT_MULTICAST_CIPHER_ALGORITHM; AssocMgr->bUseDefaultAlgorithms = TRUE; AssocMgr->ExcludeUnencrypted = AP_DEFAULT_EXCLUDE_UNENCRYPTED; AssocMgr->PrivacyExemptionList = pDefaultPrivacyExemptionList; AssocMgr->EnableWps = AP_DEFAULT_ENABLE_WPS; // Use something other than the default to force the change // to be persisted in the default layer. AssocMgr->BeaconEnabled = !(AP_DEFAULT_ENABLE_BEACON); // Persist the default Beacon enabled flag in the VNic layer // BUG: 203538 - This should be set (VOID)AmSetApBeaconMode( AssocMgr , AP_DEFAULT_ENABLE_BEACON ); // TODO: add PHY // TODO: add HW capability logic // TODO: capability NdisZeroMemory(&AssocMgr->Capability, sizeof(DOT11_CAPABILITY)); AssocMgr->Capability.ESS = 1; // privacy AssocMgr->Capability.Privacy = (AssocMgr->UnicastCipherAlgorithm != DOT11_CIPHER_ALGO_NONE) ? 1 : 0; // PHY type phyType = VNic11QueryCurrentPhyType(vnic); switch (phyType) { case dot11_phy_type_erp: set = VNic11QueryShortSlotTimeOptionImplemented(vnic, FALSE); if (set) { set = VNic11QueryShortSlotTimeOptionEnabled(vnic, FALSE); } AssocMgr->Capability.ShortSlotTime = set ? 1 : 0; set = VNic11QueryDsssOfdmOptionImplemented(vnic, FALSE); if (set) { set = VNic11QueryDsssOfdmOptionEnabled(vnic, FALSE); } AssocMgr->Capability.DSSSOFDM = set ? 1 : 0; // fall through case dot11_phy_type_hrdsss: set = VNic11QueryShortPreambleOptionImplemented(vnic, FALSE); AssocMgr->Capability.ShortPreamble = set ? 1: 0; set = VNic11QueryPbccOptionImplemented(vnic, FALSE); AssocMgr->Capability.PBCC = set ? 1: 0; set = VNic11QueryChannelAgilityPresent(vnic, FALSE); if (set) { set = VNic11QueryChannelAgilityEnabled(vnic, FALSE); } AssocMgr->Capability.ChannelAgility = set ? 1 : 0; } // TODO: OperationalRateSet VNic11QueryOperationalRateSet( AP_GET_VNIC(AssocMgr->ApPort), &AssocMgr->OperationalRateSet, FALSE ); } /** * Set association manager to its defaults after a Reset OID */ VOID AmRestoreDefault( _In_ PAP_ASSOC_MGR AssocMgr ) { AssocMgr->AuthAlgorithm = AP_DEFAULT_AUTHENTICATION_ALGORITHM; AssocMgr->UnicastCipherAlgorithm = AP_DEFAULT_UNICAST_CIPHER_ALGORITHM; AssocMgr->MulticastCipherAlgorithm = AP_DEFAULT_MULTICAST_CIPHER_ALGORITHM; AssocMgr->bUseDefaultAlgorithms = TRUE; AmSetDefaultAuthAndCipherAlgorithms( AssocMgr ); AssocMgr->ExcludeUnencrypted = AP_DEFAULT_EXCLUDE_UNENCRYPTED; AmCleanupPrivacyExemptionList( AssocMgr ); AmCleanupDesiredSsidList( AssocMgr ); AmSetWpsEnabled( AssocMgr , AP_DEFAULT_ENABLE_WPS ); } /** * Set default cipher based on the auth algorithm and hardware capability */ VOID AmSetDefaultCipher( _In_ PAP_ASSOC_MGR AssocMgr ) { PVNIC vnic = AP_GET_VNIC(AssocMgr->ApPort); BOOLEAN WEP40Implemented = VNic11WEP40Implemented(vnic); BOOLEAN WEP104Implemented = VNic11WEP104Implemented(vnic); BOOLEAN CCMPImplemented = VNic11CCMPImplemented(vnic, dot11_BSS_type_infrastructure); switch (AssocMgr->AuthAlgorithm) { case DOT11_AUTH_ALGO_80211_OPEN: if (WEP104Implemented || WEP40Implemented) { AssocMgr->UnicastCipherAlgorithm = DOT11_CIPHER_ALGO_WEP; AssocMgr->MulticastCipherAlgorithm = DOT11_CIPHER_ALGO_WEP; } else { AssocMgr->UnicastCipherAlgorithm = DOT11_CIPHER_ALGO_NONE; AssocMgr->MulticastCipherAlgorithm = DOT11_CIPHER_ALGO_NONE; } break; case DOT11_AUTH_ALGO_WPA: case DOT11_AUTH_ALGO_WPA_PSK: case DOT11_AUTH_ALGO_RSNA: case DOT11_AUTH_ALGO_RSNA_PSK: MPASSERT(VNic11TKIPImplemented(vnic) || CCMPImplemented); if (CCMPImplemented) { AssocMgr->UnicastCipherAlgorithm = DOT11_CIPHER_ALGO_CCMP; AssocMgr->MulticastCipherAlgorithm = DOT11_CIPHER_ALGO_CCMP; } else { AssocMgr->UnicastCipherAlgorithm = DOT11_CIPHER_ALGO_TKIP; AssocMgr->MulticastCipherAlgorithm = DOT11_CIPHER_ALGO_TKIP; } break; default: MPASSERT(FALSE); return; } // // Set ciphers to VNIC // VNic11SetCipher(vnic, TRUE, AssocMgr->UnicastCipherAlgorithm); VNic11SetCipher(vnic, FALSE, AssocMgr->MulticastCipherAlgorithm); } /** * Set default auth and cipher algorithms in VNIC */ VOID AmSetDefaultAuthAndCipherAlgorithms ( _In_ PAP_ASSOC_MGR AssocMgr ) { MPASSERT(AP_DEFAULT_AUTHENTICATION_ALGORITHM == AssocMgr->AuthAlgorithm); VNic11SetAuthentication ( AP_GET_VNIC(AssocMgr->ApPort), AssocMgr->AuthAlgorithm ); AmSetDefaultCipher( AssocMgr ); return; } /** * Clean up Association Manager Privacy Exemption List */ VOID AmCleanupPrivacyExemptionList( _In_ PAP_ASSOC_MGR AssocMgr ) { MPASSERT(AssocMgr->PrivacyExemptionList); if (AssocMgr->PrivacyExemptionList != pDefaultPrivacyExemptionList) { MP_FREE_MEMORY(AssocMgr->PrivacyExemptionList); AssocMgr->PrivacyExemptionList = pDefaultPrivacyExemptionList; } } /** * Clean up Association Manager Desired SSID List */ VOID AmCleanupDesiredSsidList( _In_ PAP_ASSOC_MGR AssocMgr ) { // Setting default SSID RtlZeroMemory( &(AssocMgr->Ssid), sizeof(AssocMgr->Ssid) ); } /** * Clean up Association Manager * Shall be called after ApStopAssocMgr() * All clients must have been disassociated at this point */ VOID AmCleanup( _In_ PAP_ASSOC_MGR AssocMgr ) { MPASSERT(AssocMgr != NULL); do { // association shall be in stopped state MPASSERT(AP_ASSOC_MGR_STATE_STOPPED == AssocMgr->State); if (AssocMgr->State != AP_ASSOC_MGR_STATE_STOPPED) { MpTrace(COMP_ASSOC_MGR, DBG_NORMAL, ("Port(%u): Cannot cleanup association manager when it is not in stopped state.", AP_GET_PORT_NUMBER(AssocMgr->ApPort))); break; } // TODO: free memory blocks AmCleanupPrivacyExemptionList( AssocMgr ); } while (FALSE); } /** * Get an available AID * Each AID is represented as a bit in the AID table. * The bit is set to 1 if an AID is used. */ USHORT AmAllocateAid( _In_ PAP_ASSOC_MGR AssocMgr ) { // TODO: hold a lock? USHORT aid = AP_INVALID_AID; USHORT i; UCHAR *byteFound = NULL; UCHAR byteMask = 1; // first find the UCHAR that has bit with value 0 for (i = 0; i < AP_AID_TABLE_SIZE; i++) { if (AssocMgr->AidTable[i] != AP_AID_TABLE_UNIT_MASK) { byteFound = &AssocMgr->AidTable[i]; break; } } if (byteFound != NULL) { aid = i * AP_AID_TABLE_UNIT_SIZE; // find the position of the first bit that is 0 while (TRUE) { aid++; if ((*byteFound & byteMask) == 0) { // find the first bit that is 0, set it to 1 *byteFound |= byteMask; break; } byteMask <<= 1; } } MPASSERT(aid > 0); MPASSERT(aid != AP_INVALID_AID); MPASSERT(aid <= AP_MAX_AID); // add AID header aid |= AP_AID_HEADER; return aid; } /** Free an AID */ VOID AmFreeAid( _In_ PAP_ASSOC_MGR AssocMgr, _In_ USHORT Aid ) { // TODO: hold a lock? UCHAR byteMask = 1; // remove AID header Aid &= ~(AP_AID_HEADER); MPASSERT(Aid > 0 && Aid <= AP_MAX_AID); if (Aid > 0 && Aid <= AP_MAX_AID) { // prepare the byte mask byteMask <<= (Aid - 1) % AP_AID_TABLE_UNIT_SIZE; // update the UCHAR that has the corresponding bit for the AID AssocMgr->AidTable[(Aid - 1) / AP_AID_TABLE_UNIT_SIZE] &= ~byteMask; } } /** * Internal functions for OIDs */ _Success_(return == NDIS_STATUS_SUCCESS) NDIS_STATUS AmQuerySsid( _In_ PAP_ASSOC_MGR AssocMgr, _Out_ PDOT11_SSID_LIST SsidList, _In_ ULONG InformationBufferLength, _Out_ PULONG BytesWritten, _Out_when_invalid_ndis_length_ PULONG BytesNeeded ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; MPASSERT(AssocMgr->State != AP_ASSOC_MGR_STATE_NOT_INITIALIZED); do { // only return one SSID if (InformationBufferLength < sizeof(DOT11_SSID_LIST)) { SET_NEEDED_BUFFER_SIZE_AND_BREAK(sizeof(DOT11_SSID_LIST)); } if ( AssocMgr->Ssid.uSSIDLength ) { SsidList->uNumOfEntries = SsidList->uTotalNumOfEntries = 1; RtlCopyMemory( &SsidList->SSIDs[0], &AssocMgr->Ssid, sizeof(DOT11_SSID) ); } else { SsidList->uNumOfEntries = SsidList->uTotalNumOfEntries = 0; RtlZeroMemory( &SsidList->SSIDs[0], sizeof(DOT11_SSID) ); } *BytesWritten = sizeof(DOT11_SSID_LIST); } while (FALSE); return ndisStatus; } NDIS_STATUS AmSetSsid( _In_ PAP_ASSOC_MGR AssocMgr, _In_ PDOT11_SSID_LIST SsidList ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; BOOLEAN bOk = FALSE; MPASSERT(AssocMgr->State != AP_ASSOC_MGR_STATE_NOT_INITIALIZED); do { // SSID can only be set when association manager is stopped. MPASSERT(AP_ASSOC_MGR_STATE_STOPPED == AssocMgr->State); bOk = ( (1 == SsidList->uNumOfEntries) && (1 == SsidList->uTotalNumOfEntries) && (1 <= SsidList->SSIDs[0].uSSIDLength) && (DOT11_SSID_MAX_LENGTH >= SsidList->SSIDs[0].uSSIDLength) ) ? TRUE : FALSE; if (!bOk) { ndisStatus = NDIS_STATUS_INVALID_DATA; MpTrace(COMP_ASSOC_MGR, DBG_NORMAL, ("Invalid desired SSID.")); break; } if (AssocMgr->State != AP_ASSOC_MGR_STATE_STOPPED) { ndisStatus = NDIS_STATUS_INVALID_STATE; MpTrace(COMP_ASSOC_MGR, DBG_NORMAL, ("Port(%u): Association manager is not in stopped state when setting desired SSID.", AP_GET_PORT_NUMBER(AssocMgr->ApPort))); break; } // copy SSID RtlCopyMemory( &AssocMgr->Ssid, &SsidList->SSIDs[0], sizeof(DOT11_SSID) ); } while (FALSE); return ndisStatus; } NDIS_STATUS AmSetBssid( _In_ PAP_ASSOC_MGR AssocMgr, _In_ DOT11_MAC_ADDRESS * Bssid ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; MPASSERT(AssocMgr->State != AP_ASSOC_MGR_STATE_NOT_INITIALIZED); do { // BSSID can only be set when association manager is stopped. MPASSERT(AP_ASSOC_MGR_STATE_STOPPED == AssocMgr->State); if (AssocMgr->State != AP_ASSOC_MGR_STATE_STOPPED) { ndisStatus = NDIS_STATUS_INVALID_STATE; MpTrace(COMP_ASSOC_MGR, DBG_NORMAL, ("Port(%u): Association manager is not in stopped state when setting BSSID.", AP_GET_PORT_NUMBER(AssocMgr->ApPort))); break; } // copy BSSID RtlCopyMemory( AssocMgr->Bssid, *Bssid, sizeof(DOT11_MAC_ADDRESS) ); } while (FALSE); return ndisStatus; } _Success_(return == NDIS_STATUS_SUCCESS) NDIS_STATUS AmQueryAuthAlgorithm( _In_ PAP_ASSOC_MGR AssocMgr, _Out_ PDOT11_AUTH_ALGORITHM_LIST EnabledAuthenticationAlgorithm, _In_ ULONG InformationBufferLength, _Out_ PULONG BytesWritten, _Out_when_invalid_ndis_length_ PULONG BytesNeeded ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; MPASSERT(AssocMgr->State != AP_ASSOC_MGR_STATE_NOT_INITIALIZED); do { // only return one auth algorithm if (InformationBufferLength < sizeof(DOT11_AUTH_ALGORITHM_LIST)) { SET_NEEDED_BUFFER_SIZE_AND_BREAK(sizeof(DOT11_AUTH_ALGORITHM_LIST)); } EnabledAuthenticationAlgorithm->uNumOfEntries = EnabledAuthenticationAlgorithm->uTotalNumOfEntries = 1; EnabledAuthenticationAlgorithm->AlgorithmIds[0] = AssocMgr->AuthAlgorithm; *BytesWritten = sizeof(DOT11_AUTH_ALGORITHM_LIST); } while (FALSE); return ndisStatus; } NDIS_STATUS AmSetAuthAlgorithm( _In_ PAP_ASSOC_MGR AssocMgr, _In_ PDOT11_AUTH_ALGORITHM_LIST EnabledAuthenticationAlgorithm ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; MPASSERT(AssocMgr->State != AP_ASSOC_MGR_STATE_NOT_INITIALIZED); do { // Auth algorithm can only be set when association manager is stopped. MPASSERT(AP_ASSOC_MGR_STATE_STOPPED == AssocMgr->State); if (AssocMgr->State != AP_ASSOC_MGR_STATE_STOPPED) { ndisStatus = NDIS_STATUS_INVALID_STATE; MpTrace(COMP_ASSOC_MGR, DBG_NORMAL, ("Port(%u): Association manager is not in stopped state when setting auth algorithm.", AP_GET_PORT_NUMBER(AssocMgr->ApPort))); break; } // // Only supports one auth algorithm // if (EnabledAuthenticationAlgorithm->uNumOfEntries != 1) { ndisStatus = NDIS_STATUS_INVALID_DATA; break; } // // Validate auth algorithm // if (!ApValidateAuthAlgo( AssocMgr->ApPort, EnabledAuthenticationAlgorithm->AlgorithmIds[0] )) { // // Invalid auth algorithm // ndisStatus = NDIS_STATUS_INVALID_DATA; MpTrace(COMP_ASSOC_MGR, DBG_NORMAL, ("Port(%u): Invalid auth algorithm (%u).", AP_GET_PORT_NUMBER(AssocMgr->ApPort), EnabledAuthenticationAlgorithm->AlgorithmIds[0])); break; } // // Set it to VNIC // VNic11SetAuthentication( AP_GET_VNIC(AssocMgr->ApPort), EnabledAuthenticationAlgorithm->AlgorithmIds[0] ); AssocMgr->AuthAlgorithm = EnabledAuthenticationAlgorithm->AlgorithmIds[0]; // // Set default ciphers // AmSetDefaultCipher(AssocMgr); AssocMgr->bUseDefaultAlgorithms = FALSE; } while (FALSE); return ndisStatus; } _Success_(return == NDIS_STATUS_SUCCESS) NDIS_STATUS AmQueryUnicastCipherAlgorithm( _In_ PAP_ASSOC_MGR AssocMgr, _Out_ PDOT11_CIPHER_ALGORITHM_LIST EnabledUnicastCipherAlgorithm, _In_ ULONG InformationBufferLength, _Out_ PULONG BytesWritten, _Out_when_invalid_ndis_length_ PULONG BytesNeeded ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; MPASSERT(AssocMgr->State != AP_ASSOC_MGR_STATE_NOT_INITIALIZED); do { // only return one unicast cipher algorithm if (InformationBufferLength < sizeof(DOT11_CIPHER_ALGORITHM_LIST)) { SET_NEEDED_BUFFER_SIZE_AND_BREAK(sizeof(DOT11_CIPHER_ALGORITHM_LIST)); } EnabledUnicastCipherAlgorithm->uNumOfEntries = EnabledUnicastCipherAlgorithm->uTotalNumOfEntries = 1; EnabledUnicastCipherAlgorithm->AlgorithmIds[0] = AssocMgr->UnicastCipherAlgorithm; *BytesWritten = sizeof(DOT11_CIPHER_ALGORITHM_LIST); } while (FALSE); return ndisStatus; } NDIS_STATUS AmSetUnicastCipherAlgorithm( _In_ PAP_ASSOC_MGR AssocMgr, _In_ PDOT11_CIPHER_ALGORITHM_LIST EnabledUnicastCipherAlgorithm ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; MPASSERT(AssocMgr->State != AP_ASSOC_MGR_STATE_NOT_INITIALIZED); do { // // Unicast cipher algorithm can only be set when association manager is stopped. // MPASSERT(AP_ASSOC_MGR_STATE_STOPPED == AssocMgr->State); if (AssocMgr->State != AP_ASSOC_MGR_STATE_STOPPED) { ndisStatus = NDIS_STATUS_INVALID_STATE; MpTrace(COMP_ASSOC_MGR, DBG_NORMAL, ("Port(%u): Association manager is not in stopped state when setting unicast cipher algorithm.", AP_GET_PORT_NUMBER(AssocMgr->ApPort))); break; } // // Only supports one cipher algorithm // if (EnabledUnicastCipherAlgorithm->uNumOfEntries != 1) { ndisStatus = NDIS_STATUS_INVALID_DATA; break; } // // Validate auth/cipher pair // if (!ApValidateUnicastAuthCipherPair( AssocMgr->ApPort, AssocMgr->AuthAlgorithm, EnabledUnicastCipherAlgorithm->AlgorithmIds[0] )) { // // Invalid cipher algorithm // ndisStatus = NDIS_STATUS_INVALID_DATA; MpTrace(COMP_ASSOC_MGR, DBG_NORMAL, ("Port(%u): Invalid unicast cipher algorithm (%u) for auth algorithm (%u).", AP_GET_PORT_NUMBER(AssocMgr->ApPort), EnabledUnicastCipherAlgorithm->AlgorithmIds[0], AssocMgr->AuthAlgorithm)); break; } // // Set it to VNIC // VNic11SetCipher( AP_GET_VNIC(AssocMgr->ApPort), TRUE, // is unicast EnabledUnicastCipherAlgorithm->AlgorithmIds[0] ); AssocMgr->UnicastCipherAlgorithm = EnabledUnicastCipherAlgorithm->AlgorithmIds[0]; // update Capability info AssocMgr->Capability.Privacy = (AssocMgr->UnicastCipherAlgorithm != DOT11_CIPHER_ALGO_NONE) ? 1 : 0; } while (FALSE); return ndisStatus; } _Success_(return == NDIS_STATUS_SUCCESS) NDIS_STATUS AmQueryMulticastCipherAlgorithm( _In_ PAP_ASSOC_MGR AssocMgr, _Out_ PDOT11_CIPHER_ALGORITHM_LIST EnabledMulticastCipherAlgorithm, _In_ ULONG InformationBufferLength, _Out_ PULONG BytesWritten, _Out_when_invalid_ndis_length_ PULONG BytesNeeded ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; MPASSERT(AssocMgr->State != AP_ASSOC_MGR_STATE_NOT_INITIALIZED); do { // only return one multicast cipher algorithm if (InformationBufferLength < sizeof(DOT11_CIPHER_ALGORITHM_LIST)) { SET_NEEDED_BUFFER_SIZE_AND_BREAK(sizeof(DOT11_CIPHER_ALGORITHM_LIST)); } EnabledMulticastCipherAlgorithm->uNumOfEntries = EnabledMulticastCipherAlgorithm->uTotalNumOfEntries = 1; EnabledMulticastCipherAlgorithm->AlgorithmIds[0] = AssocMgr->MulticastCipherAlgorithm; *BytesWritten = sizeof(DOT11_CIPHER_ALGORITHM_LIST); } while (FALSE); return ndisStatus; } NDIS_STATUS AmSetMulticastCipherAlgorithm( _In_ PAP_ASSOC_MGR AssocMgr, _In_ PDOT11_CIPHER_ALGORITHM_LIST EnabledMulticastCipherAlgorithm ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; MPASSERT(AssocMgr->State != AP_ASSOC_MGR_STATE_NOT_INITIALIZED); do { // Multicast cipher algorithm can only be set when association manager is stopped. MPASSERT(AP_ASSOC_MGR_STATE_STOPPED == AssocMgr->State); if (AssocMgr->State != AP_ASSOC_MGR_STATE_STOPPED) { ndisStatus = NDIS_STATUS_INVALID_STATE; MpTrace(COMP_ASSOC_MGR, DBG_NORMAL, ("Port(%u): Association manager is not in stopped state when setting multicast cipher algorithm.", AP_GET_PORT_NUMBER(AssocMgr->ApPort))); break; } // // Only supports one cipher algorithm // if (EnabledMulticastCipherAlgorithm->uNumOfEntries != 1) { ndisStatus = NDIS_STATUS_INVALID_DATA; break; } // // Validate auth/cipher pair // if (!ApValidateMulticastAuthCipherPair( AssocMgr->ApPort, AssocMgr->AuthAlgorithm, EnabledMulticastCipherAlgorithm->AlgorithmIds[0] )) { // // Invalid cipher algorithm // ndisStatus = NDIS_STATUS_INVALID_DATA; MpTrace(COMP_ASSOC_MGR, DBG_NORMAL, ("Port(%u): Invalid multicast cipher algorithm (%u) for auth algorithm (%u).", AP_GET_PORT_NUMBER(AssocMgr->ApPort), EnabledMulticastCipherAlgorithm->AlgorithmIds[0], AssocMgr->AuthAlgorithm)); break; } // // Set it to VNIC // VNic11SetCipher( AP_GET_VNIC(AssocMgr->ApPort), FALSE, // is multicast EnabledMulticastCipherAlgorithm->AlgorithmIds[0] ); AssocMgr->MulticastCipherAlgorithm = EnabledMulticastCipherAlgorithm->AlgorithmIds[0]; } while (FALSE); return ndisStatus; } NDIS_STATUS AmSetOperationalRateSet( _In_ PAP_ASSOC_MGR AssocMgr, _In_ PDOT11_RATE_SET OperationalRateSet ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; MPASSERT(AssocMgr->State != AP_ASSOC_MGR_STATE_NOT_INITIALIZED); do { // Operational rate set can only be set when association manager is stopped. MPASSERT(AP_ASSOC_MGR_STATE_STOPPED == AssocMgr->State); if (AssocMgr->State != AP_ASSOC_MGR_STATE_STOPPED) { ndisStatus = NDIS_STATUS_INVALID_STATE; MpTrace(COMP_ASSOC_MGR, DBG_NORMAL, ("Port(%u): Association manager is not in stopped state when setting operational rate set.", AP_GET_PORT_NUMBER(AssocMgr->ApPort))); break; } // copy rate set RtlCopyMemory( &AssocMgr->OperationalRateSet, OperationalRateSet, sizeof(DOT11_RATE_SET) ); } while (FALSE); return ndisStatus; } NDIS_STATUS AmSetExcludeUnencrypted( _In_ PAP_ASSOC_MGR AssocMgr, _In_ BOOLEAN ExcludeUnencrypted ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; // // No need to hold the lock. // This is can only be set before AP starts. // AssocMgr->ExcludeUnencrypted = ExcludeUnencrypted; return ndisStatus; } _Success_(return == NDIS_STATUS_SUCCESS) NDIS_STATUS AmQueryPrivacyExemptionList( _In_ PAP_ASSOC_MGR AssocMgr, _Out_ PDOT11_PRIVACY_EXEMPTION_LIST PrivacyExemptionList, _In_ ULONG InformationBufferLength, _Out_ PULONG BytesWritten, _Out_when_invalid_ndis_length_ PULONG BytesNeeded ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; ULONG requiredSize = 0; do { ndisStatus = ValiatePrivacyExemptionListSize( AssocMgr->PrivacyExemptionList, InformationBufferLength, &requiredSize ); if (NDIS_STATUS_INVALID_LENGTH == ndisStatus) { // // the buffer is not big enough // *BytesNeeded = requiredSize; break; } if (ndisStatus != NDIS_STATUS_SUCCESS) { // // this should not happen // MPASSERT(FALSE); break; } // // the buffer is big enough, copy the list // RtlCopyMemory( PrivacyExemptionList, AssocMgr->PrivacyExemptionList, requiredSize ); *BytesWritten = requiredSize; } while (FALSE); return ndisStatus; } NDIS_STATUS AmSetPrivacyExemptionList( _In_ PAP_ASSOC_MGR AssocMgr, _In_ PDOT11_PRIVACY_EXEMPTION_LIST PrivacyExemptionList ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; ULONG tmpExemptionListSize = 0; PDOT11_PRIVACY_EXEMPTION_LIST tmpExemptionList = NULL; do { // // Get the required list size first // if (!GetRequiredListSize( FIELD_OFFSET(DOT11_PRIVACY_EXEMPTION_LIST, PrivacyExemptionEntries), sizeof(DOT11_PRIVACY_EXEMPTION), PrivacyExemptionList->uNumOfEntries, &tmpExemptionListSize )) { ndisStatus = NDIS_STATUS_INVALID_DATA; break; } // // Allocate memory for the new list // MP_ALLOCATE_MEMORY( AP_GET_MP_HANDLE(AssocMgr->ApPort), &tmpExemptionList, tmpExemptionListSize, EXTAP_MEMORY_TAG ); if (NULL == tmpExemptionList) { MpTrace(COMP_ASSOC_MGR, DBG_SERIOUS, ("Port(%u): Failed to allocate %d bytes for the exemption list.", AP_GET_PORT_NUMBER(AssocMgr->ApPort), tmpExemptionListSize)); ndisStatus = NDIS_STATUS_RESOURCES; break; } // // Copy the list // RtlCopyMemory( tmpExemptionList, PrivacyExemptionList, tmpExemptionListSize ); // // No need to hold the lock. // This is can only be set before AP starts. // AmCleanupPrivacyExemptionList( AssocMgr ); AssocMgr->PrivacyExemptionList = tmpExemptionList; } while (FALSE); return ndisStatus; } NDIS_STATUS AmSetWpsEnabled( _In_ PAP_ASSOC_MGR AssocMgr, _In_ BOOLEAN WpsEnabled ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; MPASSERT(AssocMgr->State != AP_ASSOC_MGR_STATE_NOT_INITIALIZED); // WPS can be enabled any time if (WpsEnabled && !AssocMgr->EnableWps) { // WPS needs to be enabled // Beacon and probe response shall be handled differently AmEnableWps(AssocMgr); AssocMgr->EnableWps = TRUE; } else if (!WpsEnabled && AssocMgr->EnableWps) { // WPS needs to be disabled // Beacon and probe response shall be handled differently AmDisableWps(AssocMgr); AssocMgr->EnableWps = FALSE; } return ndisStatus; } NDIS_STATUS AmSetApBeaconMode( _In_ PAP_ASSOC_MGR AssocMgr, _In_ BOOLEAN BeaconEnabled ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; PVNIC vnic = AP_GET_VNIC(AssocMgr->ApPort); MPASSERT(AssocMgr->State != AP_ASSOC_MGR_STATE_NOT_INITIALIZED); BeaconEnabled = BeaconEnabled?TRUE:FALSE; while (BeaconEnabled != AssocMgr->BeaconEnabled) { // VNic Layer needs to be informed of change. ndisStatus = VNic11SetBeaconEnabledFlag( vnic, BeaconEnabled ); if (NDIS_STATUS_SUCCESS != ndisStatus) { break; } // persist the value locally. AssocMgr->BeaconEnabled = BeaconEnabled; break; } return ndisStatus; } /** enum peer information */ _Success_(return == NDIS_STATUS_SUCCESS) NDIS_STATUS AmEnumPeerInfo( _In_ PAP_ASSOC_MGR AssocMgr, _Out_ PDOT11_PEER_INFO_LIST PeerInfo, _In_ ULONG InformationBufferLength, _Out_ PULONG BytesWritten, _Out_when_invalid_ndis_length_ PULONG BytesNeeded ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; MP_RW_LOCK_STATE lockState; ULONG staEntryCount = 0; ULONG requiredSize = 0; do { // acquire the read lock on the MAC hash table MP_ACQUIRE_READ_LOCK(&AssocMgr->MacHashTableLock, &lockState); // get station entry count staEntryCount = GetMacHashTableEntryCount(&AssocMgr->MacHashTable); // calculate the required size if (!GetRequiredListSize( FIELD_OFFSET(DOT11_PEER_INFO_LIST, PeerInfo), sizeof(DOT11_PEER_INFO), staEntryCount, &requiredSize )) { // overflow should not happen MPASSERT(FALSE); ndisStatus = NDIS_STATUS_INVALID_DATA; break; } if (InformationBufferLength < requiredSize) { SET_NEEDED_BUFFER_SIZE_AND_BREAK(requiredSize); } // the buffer is large enough to hold all information PeerInfo->uTotalNumOfEntries = staEntryCount; PeerInfo->uNumOfEntries = 0; // enumerate all station entries and get association info EnumMacEntry( &AssocMgr->MacHashTable, AmGetPeerInfoCallback, PeerInfo ); // all association information is obtained MPASSERT(PeerInfo->uNumOfEntries == PeerInfo->uTotalNumOfEntries); *BytesWritten = requiredSize; } while (FALSE); // release the read lock MP_RELEASE_READ_LOCK(&AssocMgr->MacHashTableLock, &lockState); return ndisStatus; } NDIS_STATUS AmSetStaPortState( _In_ PAP_ASSOC_MGR AssocMgr, _In_ PDOT11_MAC_ADDRESS PeerMacAddr, _In_ BOOLEAN PortOpen ) { NDIS_STATUS ndisStatus = NDIS_STATUS_INVALID_STATE; PMAC_HASH_ENTRY macEntry; PAP_STA_ENTRY staEntry; MP_RW_LOCK_STATE lockState; // // Acquire read lock on the Mac table // MP_ACQUIRE_READ_LOCK(&AssocMgr->MacHashTableLock, &lockState); // // Remove a specific station // macEntry = LookupMacHashTable( &AssocMgr->MacHashTable, PeerMacAddr ); if (macEntry != NULL) { // // Change port state // staEntry = CONTAINING_RECORD(macEntry, AP_STA_ENTRY, MacHashEntry); if (ApGetStaAssocState(staEntry) == dot11_assoc_state_auth_assoc) { // // Port state is only meaningful after the state is associated. // ApSetStaPortState( staEntry, PortOpen ? STA_PORT_STATE_OPEN : STA_PORT_STATE_CLOSED ); ndisStatus = NDIS_STATUS_SUCCESS; } } // // Release the read lock // MP_RELEASE_READ_LOCK(&AssocMgr->MacHashTableLock, &lockState); return ndisStatus; } /** * Scan complete callback */ NDIS_STATUS ApScanComplete( _In_ PMP_PORT Port, _In_ PVOID Data ) { NDIS_STATUS ndisStatus = *(NDIS_STATUS*)Data; PAP_ASSOC_MGR assocMgr = AP_GET_ASSOC_MGR(MP_GET_AP_PORT(Port)); MP_RW_LOCK_STATE lockState; PVOID scanRequestId = NULL; // // TODO: use another lock? // MP_ACQUIRE_WRITE_LOCK(&assocMgr->MacHashTableLock, &lockState); MPASSERT(assocMgr->ScanRequest.Dot11ScanRequest != NULL); if (assocMgr->ScanRequest.Dot11ScanRequest != NULL) { MP_FREE_MEMORY(assocMgr->ScanRequest.Dot11ScanRequest); assocMgr->ScanRequest.Dot11ScanRequest = NULL; scanRequestId = assocMgr->ScanRequestId; assocMgr->ScanRequestId = NULL; } MP_RELEASE_WRITE_LOCK(&assocMgr->MacHashTableLock, &lockState); // // Reset scan state // if (!ApSetScanState(assocMgr, FALSE)) { // // This shouldn't happen // MPASSERT(FALSE); } ApIndicateDot11Status( assocMgr->ApPort, NDIS_STATUS_DOT11_SCAN_CONFIRM, scanRequestId, &ndisStatus, sizeof(NDIS_STATUS) ); return NDIS_STATUS_SUCCESS; } /** * MAC hash table enum callback function to check whether a station * is in the process of connecting to the AP. * The caller must hold a write lock. */ BOOLEAN AmPortStateStaEntryCallback( _In_ PMAC_HASH_TABLE Table, _In_ PMAC_HASH_ENTRY MacEntry, _In_ PVOID CallbackCtxt ) { PAP_STA_ENTRY staEntry = CONTAINING_RECORD(MacEntry, AP_STA_ENTRY, MacHashEntry); BOOLEAN * connectionInProcess = (BOOLEAN *)CallbackCtxt; UNREFERENCED_PARAMETER(Table); if (*connectionInProcess) { return FALSE; } else if (ApGetStaPortState(staEntry) != STA_PORT_STATE_OPEN) { // // Presence of the station entry indicates authentication started. // The connection completes when the station port state is set to open. // *connectionInProcess = TRUE; // // TODO: timeout for port state update? // return FALSE; } return TRUE; } /** * Handle scan request * Ignore non-forced scan request if a station is connecting to AP */ NDIS_STATUS AmScanRequest( _In_ PAP_ASSOC_MGR AssocMgr, _In_ PVOID ScanRequestId, _In_ PDOT11_SCAN_REQUEST_V2 ScanRequest, _In_ ULONG ScanRequestBufferLength ) { NDIS_STATUS ndisStatus = NDIS_STATUS_INVALID_STATE; MP_RW_LOCK_STATE lockState; BOOLEAN connectionInProcess = FALSE; PDOT11_SCAN_REQUEST_V2 localScanRequest = NULL; // // Acquire read lock on the Mac table // MP_ACQUIRE_READ_LOCK(&AssocMgr->MacHashTableLock, &lockState); // // Enum station entries to check whether a connection is in process // EnumMacEntry( &AssocMgr->MacHashTable, AmPortStateStaEntryCallback, &connectionInProcess ); // // Release the read lock // MP_RELEASE_READ_LOCK(&AssocMgr->MacHashTableLock, &lockState); do { if (ApGetScanState(AssocMgr)) { MpTrace(COMP_SCAN, DBG_SERIOUS, ("Port(%u): External scan already in progress. Ignoring new request.", AP_GET_PORT_NUMBER(AssocMgr->ApPort))); ndisStatus = NDIS_STATUS_DOT11_MEDIA_IN_USE; break; } if (connectionInProcess && ScanRequest->dot11ScanType != dot11_scan_type_forced) { MpTrace(COMP_OID, DBG_SERIOUS, ("Port(%u): Non-forced scan is ignored as a connection is in progress on this adapter.", AP_GET_PORT_NUMBER(AssocMgr->ApPort))); ndisStatus = NDIS_STATUS_DOT11_MEDIA_IN_USE; break; } // // Make a local copy of the scan request // MP_ALLOCATE_MEMORY( AP_GET_MP_HANDLE(AssocMgr->ApPort), &localScanRequest, ScanRequestBufferLength, EXTAP_MEMORY_TAG ); if (NULL == localScanRequest) { MpTrace(COMP_OID, DBG_SERIOUS, ("Port(%u): Failed to allocate %d bytes for scan request.", AP_GET_PORT_NUMBER(AssocMgr->ApPort), ScanRequestBufferLength)); ndisStatus = NDIS_STATUS_RESOURCES; break; } // // Set scan state // if (ApSetScanState(AssocMgr, TRUE)) { // // Somebody sets the scan state already // This shouldn't happen // MPASSERT(FALSE); break; } // // Save scan request // RtlCopyMemory( localScanRequest, ScanRequest, ScanRequestBufferLength ); // // TODO: use another lock? // MP_ACQUIRE_WRITE_LOCK(&AssocMgr->MacHashTableLock, &lockState); if (AssocMgr->ScanRequest.Dot11ScanRequest) { // Shouldn't happen MPASSERT(FALSE); MP_FREE_MEMORY(AssocMgr->ScanRequest.Dot11ScanRequest); } NdisZeroMemory(&AssocMgr->ScanRequest, sizeof(MP_SCAN_REQUEST)); AssocMgr->ScanRequest.Dot11ScanRequest = localScanRequest; AssocMgr->ScanRequest.ScanRequestBufferLength = ScanRequestBufferLength; AssocMgr->ScanRequestId = ScanRequestId; MP_RELEASE_WRITE_LOCK(&AssocMgr->MacHashTableLock, &lockState); ndisStatus = Mp11Scan( AP_GET_ADAPTER(AssocMgr->ApPort), AP_GET_MP_PORT(AssocMgr->ApPort), &AssocMgr->ScanRequest, ApScanComplete ); if (NDIS_STATUS_SUCCESS == ndisStatus) { // // Don't free the local scan request // It will be freed when scan completes. // localScanRequest = NULL; } else { // // TODO: use another lock? // MP_ACQUIRE_WRITE_LOCK(&AssocMgr->MacHashTableLock, &lockState); MPASSERT(AssocMgr->ScanRequest.Dot11ScanRequest == localScanRequest); AssocMgr->ScanRequest.Dot11ScanRequest = NULL; MP_RELEASE_WRITE_LOCK(&AssocMgr->MacHashTableLock, &lockState); // // Reset scan state // if (!ApSetScanState(AssocMgr, FALSE)) { // // This shouldn't happen // MPASSERT(FALSE); } } } while (FALSE); if (localScanRequest != NULL) { MP_FREE_MEMORY(localScanRequest); } return ndisStatus; } NDIS_STATUS AmDisassociatePeerRequest( _In_ PAP_ASSOC_MGR AssocMgr, _In_ PDOT11_MAC_ADDRESS PeerMacAddr, _In_ USHORT Reason ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; LIST_ENTRY head; // list of stations to disassociate LIST_ENTRY * entry; PMAC_HASH_ENTRY macEntry; PAP_STA_ENTRY staEntry; MP_RW_LOCK_STATE lockState; PUCHAR pucMacAddr = (PUCHAR) PeerMacAddr; MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): disassociate STA %02X-%02X-%02X-%02X-%02X-%02X. Reason = 0x%08x.", AP_GET_PORT_NUMBER(AssocMgr->ApPort), pucMacAddr[0], pucMacAddr[1], pucMacAddr[2], pucMacAddr[3], pucMacAddr[4], pucMacAddr[5], Reason )); // // Initialize the list head // InitializeListHead(&head); // // Acquire write lock on the Mac table // MP_ACQUIRE_WRITE_LOCK(&AssocMgr->MacHashTableLock, &lockState); if (DOT11_IS_BROADCAST(PeerMacAddr)) { // // Remove all stations // EnumMacEntry( &AssocMgr->MacHashTable, AmRemoveStaEntryCallback, &head ); } else { // // Remove a specific station // macEntry = RemoveFromMacHashTable( &AssocMgr->MacHashTable, PeerMacAddr ); if (macEntry != NULL) { // // Insert it to the list of stations to disassociate // InsertTailList(&head, &macEntry->Linkage); } } // // Release the write lock // MP_RELEASE_WRITE_LOCK(&AssocMgr->MacHashTableLock, &lockState); // // Disassociate all stations // while(!IsListEmpty(&head)) { entry = RemoveHeadList(&head); macEntry = CONTAINING_RECORD(entry, MAC_HASH_ENTRY, Linkage); staEntry = CONTAINING_RECORD(macEntry, AP_STA_ENTRY, MacHashEntry); AmDeauthenticateSta( AssocMgr, staEntry, DOT11_DISASSOC_REASON_OS, TRUE, // Send deauth frame Reason ); } return ndisStatus; } /* * This function is called inside a direct call. * The caller must ensure the AP port stays valid. * The function must handle potential concurrent accesses * to shared resources. */ NDIS_STATUS AmAssociationDecision( _In_ PAP_ASSOC_MGR AssocMgr, _In_ PDOT11_INCOMING_ASSOC_DECISION AssocDecision ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; PMAC_HASH_ENTRY macEntry = NULL; PAP_STA_ENTRY staEntry = NULL; MP_RW_LOCK_STATE lockState; MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): Get association decision for STA %02X-%02X-%02X-%02X-%02X-%02X. Accept = %!BOOLEAN!, Reason = 0x%08x.", AP_GET_PORT_NUMBER(AssocMgr->ApPort), AssocDecision->PeerMacAddr[0], AssocDecision->PeerMacAddr[1], AssocDecision->PeerMacAddr[2], AssocDecision->PeerMacAddr[3], AssocDecision->PeerMacAddr[4], AssocDecision->PeerMacAddr[5], AssocDecision->bAccept, AssocDecision->usReasonCode )); // // acquire a write lock on Mac table because we may need to update it // MP_ACQUIRE_WRITE_LOCK(&AssocMgr->MacHashTableLock, &lockState); // // Lookup the station entry from Mac table // macEntry = LookupMacHashTable( &AssocMgr->MacHashTable, &AssocDecision->PeerMacAddr ); if (NULL == macEntry) { MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): STA is not in the peer table.", AP_GET_PORT_NUMBER(AssocMgr->ApPort))); ndisStatus = NDIS_STATUS_INVALID_DATA; } else { staEntry = CONTAINING_RECORD(macEntry, AP_STA_ENTRY, MacHashEntry); // // Cache the decision. // This call is made by OS in the same thread of NDIS_STATUS_DOT11_INCOMING_ASSOC_REQUEST_RECEIVED // indication. // We have to process the decision after NDIS_STATUS_DOT11_INCOMING_ASSOC_REQUEST_RECEIVED indication returns, // so that we can send NDIS_STATUS_DOT11_INCOMING_ASSOC_COMPLETION indication. // If we process the decision here and send NDIS_STATUS_DOT11_INCOMING_ASSOC_COMPLETION indication, // the nested indications will result in a deadlock. // staEntry->AcceptAssoc = AssocDecision->bAccept; staEntry->Reason = AssocDecision->usReasonCode; // BUGBUG - handle OS specified IEs in assoc response after Windows 7 MPASSERT( 0 == AssocDecision->uAssocResponseIEsLength ); } MP_RELEASE_WRITE_LOCK(&AssocMgr->MacHashTableLock, &lockState); return ndisStatus; } /** * Process the association decision made by OS. */ NDIS_STATUS AmProcessAssociationDecision( _In_ PAP_ASSOC_MGR AssocMgr, _In_ BOOLEAN reAssociation, _In_reads_bytes_(AssocReqFrameSize) PDOT11_MGMT_HEADER AssocReqFrame, _In_ USHORT AssocReqFrameSize, _In_ PDOT11_INCOMING_ASSOC_COMPLETION_PARAMETERS AssocCompletePara, _In_ ULONG AssocCompleteParaSize ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; PUCHAR assocRespFrame = NULL; // association response USHORT assocRespFrameSize = 0; BOOLEAN sendResponse = FALSE; PMAC_HASH_ENTRY macEntry = NULL; PAP_STA_ENTRY staEntry = NULL; BOOLEAN releaseMacTableLock = FALSE; MP_RW_LOCK_STATE lockState; // NDIS indication BOOLEAN indicateAssocComplete = FALSE; ULONG errorStatus = 0; // success MPASSERT(AssocReqFrame != NULL); MPASSERT(AssocCompletePara != NULL); do { MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): Process association decision for STA %02X-%02X-%02X-%02X-%02X-%02X. Reassociation = %!BOOLEAN!.", AP_GET_PORT_NUMBER(AssocMgr->ApPort), AssocReqFrame->SA[0], AssocReqFrame->SA[1], AssocReqFrame->SA[2], AssocReqFrame->SA[3], AssocReqFrame->SA[4], AssocReqFrame->SA[5], reAssociation )); // // acquire a write lock on Mac table because we may need to update it // MP_ACQUIRE_WRITE_LOCK(&AssocMgr->MacHashTableLock, &lockState); releaseMacTableLock = TRUE; // // Lookup the station entry from Mac table // macEntry = LookupMacHashTable( &AssocMgr->MacHashTable, &AssocReqFrame->SA ); if (NULL == macEntry) { ndisStatus = NDIS_STATUS_INVALID_DATA; break; } staEntry = CONTAINING_RECORD(macEntry, AP_STA_ENTRY, MacHashEntry); MPASSERT(dot11_assoc_state_auth_unassoc == staEntry->AssocState); if (staEntry->AssocState != dot11_assoc_state_auth_unassoc) { ndisStatus = NDIS_STATUS_INVALID_STATE; break; } indicateAssocComplete = TRUE; // // Create association response frame // if (staEntry->AcceptAssoc) { ndisStatus = AmCreateAssocRespFrame( AssocMgr, staEntry, reAssociation, AssocReqFrame, AssocReqFrameSize, DOT11_FRAME_STATUS_SUCCESSFUL, &assocRespFrame, &assocRespFrameSize ); } else { ndisStatus = AmCreateAssocRespFrame( AssocMgr, staEntry, reAssociation, AssocReqFrame, AssocReqFrameSize, staEntry->Reason, &assocRespFrame, &assocRespFrameSize ); } if (ndisStatus != NDIS_STATUS_SUCCESS) { MpTrace(COMP_ASSOC_MGR, DBG_SERIOUS, ("Port(%u): Failed to create association response frame. Status = 0x%08x.", AP_GET_PORT_NUMBER(AssocMgr->ApPort), ndisStatus)); errorStatus = ndisStatus; break; } sendResponse = TRUE; // // update station entry // if (staEntry->AcceptAssoc) { MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): STA is associated.", AP_GET_PORT_NUMBER(AssocMgr->ApPort) )); staEntry->AssocState = dot11_assoc_state_auth_assoc; // // Reset statistics // NdisZeroMemory(&staEntry->Statistics, sizeof(DOT11_PEER_STATISTICS)); // // Association up time // NdisGetCurrentSystemTime(&staEntry->AssocUpTime); // // Start inactive timer // AmStartStaInactiveTimer(AssocMgr); } else { // Otherwise, leave it at the unassociated state MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): STA is not associated.", AP_GET_PORT_NUMBER(AssocMgr->ApPort) )); } } while (FALSE); // // Prepare association complete indication // // // Association complete indication // if (indicateAssocComplete) { if (!staEntry->AcceptAssoc) { // // OS rejects the association. // Set the status code. // errorStatus = staEntry->Reason; } // // Can be a success or failure. // If it is failure, it must be an error from OS // if (errorStatus != 0) { AmPrepareAssocCompletePara( AssocMgr, NULL, // no STA entry is required for a failure &AssocReqFrame->SA, errorStatus, DOT11_ASSOC_ERROR_SOURCE_OS, reAssociation, // reassociation NULL, // no association request frame 0, NULL, // no association response frame 0, AssocCompletePara, &AssocCompleteParaSize ); } else { // // Mac headers of the request and response frames // are not needed. // AmPrepareAssocCompletePara( AssocMgr, staEntry, &AssocReqFrame->SA, errorStatus, DOT11_ASSOC_ERROR_SOURCE_OS, reAssociation, // reassociation Add2Ptr(AssocReqFrame, DOT11_MGMT_HEADER_SIZE), AssocReqFrameSize - DOT11_MGMT_HEADER_SIZE, Add2Ptr(assocRespFrame, DOT11_MGMT_HEADER_SIZE), assocRespFrameSize - DOT11_MGMT_HEADER_SIZE, AssocCompletePara, &AssocCompleteParaSize ); } } // // release locks // if (releaseMacTableLock) { MP_RELEASE_WRITE_LOCK(&AssocMgr->MacHashTableLock, &lockState); } // // Send management frames // if (sendResponse) { // // Send association response // MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): Send association response frame.", AP_GET_PORT_NUMBER(AssocMgr->ApPort))); ndisStatus = ApSendMgmtPacket( AssocMgr->ApPort, assocRespFrame, assocRespFrameSize ); if (ndisStatus != NDIS_STATUS_SUCCESS) { // TODO: do we disassociate? MpTrace(COMP_ASSOC_MGR, DBG_SERIOUS, ("Port(%u): Failed to send association response frame. Status = 0x%08x.", AP_GET_PORT_NUMBER(AssocMgr->ApPort), ndisStatus)); } MP_FREE_MEMORY(assocRespFrame); assocRespFrame = NULL; } // // Send NDIS indications // if (indicateAssocComplete) { ApIndicateDot11Status( AssocMgr->ApPort, NDIS_STATUS_DOT11_INCOMING_ASSOC_COMPLETION, NULL, // no request ID AssocCompletePara, AssocCompleteParaSize ); } return ndisStatus; } /** * Internal functions for station management */ /** * Processing station authentication frame * Receving authentication for a station that is already associated/authenticated * will put it in unauthenticated/unassociated state. */ VOID AmProcessStaAuthentication( _In_ PAP_ASSOC_MGR AssocMgr, _In_reads_bytes_(PacketSize) PDOT11_MGMT_HEADER MgmtPacket, _In_ USHORT PacketSize ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; PVNIC vnic = AP_GET_VNIC(AssocMgr->ApPort); PDOT11_AUTH_FRAME authFrame = NULL; PUCHAR authRespFrame = NULL; // auth response USHORT authRespFrameSize = 0; USHORT statusCode = DOT11_FRAME_STATUS_SUCCESSFUL; BOOLEAN sendResponse = FALSE; PMAC_HASH_ENTRY macEntry = NULL; PAP_STA_ENTRY staEntry = NULL; BOOLEAN releaseMacTableLock = FALSE; MP_RW_LOCK_STATE lockState = {0}; /** NDIS indications */ DOT11_DISASSOCIATION_PARAMETERS disassocPara; BOOLEAN indicateDisassoc = FALSE; DOT11_INCOMING_ASSOC_STARTED_PARAMETERS assocStartPara = {0}; BOOLEAN indicateAssocStart = FALSE; PDOT11_INCOMING_ASSOC_COMPLETION_PARAMETERS assocCompletePara = NULL; ULONG assocCompleteParaSize = 0; BOOLEAN indicateAssocComplete = FALSE; UCHAR errorSource = DOT11_ASSOC_ERROR_SOURCE_OS; // error source for association complete indication ULONG errorStatus = 0; // only handle authentication MPASSERT(DOT11_FRAME_TYPE_MANAGEMENT == MgmtPacket->FrameControl.Type); MPASSERT(DOT11_MGMT_SUBTYPE_AUTHENTICATION == MgmtPacket->FrameControl.Subtype); MPASSERT(PacketSize >= DOT11_MGMT_HEADER_SIZE + sizeof(DOT11_AUTH_FRAME)); do { // // Check BSSID and DA // if (!MP_COMPARE_MAC_ADDRESS(MgmtPacket->BSSID, VNic11QueryCurrentBSSID(vnic)) || !MP_COMPARE_MAC_ADDRESS(MgmtPacket->DA, VNic11QueryMACAddress(vnic))) { break; } MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): Process authentication request for STA %02X-%02X-%02X-%02X-%02X-%02X.", AP_GET_PORT_NUMBER(AssocMgr->ApPort), MgmtPacket->SA[0], MgmtPacket->SA[1], MgmtPacket->SA[2], MgmtPacket->SA[3], MgmtPacket->SA[4], MgmtPacket->SA[5] )); // // Acquire a write lock on Mac table because we may need to update it // MP_ACQUIRE_WRITE_LOCK(&AssocMgr->MacHashTableLock, &lockState); // // Remove the station entry from Mac table if it exists // macEntry = RemoveFromMacHashTable( &AssocMgr->MacHashTable, &MgmtPacket->SA ); MP_RELEASE_WRITE_LOCK(&AssocMgr->MacHashTableLock, &lockState); if (macEntry != NULL) { staEntry = CONTAINING_RECORD(macEntry, AP_STA_ENTRY, MacHashEntry); // // We need to deauthenticate the station // AmDeauthenticateSta( AssocMgr, staEntry, DOT11_DISASSOC_REASON_OS, // TODO: a better reason code FALSE, // Don't need to send deauth frame 0 ); // // Station entry is destroyed after the ref count reaches zero // staEntry = NULL; } // // Now there is no entry for the station. // Let's start from the beginning. // // // Acquire a write lock on Mac table because we may need to update it // MP_ACQUIRE_WRITE_LOCK(&AssocMgr->MacHashTableLock, &lockState); releaseMacTableLock = TRUE; // // Get Auth Frame // authFrame = (PDOT11_AUTH_FRAME)Add2Ptr(MgmtPacket, DOT11_MGMT_HEADER_SIZE); // // We shall send response even if authentication fails // sendResponse = TRUE; // // Check the associated/associating station limit // if (GetMacHashTableEntryCount(&AssocMgr->MacHashTable) >= AP_STA_MAX_ENTRIES_DEFAULT) { MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): Deny request because too many stations are associated/associating.", AP_GET_PORT_NUMBER(AssocMgr->ApPort))); statusCode = DOT11_FRAME_STATUS_ASSOC_DENIED_AP_BUSY; // indicate association start and complete indicateAssocStart = indicateAssocComplete = TRUE; errorSource = DOT11_ASSOC_ERROR_SOURCE_OS; errorStatus = (ULONG)NDIS_STATUS_RESOURCES; // TODO: a better status code? break; } // // Check authentication algorithm, we only accept open system authentication // if (authFrame->usAlgorithmNumber != DOT11_AUTH_OPEN_SYSTEM) { statusCode = DOT11_FRAME_STATUS_UNSUPPORTED_AUTH_ALGO; MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): Invalid authentication algorithm %u.", AP_GET_PORT_NUMBER(AssocMgr->ApPort), authFrame->usAlgorithmNumber)); // // Indicate association start and complete // indicateAssocStart = indicateAssocComplete = TRUE; errorSource = DOT11_ASSOC_ERROR_SOURCE_REMOTE; errorStatus = statusCode; break; } // // Check transaction sequence number // if (authFrame->usXid != 1) { statusCode = DOT11_FRAME_STATUS_INVALID_AUTH_XID; MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): Invalid authentication XID %u.", AP_GET_PORT_NUMBER(AssocMgr->ApPort), authFrame->usXid)); // // Indicate association start and complete // indicateAssocStart = indicateAssocComplete = TRUE; errorSource = DOT11_ASSOC_ERROR_SOURCE_REMOTE; errorStatus = statusCode; break; } // // Open system authentication always succeeds // // // Create a new station entry // if (AmAllocateStaEntry( AssocMgr, &MgmtPacket->SA, &staEntry ) != NDIS_STATUS_SUCCESS) { MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): Couldn't add sta entry.", AP_GET_PORT_NUMBER(AssocMgr->ApPort))); statusCode = DOT11_FRAME_STATUS_FAILURE; // TODO: a better status code? // // Indicate association start and complete // indicateAssocStart = indicateAssocComplete = TRUE; errorSource = DOT11_ASSOC_ERROR_SOURCE_OS; errorStatus = (ULONG)NDIS_STATUS_RESOURCES; // TODO: Is it the right way? break; } // // Authentication succeeds. // Send association start indication after receiving association request or timing out. // staEntry->AssocState = dot11_assoc_state_auth_unassoc; // // Add the station entry to the Mac table // InsertMacHashTable(&AssocMgr->MacHashTable, &staEntry->MacHashEntry); // // Start association timer // AmStartStaAssocTimer(staEntry); } while (FALSE); if (sendResponse) { // // Create auth response frame, can be a success or failure // AmCreateAuthRespFrame( AssocMgr, staEntry, MgmtPacket, PacketSize, statusCode, &authRespFrame, &authRespFrameSize ); MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): Create authentication response. StatusCode = 0x%08x.", AP_GET_PORT_NUMBER(AssocMgr->ApPort), statusCode)); } else { // // Do nothing if cannot create auth response frame. // Let the timeout precedure clean up the stale entry. // } // // Prepare association start and complete indications // if (indicateAssocStart) { // // Must be a failure case and complete shall be indicated. // MPASSERT(indicateAssocComplete); AmPrepareAssocStartPara( &MgmtPacket->SA, &assocStartPara ); } if (indicateAssocComplete) { // // Allocate association complete parameters // if (AmAllocAssocCompletePara( AssocMgr, 0, // no association request frame 0, // no association response frame &assocCompletePara, &assocCompleteParaSize ) != NDIS_STATUS_SUCCESS) { MpTrace(COMP_ASSOC_MGR, DBG_SERIOUS, ("Port(%u): Failed to allocate association completion parameters.", AP_GET_PORT_NUMBER(AssocMgr->ApPort))); // // Cannot create association complete parameter. Don't send any indication. // indicateAssocStart = indicateAssocComplete = FALSE; } else { // // Fill in information // AmPrepareAssocCompletePara( AssocMgr, NULL, // no STA entry is required &MgmtPacket->SA, errorStatus, errorSource, FALSE, // not reassociation NULL, // no association request frame 0, NULL, 0, // no association response frame assocCompletePara, &assocCompleteParaSize ); } } // // Release locks // if (releaseMacTableLock) { MP_RELEASE_WRITE_LOCK(&AssocMgr->MacHashTableLock, &lockState); } // // Send management frames // if (authRespFrame) { // // Send authentication response // MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): Send authentication response frame.", AP_GET_PORT_NUMBER(AssocMgr->ApPort))); ndisStatus = ApSendMgmtPacket( AssocMgr->ApPort, authRespFrame, authRespFrameSize ); if (ndisStatus != NDIS_STATUS_SUCCESS) { MpTrace(COMP_ASSOC_MGR, DBG_SERIOUS, ("Port(%u): Failed to send authentication response frame. Status = 0x%08x.", AP_GET_PORT_NUMBER(AssocMgr->ApPort), ndisStatus)); } MP_FREE_MEMORY(authRespFrame); authRespFrame = NULL; } // // Send NDIS indications // if (indicateDisassoc) { ApIndicateDot11Status( AssocMgr->ApPort, NDIS_STATUS_DOT11_DISASSOCIATION, NULL, // no request ID &disassocPara, sizeof(disassocPara) ); } if (indicateAssocStart) { ApIndicateDot11Status( AssocMgr->ApPort, NDIS_STATUS_DOT11_INCOMING_ASSOC_STARTED, NULL, // no request ID &assocStartPara, sizeof(assocStartPara) ); } if (indicateAssocComplete) { ApIndicateDot11Status( AssocMgr->ApPort, NDIS_STATUS_DOT11_INCOMING_ASSOC_COMPLETION, NULL, // no request ID assocCompletePara, assocCompleteParaSize ); } // // Free indications // if (assocCompletePara) { MP_FREE_MEMORY(assocCompletePara); } } /** * Processing station deauthentication frame */ VOID AmProcessStaDeauthentication( _In_ PAP_ASSOC_MGR AssocMgr, _In_reads_bytes_(PacketSize) PDOT11_MGMT_HEADER MgmtPacket, _In_ _In_range_(DOT11_MGMT_HEADER_SIZE + sizeof(DOT11_DEAUTH_FRAME), USHORT_MAX) USHORT PacketSize ) { PVNIC vnic = AP_GET_VNIC(AssocMgr->ApPort); PDOT11_DEAUTH_FRAME deauthFrame; ULONG reasonCode; PMAC_HASH_ENTRY macEntry = NULL; PAP_STA_ENTRY staEntry = NULL; MP_RW_LOCK_STATE lockState; UNREFERENCED_PARAMETER(PacketSize); // only handle deauth MPASSERT(DOT11_FRAME_TYPE_MANAGEMENT == MgmtPacket->FrameControl.Type); MPASSERT(DOT11_MGMT_SUBTYPE_DEAUTHENTICATION == MgmtPacket->FrameControl.Subtype); MPASSERT(PacketSize >= DOT11_MGMT_HEADER_SIZE + sizeof(DOT11_DEAUTH_FRAME)); do { // // Check BSSID and DA // if (!MP_COMPARE_MAC_ADDRESS(MgmtPacket->BSSID, VNic11QueryCurrentBSSID(vnic)) || !MP_COMPARE_MAC_ADDRESS(MgmtPacket->DA, VNic11QueryMACAddress(vnic))) { break; } MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): Process deauthentication request for STA %02X-%02X-%02X-%02X-%02X-%02X.", AP_GET_PORT_NUMBER(AssocMgr->ApPort), MgmtPacket->SA[0], MgmtPacket->SA[1], MgmtPacket->SA[2], MgmtPacket->SA[3], MgmtPacket->SA[4], MgmtPacket->SA[5] )); // // acquire a write lock on Mac table because we may need to update it // MP_ACQUIRE_WRITE_LOCK(&AssocMgr->MacHashTableLock, &lockState); // // Remove the station entry from Mac table if it exists // macEntry = RemoveFromMacHashTable( &AssocMgr->MacHashTable, &MgmtPacket->SA ); MP_RELEASE_WRITE_LOCK(&AssocMgr->MacHashTableLock, &lockState); if (NULL == macEntry) { // // The station is not in our station table. // Ignore the deauth. // break; } // // Get Deauth Frame // deauthFrame = (PDOT11_DEAUTH_FRAME)Add2Ptr(MgmtPacket, DOT11_MGMT_HEADER_SIZE); // // Get the reason // reasonCode = DOT11_DISASSOC_REASON_PEER_DEAUTHENTICATED; reasonCode += deauthFrame->ReasonCode; staEntry = CONTAINING_RECORD(macEntry, AP_STA_ENTRY, MacHashEntry); // // We need to deauth the station // AmDeauthenticateSta( AssocMgr, staEntry, reasonCode, FALSE, // Don't send deauth frame 0 ); } while (FALSE); } /** * Processing station association request frame */ VOID AmProcessStaAssociation( _In_ PAP_ASSOC_MGR AssocMgr, _In_ BOOLEAN reAssociation, _In_reads_bytes_(PacketSize) PDOT11_MGMT_HEADER MgmtPacket, _In_ _In_range_(DOT11_MGMT_HEADER_SIZE + sizeof(DOT11_REASSOC_REQUEST_FRAME), USHORT_MAX) USHORT PacketSize ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; PVNIC vnic = AP_GET_VNIC(AssocMgr->ApPort); PDOT11_ASSOC_REQUEST_FRAME assocRequestFrame = NULL; PUCHAR assocRespFrame = NULL; // association response USHORT assocRespFrameSize = 0; USHORT statusCode = DOT11_FRAME_STATUS_SUCCESSFUL; BOOLEAN sendResponse = FALSE; USHORT reasonCode = DOT11_MGMT_REASON_UPSPEC_REASON; BOOLEAN sendDeauth = FALSE; PMAC_HASH_ENTRY macEntry = NULL; PAP_STA_ENTRY staEntry = NULL; BOOLEAN releaseMacTableLock = FALSE; MP_RW_LOCK_STATE lockState; DOT11_SSID ssid = {0}; DOT11_RATE_SET rateSet = {0}; PUCHAR ieBlobPtr; USHORT ieBlobSize; PUCHAR rsnIePtr; UCHAR rsnIeSize; RSN_IE_INFO rsnIeInfo; /** NDIS indications */ DOT11_INCOMING_ASSOC_STARTED_PARAMETERS assocStartPara; BOOLEAN indicateAssocStart = FALSE; PDOT11_INCOMING_ASSOC_REQUEST_RECEIVED_PARAMETERS assocReqPara = NULL; ULONG assocReqParaSize = 0; BOOLEAN indicateAssocReqReceived = FALSE; PDOT11_INCOMING_ASSOC_COMPLETION_PARAMETERS assocCompletePara = NULL; ULONG assocCompleteParaSize = 0; BOOLEAN indicateAssocComplete = FALSE; UCHAR errorSource = DOT11_ASSOC_ERROR_SOURCE_OS; // error source for association complete indication ULONG errorStatus = 0; USHORT frameSubType = reAssociation?DOT11_MGMT_SUBTYPE_REASSOCIATION_REQUEST:DOT11_MGMT_SUBTYPE_ASSOCIATION_REQUEST; USHORT expectedFrameSize = reAssociation?sizeof(DOT11_REASSOC_REQUEST_FRAME):sizeof(DOT11_ASSOC_REQUEST_FRAME); // only handle association // or reassociation MPASSERT(DOT11_FRAME_TYPE_MANAGEMENT == MgmtPacket->FrameControl.Type); MPASSERT(frameSubType == MgmtPacket->FrameControl.Subtype); MPASSERT(PacketSize >= DOT11_MGMT_HEADER_SIZE + expectedFrameSize); UNREFERENCED_PARAMETER(frameSubType); do { // // Check BSSID and DA // if (!MP_COMPARE_MAC_ADDRESS(MgmtPacket->BSSID, VNic11QueryCurrentBSSID(vnic)) || !MP_COMPARE_MAC_ADDRESS(MgmtPacket->DA, VNic11QueryMACAddress(vnic))) { break; } MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): Process association request for STA %02X-%02X-%02X-%02X-%02X-%02X. Reassociation = %!BOOLEAN!", AP_GET_PORT_NUMBER(AssocMgr->ApPort), MgmtPacket->SA[0], MgmtPacket->SA[1], MgmtPacket->SA[2], MgmtPacket->SA[3], MgmtPacket->SA[4], MgmtPacket->SA[5], reAssociation )); // // Acquire a write lock on Mac table because we may need to update it // MP_ACQUIRE_WRITE_LOCK(&AssocMgr->MacHashTableLock, &lockState); releaseMacTableLock = TRUE; // // Lookup the station entry from Mac table if it exists // macEntry = LookupMacHashTable( &AssocMgr->MacHashTable, &MgmtPacket->SA ); if (macEntry != NULL) { staEntry = CONTAINING_RECORD(macEntry, AP_STA_ENTRY, MacHashEntry); // // Stop association timer because we've received the association request. // AmStopStaAssocTimer(staEntry); // // Do we need to disassociate the station first? // if (ApGetStaAssocState(staEntry) == dot11_assoc_state_auth_assoc) { MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): Need to disassociate the STA that is already in associated state.", AP_GET_PORT_NUMBER(AssocMgr->ApPort))); // // We need to disassociate the station. // The steps are the following: // 1. Reference the station entry so that it won't be destroyed. // 2. Release lock. // 3. Disassociation // 4. De-reference the station entry (the entry may be destroyted). // 5. Acquire lock. // 6. Search for station entry // ApRefSta(staEntry); MP_RELEASE_WRITE_LOCK(&AssocMgr->MacHashTableLock, &lockState); AmDisassociateSta( AssocMgr, staEntry, DOT11_DISASSOC_REASON_OS // TODO: a better reason code ); ApDerefSta(staEntry); MP_ACQUIRE_WRITE_LOCK(&AssocMgr->MacHashTableLock, &lockState); // // Note: // The station entry can be removed because of timeout cleanup // or OS issueing Reset and/or Disassociation. // However, a new entry will NOT be added because received packet // processing is serialized, i.e. packets are processed one by one. // macEntry = LookupMacHashTable( &AssocMgr->MacHashTable, &MgmtPacket->SA ); if (macEntry != NULL) { staEntry = CONTAINING_RECORD(macEntry, AP_STA_ENTRY, MacHashEntry); } else { staEntry = NULL; } } } if (NULL == macEntry) { // // The station is not authenticated yet, send a deauth. // MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): Send deauthentication frame to STA not authenticated yet.", AP_GET_PORT_NUMBER(AssocMgr->ApPort))); sendDeauth = TRUE; reasonCode = DOT11_MGMT_REASON_CLASS2_ERROR; // // Don't indication association start because the station is not authenticated yet. // break; } if (ApGetStaAssocState(staEntry) != dot11_assoc_state_auth_unassoc) { MPASSERT(FALSE); // // The station is not authenticated yet, send a deauth. // MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): Send deauthentication frame to STA not authenticated yet.", AP_GET_PORT_NUMBER(AssocMgr->ApPort))); sendDeauth = TRUE; reasonCode = DOT11_MGMT_REASON_CLASS2_ERROR; // // Don't indication association start because the station is not authenticated yet. // break; } // // We must prepare assoc completion indication at this time. // Allocate the maximum buffer for the completion. // ndisStatus = AmAllocAssocCompletePara( AssocMgr, DOT11_MAX_MSDU_SIZE, DOT11_MAX_MSDU_SIZE, &assocCompletePara, &assocCompleteParaSize ); if (ndisStatus != NDIS_STATUS_SUCCESS) { MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): Failed to allocate association completion parameter. Status = 0x%08x.", AP_GET_PORT_NUMBER(AssocMgr->ApPort), ndisStatus)); break; } // // Need to send assoc start indication // indicateAssocStart = TRUE; // // Send association complete if associations is not accepted by miniport. // indicateAssocComplete = TRUE; // // Get association Frame // Note that reassociation request frame contains association request frame. // assocRequestFrame = (PDOT11_ASSOC_REQUEST_FRAME)Add2Ptr(MgmtPacket, DOT11_MGMT_HEADER_SIZE); // // Get IE blob // ieBlobPtr = (PUCHAR)assocRequestFrame + expectedFrameSize; ieBlobSize = PacketSize - DOT11_MGMT_HEADER_SIZE - expectedFrameSize; // // Match SSID // ndisStatus = Dot11CopySSIDFromMemoryBlob( ieBlobPtr, ieBlobSize, &ssid ); if (ndisStatus != NDIS_STATUS_SUCCESS) { sendResponse = TRUE; statusCode = DOT11_FRAME_STATUS_INVALID_IE; // TODO: a better status code? MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): Failed to get SSID from IE blob. Status = 0x%08x.", AP_GET_PORT_NUMBER(AssocMgr->ApPort), ndisStatus )); // // For assoc complete indication // errorSource = DOT11_ASSOC_ERROR_SOURCE_OTHER; errorStatus = statusCode; break; } if (ssid.uSSIDLength != AssocMgr->Ssid.uSSIDLength || memcmp(ssid.ucSSID, AssocMgr->Ssid.ucSSID, ssid.uSSIDLength) != 0 ) { sendResponse = TRUE; statusCode = DOT11_FRAME_STATUS_INVALID_IE; // TODO: a better status code? MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): SSID doesn't match.", AP_GET_PORT_NUMBER(AssocMgr->ApPort))); // // For assoc complete indication // errorSource = DOT11_ASSOC_ERROR_SOURCE_OTHER; errorStatus = statusCode; break; } // // TODO: match capability // if (assocRequestFrame->Capability.ESS != 1 || assocRequestFrame->Capability.IBSS != 0 ) { MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): Capability doesn't match. ESS = %u, IBSS = %u.", AP_GET_PORT_NUMBER(AssocMgr->ApPort), assocRequestFrame->Capability.ESS, assocRequestFrame->Capability.IBSS)); sendResponse = TRUE; statusCode = DOT11_FRAME_STATUS_UNSUPPORTED_CAPABILITIES; // // For assoc complete indication // errorSource = DOT11_ASSOC_ERROR_SOURCE_OTHER; errorStatus = statusCode; break; } // // Match rate set // ndisStatus = Dot11GetRateSetFromInfoEle( ieBlobPtr, ieBlobSize, FALSE, // all rates &rateSet ); if (ndisStatus != NDIS_STATUS_SUCCESS) { MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): Failed to rate set from IE blob. Status = 0x%08x.", AP_GET_PORT_NUMBER(AssocMgr->ApPort), ndisStatus)); sendResponse = TRUE; statusCode = DOT11_FRAME_STATUS_INVALID_IE; // // For assoc complete indication // errorSource = DOT11_ASSOC_ERROR_SOURCE_OTHER; errorStatus = statusCode; break; } // // Filter out the rates that are not supported by AP // AmFilterUnsupportedRates( AssocMgr, &rateSet ); if (0 == rateSet.uRateSetLength) { MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): No matching rate.", AP_GET_PORT_NUMBER(AssocMgr->ApPort))); sendResponse = TRUE; statusCode = DOT11_FRAME_STATUS_ASSOC_DENIED_DATA_RATE_SET; // // For assoc complete indication // errorSource = DOT11_ASSOC_ERROR_SOURCE_OTHER; errorStatus = statusCode; break; } // // set the default auth/cipher in staEntry // staEntry->AuthAlgo = DOT11_AUTH_ALGO_80211_OPEN; if (assocRequestFrame->Capability.Privacy != 0) { MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): Choosing WEP.", AP_GET_PORT_NUMBER(AssocMgr->ApPort))); // assuming WEP if Privacy bit is set staEntry->UnicastCipher = DOT11_CIPHER_ALGO_WEP; staEntry->MulticastCipher = DOT11_CIPHER_ALGO_WEP; } else { MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): Choosing None.", AP_GET_PORT_NUMBER(AssocMgr->ApPort))); staEntry->UnicastCipher = DOT11_CIPHER_ALGO_NONE; staEntry->MulticastCipher = DOT11_CIPHER_ALGO_NONE; } // // Check the WPA2 Auth/Cipher in IE blob // TODO: check WPA IE and set auth/cipher in staEntry as well // if (AssocMgr->AuthAlgorithm == DOT11_AUTH_ALGO_RSNA_PSK) { MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): Choosing RSNA PSK.", AP_GET_PORT_NUMBER(AssocMgr->ApPort))); if( Dot11GetInfoEle( ieBlobPtr, ieBlobSize, DOT11_INFO_ELEMENT_ID_RSN, &rsnIeSize, &rsnIePtr ) == NDIS_STATUS_SUCCESS) { MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): RSN IE present.", AP_GET_PORT_NUMBER(AssocMgr->ApPort))); // // WPA2 IE is present, parse it and ensure it is supported // ndisStatus = Dot11ParseRNSIE(rsnIePtr, RSNA_OUI_PREFIX, rsnIeSize, &rsnIeInfo); if (ndisStatus != NDIS_STATUS_SUCCESS) { MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): Failed to parse RSN IE. Status = 0x%08x.", AP_GET_PORT_NUMBER(AssocMgr->ApPort), ndisStatus)); sendResponse = TRUE; statusCode = DOT11_FRAME_STATUS_INVALID_IE; // // For assoc complete indication // errorSource = DOT11_ASSOC_ERROR_SOURCE_OTHER; errorStatus = statusCode; break; } ndisStatus = AmMatchRsnInfo( AssocMgr, &rsnIeInfo, &statusCode ); if (ndisStatus != NDIS_STATUS_SUCCESS) { MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): RSN IE mismatch. Status = 0x%08x.", AP_GET_PORT_NUMBER(AssocMgr->ApPort), ndisStatus)); sendResponse = TRUE; // // For assoc complete indication // errorSource = DOT11_ASSOC_ERROR_SOURCE_OTHER; errorStatus = statusCode; break; } // // copy auth/cipher from AssocMgr to staEntry as they match // staEntry->AuthAlgo = AssocMgr->AuthAlgorithm; staEntry->UnicastCipher = AssocMgr->UnicastCipherAlgorithm; staEntry->MulticastCipher = AssocMgr->MulticastCipherAlgorithm; } else { MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): No RSN IE present.", AP_GET_PORT_NUMBER(AssocMgr->ApPort))); // // no WPA2 IE is present, only accept Open/None when WPS is enabled. // do not check Privacy bit, it may be 1 (from Vista SP1+WUR client). // if (!AssocMgr->EnableWps) { MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): Cannot accept open in non-WCN case.", AP_GET_PORT_NUMBER(AssocMgr->ApPort))); sendResponse = TRUE; statusCode = DOT11_FRAME_STATUS_UNSUPPORTED_AUTH_ALGO; // // For assoc complete indication // errorSource = DOT11_ASSOC_ERROR_SOURCE_OTHER; errorStatus = statusCode; break; } } } // Allocate association request received parameters. // ndisStatus = AmAllocAssocReqPara( AssocMgr, PacketSize - DOT11_MGMT_HEADER_SIZE, &assocReqPara, &assocReqParaSize ); if (ndisStatus != NDIS_STATUS_SUCCESS) { MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): Failed to allocate association request parameters. Status = 0x%08x.", AP_GET_PORT_NUMBER(AssocMgr->ApPort), ndisStatus)); break; } indicateAssocReqReceived = TRUE; // // Initialize association decision. // It will be overwritten by the decision made by OS. // The initial values are used for the error case. // staEntry->AcceptAssoc = FALSE; staEntry->Reason = DOT11_FRAME_STATUS_FAILURE; // // In this case, we will not send response and association completion indication. // The response is sent after receving OID_DOT11_INCOMING_ASSOCIATION_DECISION. // sendResponse = FALSE; indicateAssocComplete = FALSE; // // Miniport decides to accept the association. // Update station entry // // TODO: update station entry staEntry->Aid = AmAllocateAid(AssocMgr); staEntry->ListenInterval = assocRequestFrame->usListenInterval; staEntry->CapabilityInformation = assocRequestFrame->Capability; RtlCopyMemory( &staEntry->SupportedRateSet, &rateSet, sizeof(DOT11_RATE_SET) ); } while (FALSE); MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): First phase completed. StatusCode = 0x%08x.", AP_GET_PORT_NUMBER(AssocMgr->ApPort), statusCode)); if (sendResponse) { MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): Create association response with failure.", AP_GET_PORT_NUMBER(AssocMgr->ApPort))); // // Create association response, it must be a failure // AmCreateAssocRespFrame( AssocMgr, staEntry, reAssociation, MgmtPacket, PacketSize, statusCode, &assocRespFrame, &assocRespFrameSize ); } // // Prepare association indications // // // Association start indication // if (indicateAssocStart) { AmPrepareAssocStartPara( &MgmtPacket->SA, &assocStartPara ); } // // Association request received indication // if (indicateAssocReqReceived) { // // Mac header of the request frame is not needed. // AmPrepareAssocReqPara( AssocMgr, &MgmtPacket->SA, reAssociation, Add2Ptr(MgmtPacket, DOT11_MGMT_HEADER_SIZE), PacketSize - DOT11_MGMT_HEADER_SIZE, assocReqPara, &assocReqParaSize ); } // // Association complete indication // if (indicateAssocComplete) { // // Must be a failure // MPASSERT(errorStatus != 0); AmPrepareAssocCompletePara( AssocMgr, NULL, // no STA entry is required for a failure &MgmtPacket->SA, errorStatus, errorSource, reAssociation, NULL, // no association request frame 0, NULL, // no association response frame 0, assocCompletePara, &assocCompleteParaSize ); } // // Release locks // if (releaseMacTableLock) { MP_RELEASE_WRITE_LOCK(&AssocMgr->MacHashTableLock, &lockState); } // // Send management frames // if (assocRespFrame) { // // Send association response // MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): Sending association response frame.", AP_GET_PORT_NUMBER(AssocMgr->ApPort))); ndisStatus = ApSendMgmtPacket( AssocMgr->ApPort, assocRespFrame, assocRespFrameSize ); if (ndisStatus != NDIS_STATUS_SUCCESS) { MpTrace(COMP_ASSOC_MGR, DBG_SERIOUS, ("Port(%u): Failed to send association response frame. Status = 0x%08x.", AP_GET_PORT_NUMBER(AssocMgr->ApPort), ndisStatus)); } MP_FREE_MEMORY(assocRespFrame); assocRespFrame = NULL; } if (sendDeauth) { // // Send deauth frame // AmSendDeauthenticationFrame( AssocMgr, &MgmtPacket->SA, reasonCode ); } // // Send NDIS indications // if (indicateAssocStart) { ApIndicateDot11Status( AssocMgr->ApPort, NDIS_STATUS_DOT11_INCOMING_ASSOC_STARTED, NULL, // no request ID &assocStartPara, sizeof(assocStartPara) ); } if (indicateAssocReqReceived) { MPASSERT(!indicateAssocComplete); ApIndicateDot11Status( AssocMgr->ApPort, NDIS_STATUS_DOT11_INCOMING_ASSOC_REQUEST_RECEIVED, NULL, // no request ID assocReqPara, assocReqParaSize ); // // OS will make the direct OID call OID_DOT11_INCOMING_ASSOCIATION_DECISION // in the status indication thread. // So after the status indication returns, the association decision shall be // cached in the station entry already. We need to process it now. // ndisStatus = AmProcessAssociationDecision( AssocMgr, reAssociation, MgmtPacket, PacketSize, assocCompletePara, assocCompleteParaSize ); if (ndisStatus != NDIS_STATUS_SUCCESS) { // // Log error // MpTrace(COMP_ASSOC_MGR, DBG_SERIOUS, ("Port(%u): Failed to process association decision. Status = 0x%08x.", AP_GET_PORT_NUMBER(AssocMgr->ApPort), ndisStatus)); } } if (indicateAssocComplete) { ApIndicateDot11Status( AssocMgr->ApPort, NDIS_STATUS_DOT11_INCOMING_ASSOC_COMPLETION, NULL, // no request ID assocCompletePara, assocCompleteParaSize ); } // // Free indications // if (assocReqPara) { MP_FREE_MEMORY(assocReqPara); } if (assocCompletePara) { MP_FREE_MEMORY(assocCompletePara); } } /** * Processing station disassociation frame */ VOID AmProcessStaDisassociation( _In_ PAP_ASSOC_MGR AssocMgr, _In_reads_bytes_(PacketSize) PDOT11_MGMT_HEADER MgmtPacket, _In_ _In_range_(DOT11_MGMT_HEADER_SIZE + sizeof(DOT11_DISASSOC_FRAME), USHORT_MAX) USHORT PacketSize ) { PVNIC vnic = AP_GET_VNIC(AssocMgr->ApPort); PDOT11_DISASSOC_FRAME disassocFrame; ULONG reasonCode; PMAC_HASH_ENTRY macEntry = NULL; PAP_STA_ENTRY staEntry = NULL; BOOLEAN releaseMacTableLock = FALSE; MP_RW_LOCK_STATE lockState; UNREFERENCED_PARAMETER(PacketSize); // only handle association MPASSERT(DOT11_FRAME_TYPE_MANAGEMENT == MgmtPacket->FrameControl.Type); MPASSERT(DOT11_MGMT_SUBTYPE_DISASSOCIATION == MgmtPacket->FrameControl.Subtype); MPASSERT(PacketSize >= DOT11_MGMT_HEADER_SIZE + sizeof(DOT11_DISASSOC_FRAME)); do { // // Check BSSID and DA // if (!MP_COMPARE_MAC_ADDRESS(MgmtPacket->BSSID, VNic11QueryCurrentBSSID(vnic)) || !MP_COMPARE_MAC_ADDRESS(MgmtPacket->DA, VNic11QueryMACAddress(vnic))) { break; } MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): Process disassociation request for STA %02X-%02X-%02X-%02X-%02X-%02X.", AP_GET_PORT_NUMBER(AssocMgr->ApPort), MgmtPacket->SA[0], MgmtPacket->SA[1], MgmtPacket->SA[2], MgmtPacket->SA[3], MgmtPacket->SA[4], MgmtPacket->SA[5] )); // // acquire a write lock on Mac table because we may need to update it // MP_ACQUIRE_WRITE_LOCK(&AssocMgr->MacHashTableLock, &lockState); releaseMacTableLock = TRUE; // // Lookup the station entry from Mac table if it exists // macEntry = LookupMacHashTable( &AssocMgr->MacHashTable, &MgmtPacket->SA ); if (NULL == macEntry) { // // The station is not in our station table. // Ignore the disassociation. // break; } // // Get Disassoc Frame // disassocFrame = (PDOT11_DISASSOC_FRAME)Add2Ptr(MgmtPacket, DOT11_MGMT_HEADER_SIZE); // // Get the reason // reasonCode = DOT11_DISASSOC_REASON_PEER_DISASSOCIATED; reasonCode += disassocFrame->usReasonCode; staEntry = CONTAINING_RECORD(macEntry, AP_STA_ENTRY, MacHashEntry); // // We need to disassociate the station. // The steps are the following: // 1. Reference the station entry // 2. Release lock. // 3. Disassociate the station // 4. Dereference the station entry // ApRefSta(staEntry); MP_RELEASE_WRITE_LOCK(&AssocMgr->MacHashTableLock, &lockState); releaseMacTableLock = FALSE; AmDisassociateSta( AssocMgr, staEntry, reasonCode ); ApDerefSta(staEntry); } while (FALSE); // // Release locks // if (releaseMacTableLock) { MP_RELEASE_WRITE_LOCK(&AssocMgr->MacHashTableLock, &lockState); } } /** * Association related management frames */ /** * Fill common part of a management frame. * The frame is allocated by caller. */ VOID AmFillMgmtFrameHeader( _In_ PAP_ASSOC_MGR AssocMgr, _In_ PDOT11_MGMT_HEADER MgmtFrame, _In_ DOT11_MGMT_SUBTYPE MgmtSubtype, _In_ PDOT11_MAC_ADDRESS DestAddr ) { // // Fill the MAC header // MgmtFrame->FrameControl.Version = 0x0; MgmtFrame->FrameControl.Type = DOT11_FRAME_TYPE_MANAGEMENT; MgmtFrame->FrameControl.Subtype = MgmtSubtype; MgmtFrame->FrameControl.ToDS = 0x0; // Default value for Mgmt frames MgmtFrame->FrameControl.FromDS = 0x0; // Default value for Mgmt frames MgmtFrame->FrameControl.MoreFrag = 0x0; MgmtFrame->FrameControl.Retry = 0x0; MgmtFrame->FrameControl.PwrMgt = 0x0; MgmtFrame->FrameControl.MoreData = 0x0; MgmtFrame->FrameControl.WEP = 0x0; // no WEP MgmtFrame->FrameControl.Order = 0x0; // no order RtlCopyMemory( MgmtFrame->DA, *DestAddr, sizeof(DOT11_MAC_ADDRESS) ); RtlCopyMemory( MgmtFrame->SA, VNic11QueryMACAddress(AP_GET_VNIC(AssocMgr->ApPort)), sizeof(DOT11_MAC_ADDRESS) ); RtlCopyMemory( MgmtFrame->BSSID, AssocMgr->Bssid, sizeof(DOT11_MAC_ADDRESS) ); } /** Create authentication response frame */ NDIS_STATUS AmCreateAuthRespFrame( _In_ PAP_ASSOC_MGR AssocMgr, _In_opt_ PAP_STA_ENTRY StaEntry, _In_ PDOT11_MGMT_HEADER ReceivedMgmtPacket, _In_ USHORT ReceivedPacketSize, _In_ USHORT StatusCode, _Outptr_result_bytebuffer_(*AuthRespFrameSize) PUCHAR * AuthRespFrame, _Out_ PUSHORT AuthRespFrameSize ) { PDOT11_MGMT_HEADER mgmtFrame = NULL; PDOT11_AUTH_FRAME authFrame = NULL; PDOT11_AUTH_FRAME receivedAuthFrame; NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; PUCHAR packetBuffer = NULL; USHORT packetSize; do { if (!StaEntry) { ndisStatus = NDIS_STATUS_INVALID_PARAMETER; break; } if (ReceivedPacketSize < DOT11_MGMT_HEADER_SIZE + sizeof(DOT11_AUTH_FRAME)) { ndisStatus = NDIS_STATUS_INVALID_DATA; break; } packetSize = DOT11_MGMT_HEADER_SIZE + sizeof(DOT11_AUTH_FRAME); MP_ALLOCATE_MEMORY( AP_GET_MP_HANDLE(AssocMgr->ApPort), &packetBuffer, packetSize, EXTAP_MEMORY_TAG ); if (NULL == packetBuffer) { MpTrace(COMP_ASSOC_MGR, DBG_SERIOUS, ("Port(%u): Failed to allocate authentication frame (%u bytes).", AP_GET_PORT_NUMBER(AssocMgr->ApPort), packetSize)); ndisStatus = NDIS_STATUS_RESOURCES; break; } NdisZeroMemory(packetBuffer, packetSize); mgmtFrame = (PDOT11_MGMT_HEADER)packetBuffer; // // Fill the MAC header // AmFillMgmtFrameHeader( AssocMgr, mgmtFrame, DOT11_MGMT_SUBTYPE_AUTHENTICATION, &StaEntry->MacHashEntry.MacKey ); receivedAuthFrame = (PDOT11_AUTH_FRAME)Add2Ptr(ReceivedMgmtPacket, DOT11_MGMT_HEADER_SIZE); authFrame = (PDOT11_AUTH_FRAME)(packetBuffer + DOT11_MGMT_HEADER_SIZE); authFrame->usAlgorithmNumber = receivedAuthFrame->usAlgorithmNumber; authFrame->usXid = 2; authFrame->usStatusCode = StatusCode; // // Output parameters // *AuthRespFrame = packetBuffer; *AuthRespFrameSize = packetSize; packetBuffer = NULL; // ensure the frame will not be freed } while (FALSE); if (packetBuffer) { MP_FREE_MEMORY(packetBuffer); } return ndisStatus; } /** Create association/reassociation response frame */ _Success_(return == NDIS_STATUS_SUCCESS) NDIS_STATUS AmCreateAssocRespFrame( _In_ PAP_ASSOC_MGR AssocMgr, _In_ PAP_STA_ENTRY StaEntry, _In_ BOOLEAN reAssociation, _In_ PDOT11_MGMT_HEADER ReceivedMgmtPacket, _In_ USHORT ReceivedPacketSize, _In_ USHORT StatusCode, _Outptr_result_bytebuffer_(*AssocRespFrameSize) PUCHAR * AssocRespFrame, _Out_ PUSHORT AssocRespFrameSize ) { PDOT11_MGMT_HEADER mgmtFrame = NULL; PDOT11_ASSOC_RESPONSE_FRAME assocResponseFrame = NULL; NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; PUCHAR packetBuffer = NULL; USHORT packetSize; PUCHAR currentIe = NULL; USHORT ieSize = AP11_MAX_IE_BLOB_SIZE; USHORT frameSize = reAssociation?sizeof(DOT11_REASSOC_REQUEST_FRAME):sizeof(DOT11_ASSOC_REQUEST_FRAME); USHORT respFrameSize = reAssociation?sizeof(DOT11_REASSOC_RESPONSE_FRAME):sizeof(DOT11_ASSOC_RESPONSE_FRAME); USHORT subType = reAssociation?DOT11_MGMT_SUBTYPE_REASSOCIATION_RESPONSE:DOT11_MGMT_SUBTYPE_ASSOCIATION_RESPONSE; UNREFERENCED_PARAMETER(ReceivedMgmtPacket); do { if (ReceivedPacketSize < DOT11_MGMT_HEADER_SIZE + frameSize) { ndisStatus = NDIS_STATUS_INVALID_DATA; break; } // Need to append IE packetSize = DOT11_MGMT_HEADER_SIZE + respFrameSize + ieSize; MP_ALLOCATE_MEMORY( AP_GET_MP_HANDLE(AssocMgr->ApPort), &packetBuffer, packetSize, EXTAP_MEMORY_TAG ); if (NULL == packetBuffer) { MpTrace(COMP_ASSOC_MGR, DBG_SERIOUS, ("Port(%u): Failed to allocate association response frame (%u bytes).", AP_GET_PORT_NUMBER(AssocMgr->ApPort), packetSize)); ndisStatus = NDIS_STATUS_RESOURCES; break; } NdisZeroMemory(packetBuffer, packetSize); mgmtFrame = (PDOT11_MGMT_HEADER)packetBuffer; // // Fill the MAC header // AmFillMgmtFrameHeader( AssocMgr, mgmtFrame, subType, &StaEntry->MacHashEntry.MacKey ); assocResponseFrame = (PDOT11_ASSOC_RESPONSE_FRAME)(packetBuffer + DOT11_MGMT_HEADER_SIZE); assocResponseFrame->Capability = StaEntry->CapabilityInformation; assocResponseFrame->usAID = StaEntry->Aid; assocResponseFrame->usStatusCode = StatusCode; // // Add IEs // currentIe = (PUCHAR)assocResponseFrame + sizeof(DOT11_ASSOC_RESPONSE_FRAME); // // Add supported rates // ndisStatus = Dot11AttachElement( ¤tIe, &ieSize, DOT11_INFO_ELEMENT_ID_SUPPORTED_RATES, (UCHAR)((StaEntry->SupportedRateSet.uRateSetLength > 8) ? 8 : StaEntry->SupportedRateSet.uRateSetLength), StaEntry->SupportedRateSet.ucRateSet ); if (ndisStatus != NDIS_STATUS_SUCCESS) { MpTrace(COMP_ASSOC_MGR, DBG_SERIOUS, ("Port(%u): Failed to populate supported rates in association response.", AP_GET_PORT_NUMBER(AssocMgr->ApPort))); break; } // // Add the extended rate set if needed // if (StaEntry->SupportedRateSet.uRateSetLength > (UCHAR)8) { ndisStatus = Dot11AttachElement( ¤tIe, &ieSize, DOT11_INFO_ELEMENT_ID_EXTD_SUPPORTED_RATES, (UCHAR)(StaEntry->SupportedRateSet.uRateSetLength - 8), (StaEntry->SupportedRateSet.ucRateSet + 8) ); if (ndisStatus != NDIS_STATUS_SUCCESS) { MpTrace(COMP_ASSOC_MGR, DBG_SERIOUS, ("Port(%u): Failed to add extended rates in association response.", AP_GET_PORT_NUMBER(AssocMgr->ApPort))); break; } } // // TODO: add other IEs // // // Adjust packet length for IE space we did not use. // We may have empty space here that we dont want send out in the packet. // packetSize = packetSize - ieSize; // // Output parameters // *AssocRespFrame = packetBuffer; *AssocRespFrameSize = packetSize; packetBuffer = NULL; // ensure the frame will not be freed } while (FALSE); if (packetBuffer) { MP_FREE_MEMORY(packetBuffer); } return ndisStatus; } /** Create disassociation frame */ NDIS_STATUS AmCreateDisassocFrame( _In_ PAP_ASSOC_MGR AssocMgr, _In_reads_bytes_(DOT11_ADDRESS_SIZE) PDOT11_MAC_ADDRESS DestAddr, _In_ USHORT ReasonCode, _Outptr_result_bytebuffer_(*DisassocFrameSize) PUCHAR * DisassocFrame, _Out_ PUSHORT DisassocFrameSize ) { PDOT11_MGMT_HEADER mgmtFrame = NULL; PDOT11_DISASSOC_FRAME disassocFrame = NULL; NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; PUCHAR packetBuffer = NULL; USHORT packetSize; do { packetSize = DOT11_MGMT_HEADER_SIZE + sizeof(DOT11_DISASSOC_FRAME); MP_ALLOCATE_MEMORY( AP_GET_MP_HANDLE(AssocMgr->ApPort), &packetBuffer, packetSize, EXTAP_MEMORY_TAG ); if (NULL == packetBuffer) { MpTrace(COMP_ASSOC_MGR, DBG_SERIOUS, ("Port(%u): Failed to allocate disassociation frame (%u bytes).", AP_GET_PORT_NUMBER(AssocMgr->ApPort), packetSize)); ndisStatus = NDIS_STATUS_RESOURCES; break; } NdisZeroMemory(packetBuffer, packetSize); mgmtFrame = (PDOT11_MGMT_HEADER)packetBuffer; // // Fill the MAC header // AmFillMgmtFrameHeader( AssocMgr, mgmtFrame, DOT11_MGMT_SUBTYPE_DISASSOCIATION, DestAddr ); disassocFrame = (PDOT11_DISASSOC_FRAME)(packetBuffer + DOT11_MGMT_HEADER_SIZE); disassocFrame->usReasonCode = ReasonCode; // // Output parameters // *DisassocFrame = packetBuffer; *DisassocFrameSize = packetSize; packetBuffer = NULL; // ensure the frame will not be freed } while (FALSE); if (packetBuffer) { MP_FREE_MEMORY(packetBuffer); } return ndisStatus; } /** Create deauthentication frame */ NDIS_STATUS AmCreateDeauthFrame( _In_ PAP_ASSOC_MGR AssocMgr, _In_reads_bytes_(DOT11_ADDRESS_SIZE) PDOT11_MAC_ADDRESS DestAddr, _In_ USHORT ReasonCode, _Outptr_result_bytebuffer_(*DeauthFrameSize) PUCHAR * DeauthFrame, _Out_ PUSHORT DeauthFrameSize ) { PDOT11_MGMT_HEADER mgmtFrame = NULL; PDOT11_DEAUTH_FRAME deauthFrame = NULL; NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; PUCHAR packetBuffer = NULL; USHORT packetSize; do { packetSize = DOT11_MGMT_HEADER_SIZE + sizeof(DOT11_DEAUTH_FRAME); MP_ALLOCATE_MEMORY( AP_GET_MP_HANDLE(AssocMgr->ApPort), &packetBuffer, packetSize, EXTAP_MEMORY_TAG ); if (NULL == packetBuffer) { MpTrace(COMP_ASSOC_MGR, DBG_SERIOUS, ("Port(%u): Failed to allocate deauthentication frame (%u bytes).", AP_GET_PORT_NUMBER(AssocMgr->ApPort), packetSize)); ndisStatus = NDIS_STATUS_RESOURCES; break; } NdisZeroMemory(packetBuffer, packetSize); mgmtFrame = (PDOT11_MGMT_HEADER)packetBuffer; // // Fill the MAC header // AmFillMgmtFrameHeader( AssocMgr, mgmtFrame, DOT11_MGMT_SUBTYPE_DEAUTHENTICATION, DestAddr ); deauthFrame = (PDOT11_DEAUTH_FRAME)(packetBuffer + DOT11_MGMT_HEADER_SIZE); deauthFrame->ReasonCode = ReasonCode; // // Output parameters // *DeauthFrame = packetBuffer; *DeauthFrameSize = packetSize; packetBuffer = NULL; // ensure the frame will not be freed } while (FALSE); if (packetBuffer) { MP_FREE_MEMORY(packetBuffer); } return ndisStatus; } /** * Send disassociation frame. * Shall not invoke it in a lock. */ NDIS_STATUS AmSendDisassociationFrame( _In_ PAP_ASSOC_MGR AssocMgr, _In_reads_bytes_(DOT11_ADDRESS_SIZE) PDOT11_MAC_ADDRESS DestAddr, _In_ USHORT ReasonCode ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; PUCHAR mgmtFrame = NULL; USHORT mgmtFrameSize; PUCHAR pucMacAddr = (PUCHAR) DestAddr; do { ndisStatus = AmCreateDisassocFrame( AssocMgr, DestAddr, ReasonCode, &mgmtFrame, &mgmtFrameSize ); if (ndisStatus != NDIS_STATUS_SUCCESS) { break; } // // Send the disassociation packet // MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): Send disassociation frame to STA %02X-%02X-%02X-%02X-%02X-%02X. Reason = 0x%08x.", AP_GET_PORT_NUMBER(AssocMgr->ApPort), pucMacAddr[0], pucMacAddr[1], pucMacAddr[2], pucMacAddr[3], pucMacAddr[4], pucMacAddr[5], ReasonCode )); ndisStatus = ApSendMgmtPacket( AssocMgr->ApPort, mgmtFrame, mgmtFrameSize ); if (ndisStatus != NDIS_STATUS_SUCCESS) { MpTrace(COMP_ASSOC_MGR, DBG_SERIOUS, ("Port(%u): Failed to send disassociation frame. Status = 0x%08x.", AP_GET_PORT_NUMBER(AssocMgr->ApPort), ndisStatus)); } } while (FALSE); if (mgmtFrame) { MP_FREE_MEMORY(mgmtFrame); } return ndisStatus; } /** * Send deauthentication frame. * Shall not invoke it in a lock. */ NDIS_STATUS AmSendDeauthenticationFrame( _In_ PAP_ASSOC_MGR AssocMgr, _In_reads_bytes_(DOT11_ADDRESS_SIZE) PDOT11_MAC_ADDRESS DestAddr, _In_ USHORT ReasonCode ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; PUCHAR mgmtFrame = NULL; USHORT mgmtFrameSize; PUCHAR pucMacAddr = (PUCHAR) DestAddr; do { ndisStatus = AmCreateDeauthFrame( AssocMgr, DestAddr, ReasonCode, &mgmtFrame, &mgmtFrameSize ); if (ndisStatus != NDIS_STATUS_SUCCESS) { break; } // // Send the disassociation packet // MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): Send deauthentication frame to STA %02X-%02X-%02X-%02X-%02X-%02X. Reason = 0x%08x.", AP_GET_PORT_NUMBER(AssocMgr->ApPort), pucMacAddr[0], pucMacAddr[1], pucMacAddr[2], pucMacAddr[3], pucMacAddr[4], pucMacAddr[5], ReasonCode )); ndisStatus = ApSendMgmtPacket( AssocMgr->ApPort, mgmtFrame, mgmtFrameSize ); if (ndisStatus != NDIS_STATUS_SUCCESS) { MpTrace(COMP_ASSOC_MGR, DBG_SERIOUS, ("Port(%u): Failed to send deauthentication frame. Status = 0x%08x.", AP_GET_PORT_NUMBER(AssocMgr->ApPort), ndisStatus)); } } while (FALSE); if (mgmtFrame) { MP_FREE_MEMORY(mgmtFrame); } return ndisStatus; } /** * Disassociate a station. * The caller must ensure the station entry is valid during this call. * The caller should not hold any lock. */ VOID AmDisassociateSta( _In_ PAP_ASSOC_MGR AssocMgr, _In_ PAP_STA_ENTRY StaEntry, _In_ ULONG Reason ) { DOT11_DISASSOCIATION_PARAMETERS disassocPara; UNREFERENCED_PARAMETER(AssocMgr); UNREFERENCED_PARAMETER(StaEntry); UNREFERENCED_PARAMETER(Reason); do { // // Stop association timer. // This a no-op if the timer is not started. // AmStopStaAssocTimer(StaEntry); // // Check the current state of the station. // The state can change because no lock is held. // Set the new state to disassociated if the current state is associated. // if (ApCheckAndSetStaAssocState( StaEntry, dot11_assoc_state_auth_unassoc, dot11_assoc_state_auth_assoc ) != dot11_assoc_state_auth_assoc) { // // The state is not associated. Do nothing. // break; } // // The above state check ensures the station will not be // disassociated twice. // // // Stop inactive timer for this station // AmStopStaInactiveTimer(AssocMgr); // // TODO: clear cached packets // Packets may be cached in HW layer because of power save. // TODO: This cannot be called in a lock? // // Free AID if (StaEntry->Aid != AP_INVALID_AID) { AmFreeAid(AssocMgr, StaEntry->Aid); StaEntry->Aid = AP_INVALID_AID; } // // TODO: Send disassociation frame // if (DOT11_ASSOC_STATUS_DISASSOCIATED_BY_OS == Reason) { // TODO: send disassociation frame as appropriate } // // Prepare disassociation indication // AmPrepareDisassocPara( &StaEntry->MacHashEntry.MacKey, Reason, &disassocPara ); // // Send NDIS indication // ApIndicateDot11Status( AssocMgr->ApPort, NDIS_STATUS_DOT11_DISASSOCIATION, NULL, // no request ID &disassocPara, sizeof(disassocPara) ); // // Add trace // MpTrace(COMP_ASSOC_MGR, DBG_NORMAL, ("Port(%u): Station %02X-%02X-%02X-%02X-%02X-%02X disassociated due to reason 0x%08x.", AP_GET_PORT_NUMBER(AssocMgr->ApPort), StaEntry->MacHashEntry.MacKey[0], StaEntry->MacHashEntry.MacKey[1], StaEntry->MacHashEntry.MacKey[2], StaEntry->MacHashEntry.MacKey[3], StaEntry->MacHashEntry.MacKey[4], StaEntry->MacHashEntry.MacKey[5], Reason)); } while (FALSE); } /** * Deauthenicate a station. * This shall be called only after a station * is authenticated. * The station entry must be removed from the * Mac table already. * So this function will be invoked only once * for each station entry. */ VOID AmDeauthenticateSta( _In_ PAP_ASSOC_MGR AssocMgr, _In_ PAP_STA_ENTRY StaEntry, _In_ ULONG Reason, _In_ BOOLEAN SendDeauthFrame, _In_ USHORT Dot11Reason ) { // // Disassociate the station first. // This is a no-op if the station is not associated // AmDisassociateSta( AssocMgr, StaEntry, Reason ); if (SendDeauthFrame) { // // Send deauth frame // AmSendDeauthenticationFrame( AssocMgr, &StaEntry->MacHashEntry.MacKey, Dot11Reason ); } // // Set station state // ApSetStaAssocState( StaEntry, dot11_assoc_state_unauth_unassoc ); // // Add trace // MpTrace(COMP_ASSOC_MGR, DBG_NORMAL, ("Port(%u): Station %02X-%02X-%02X-%02X-%02X-%02X deauthenticated due to reason 0x%08x.", AP_GET_PORT_NUMBER(AssocMgr->ApPort), StaEntry->MacHashEntry.MacKey[0], StaEntry->MacHashEntry.MacKey[1], StaEntry->MacHashEntry.MacKey[2], StaEntry->MacHashEntry.MacKey[3], StaEntry->MacHashEntry.MacKey[4], StaEntry->MacHashEntry.MacKey[5], Dot11Reason)); // // dereference the entry, may result in deleting the entry // ApDerefSta(StaEntry); } /** Supporting functions for NDIS indications */ /** Allocate association completion parameters */ NDIS_STATUS AmAllocAssocCompletePara( _In_ PAP_ASSOC_MGR AssocMgr, _In_ USHORT AssocReqFrameSize, _In_ USHORT AssocRespFrameSize, _Outptr_ PDOT11_INCOMING_ASSOC_COMPLETION_PARAMETERS * AssocCompletePara, _Out_ PULONG AssocCompleteParaSize ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; ULONG requiredSize = 0; PAP_CONFIG Config; ULONG beaconFrameSize; Config = AP_GET_CONFIG(AssocMgr->ApPort); beaconFrameSize = Config->ApBeaconIESize + FIELD_OFFSET(DOT11_BEACON_FRAME, InfoElements); requiredSize = sizeof(DOT11_INCOMING_ASSOC_COMPLETION_PARAMETERS) // data structure itself + AssocReqFrameSize // association request frame size, can be zero + AssocRespFrameSize // association response frame size, can be zero + beaconFrameSize // beacon frame size + sizeof(ULONG) // active PHY ; MP_ALLOCATE_MEMORY( AP_GET_MP_HANDLE(AssocMgr->ApPort), AssocCompletePara, requiredSize, EXTAP_MEMORY_TAG ); if (NULL == *AssocCompletePara) { MpTrace(COMP_ASSOC_MGR, DBG_SERIOUS, ("Port(%u): Failed to allocate %u bytes for association complete parameters.", AP_GET_PORT_NUMBER(AssocMgr->ApPort), requiredSize)); ndisStatus = NDIS_STATUS_RESOURCES; } else { NdisZeroMemory(*AssocCompletePara, requiredSize); *AssocCompleteParaSize = requiredSize; } return ndisStatus; } /** Allocate association request received parameters */ NDIS_STATUS AmAllocAssocReqPara( _In_ PAP_ASSOC_MGR AssocMgr, _In_ USHORT AssocReqFrameSize, _Outptr_ PDOT11_INCOMING_ASSOC_REQUEST_RECEIVED_PARAMETERS * AssocReqPara, _Out_ PULONG AssocReqParaSize ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; ULONG requiredSize = 0; requiredSize = sizeof(DOT11_INCOMING_ASSOC_REQUEST_RECEIVED_PARAMETERS) // data structure itself + AssocReqFrameSize // association request frame size, can be zero ; MP_ALLOCATE_MEMORY( AP_GET_MP_HANDLE(AssocMgr->ApPort), AssocReqPara, requiredSize, EXTAP_MEMORY_TAG ); if (NULL == *AssocReqPara) { MpTrace(COMP_ASSOC_MGR, DBG_SERIOUS, ("Port(%u): Failed to allocate %u bytes for association request received parameters.", AP_GET_PORT_NUMBER(AssocMgr->ApPort), requiredSize)); ndisStatus = NDIS_STATUS_RESOURCES; } else { NdisZeroMemory(*AssocReqPara, requiredSize); *AssocReqParaSize = requiredSize; } return ndisStatus; } /** * Prepare information for association start indication. * The buffer is allocated by the caller. */ VOID AmPrepareAssocStartPara( _In_ PDOT11_MAC_ADDRESS StaMacAddr, _Out_ PDOT11_INCOMING_ASSOC_STARTED_PARAMETERS AssocStartPara ) { // // NDIS header // MP_ASSIGN_NDIS_OBJECT_HEADER( AssocStartPara->Header, NDIS_OBJECT_TYPE_DEFAULT, DOT11_INCOMING_ASSOC_STARTED_PARAMETERS_REVISION_1, sizeof(DOT11_INCOMING_ASSOC_STARTED_PARAMETERS) ); // // Copy Mac address // RtlCopyMemory( AssocStartPara->PeerMacAddr, *StaMacAddr, sizeof(DOT11_MAC_ADDRESS) ); } /** * Prepare information for association request received indication. * The buffer is allocated by the caller. * The caller must make sure the buffer is big enough to hold all data. */ VOID AmPrepareAssocReqPara( _In_ PAP_ASSOC_MGR AssocMgr, _In_reads_bytes_(DOT11_ADDRESS_SIZE) PDOT11_MAC_ADDRESS StaMacAddr, _In_ BOOLEAN Reassociation, _In_reads_bytes_(RequestFrameSize) PUCHAR RequestFrame, _In_ USHORT RequestFrameSize, _Out_ PDOT11_INCOMING_ASSOC_REQUEST_RECEIVED_PARAMETERS AssocReqPara, _Inout_ PULONG AssocReqParaSize ) { ULONG requiredAssocReqParaSize = 0; UNREFERENCED_PARAMETER(AssocMgr); // // Calculate the size of the association request received parameters // requiredAssocReqParaSize = sizeof(DOT11_INCOMING_ASSOC_REQUEST_RECEIVED_PARAMETERS) + RequestFrameSize; MPASSERT(*AssocReqParaSize >= requiredAssocReqParaSize); // // Clear everything // NdisZeroMemory(AssocReqPara, requiredAssocReqParaSize); *AssocReqParaSize = requiredAssocReqParaSize; // // Fill in information // // // NDIS header // MP_ASSIGN_NDIS_OBJECT_HEADER( AssocReqPara->Header, NDIS_OBJECT_TYPE_DEFAULT, DOT11_INCOMING_ASSOC_REQUEST_RECEIVED_PARAMETERS_REVISION_1, sizeof(DOT11_INCOMING_ASSOC_REQUEST_RECEIVED_PARAMETERS) ); // // Station Mac address // RtlCopyMemory( AssocReqPara->PeerMacAddr, *StaMacAddr, sizeof(DOT11_MAC_ADDRESS) ); // // Reassociation? // AssocReqPara->bReAssocReq = Reassociation; // // Request frame follows the data structure immediately // AssocReqPara->uAssocReqSize = RequestFrameSize; AssocReqPara->uAssocReqOffset = sizeof(DOT11_INCOMING_ASSOC_REQUEST_RECEIVED_PARAMETERS); // // Copy association request // RtlCopyMemory( Add2Ptr(AssocReqPara, AssocReqPara->uAssocReqOffset), RequestFrame, RequestFrameSize ); } /** * Prepare information for association complete indication. * The buffer is allocated by the caller. * The caller must make sure the buffer is big enough to hold all data. */ VOID AmPrepareAssocCompletePara( _In_ PAP_ASSOC_MGR AssocMgr, _In_opt_ PAP_STA_ENTRY StaEntry, _In_reads_bytes_(DOT11_ADDRESS_SIZE) PDOT11_MAC_ADDRESS StaMacAddr, _In_ ULONG Status, _In_ UCHAR ErrorSource, _In_ BOOLEAN Reassociation, _In_reads_bytes_opt_(RequestFrameSize) PUCHAR RequestFrame, _In_ USHORT RequestFrameSize, _In_reads_bytes_opt_(ResponseFrameSize) PUCHAR ResponseFrame, _In_ USHORT ResponseFrameSize, _Out_ PDOT11_INCOMING_ASSOC_COMPLETION_PARAMETERS AssocCompletePara, _Inout_ PULONG AssocCompleteParaSize ) { ULONG requiredAssocCompleteParaSize = 0; USHORT activePhyListSize; ULONG* phyId; PAP_CONFIG ApConfig; ULONG beaconFrameSize; do { // // TODO: only support one active PHY? // activePhyListSize = sizeof(ULONG); ApConfig = &AssocMgr->ApPort->Config; beaconFrameSize = ApConfig->ApBeaconIESize + FIELD_OFFSET(DOT11_BEACON_FRAME, InfoElements); // // Calculate the size of the association completion parameters // requiredAssocCompleteParaSize = sizeof(DOT11_INCOMING_ASSOC_COMPLETION_PARAMETERS) + RequestFrameSize + ResponseFrameSize + activePhyListSize + beaconFrameSize ; MPASSERT(*AssocCompleteParaSize >= requiredAssocCompleteParaSize); // // Store the pointer in station entry // *AssocCompleteParaSize = requiredAssocCompleteParaSize; // // Clear everything // NdisZeroMemory(AssocCompletePara, requiredAssocCompleteParaSize); // // Fill in information // // // NDIS header // MP_ASSIGN_NDIS_OBJECT_HEADER( AssocCompletePara->Header, NDIS_OBJECT_TYPE_DEFAULT, DOT11_INCOMING_ASSOC_COMPLETION_PARAMETERS_REVISION_1, sizeof(DOT11_INCOMING_ASSOC_COMPLETION_PARAMETERS) ); // // Status // AssocCompletePara->uStatus = Status; // // Error source // AssocCompletePara->ucErrorSource = ErrorSource; // // Station Mac address // RtlCopyMemory( AssocCompletePara->PeerMacAddr, *StaMacAddr, sizeof(DOT11_MAC_ADDRESS) ); // // Reassociation? // AssocCompletePara->bReAssocReq = AssocCompletePara->bReAssocResp = Reassociation; // // The following only applies to an successful association // if (Status != 0) { break; } // if the Status is 0 then these pointers must be valid if (StaEntry == NULL || RequestFrame == NULL || ResponseFrame == NULL) { MPASSERT(StaEntry != NULL); MPASSERT(RequestFrame != NULL); MPASSERT(ResponseFrame != NULL); return; } // // Request frame follows the data structure immediately // MPASSERT(RequestFrameSize != 0); AssocCompletePara->uAssocReqSize = RequestFrameSize; AssocCompletePara->uAssocReqOffset = sizeof(DOT11_INCOMING_ASSOC_COMPLETION_PARAMETERS); RtlCopyMemory( Add2Ptr(AssocCompletePara, AssocCompletePara->uAssocReqOffset), RequestFrame, RequestFrameSize ); // // Response frame follows request frame // MPASSERT(ResponseFrameSize != 0); AssocCompletePara->uAssocRespSize = ResponseFrameSize; AssocCompletePara->uAssocRespOffset = AssocCompletePara->uAssocReqOffset + RequestFrameSize; RtlCopyMemory( Add2Ptr(AssocCompletePara, AssocCompletePara->uAssocRespOffset), ResponseFrame, ResponseFrameSize ); // // Active PHY list follows response frame // AssocCompletePara->uActivePhyListSize = activePhyListSize; AssocCompletePara->uActivePhyListOffset = AssocCompletePara->uAssocRespOffset + ResponseFrameSize; phyId = (ULONG*)Add2Ptr(AssocCompletePara, AssocCompletePara->uActivePhyListOffset); (*phyId) = VNic11QueryOperatingPhyId(AP_GET_VNIC(AssocMgr->ApPort)); // // Beacon frames // AssocCompletePara->uBeaconSize = beaconFrameSize; AssocCompletePara->uBeaconOffset = AssocCompletePara->uActivePhyListOffset + activePhyListSize; RtlCopyMemory( Add2Ptr(AssocCompletePara, AssocCompletePara->uBeaconOffset + FIELD_OFFSET(DOT11_BEACON_FRAME, InfoElements)), ApConfig->ApBeaconIEData, ApConfig->ApBeaconIESize ); MPASSERT(StaEntry != NULL); AssocCompletePara->AuthAlgo = StaEntry->AuthAlgo; AssocCompletePara->UnicastCipher = StaEntry->UnicastCipher; AssocCompletePara->MulticastCipher = StaEntry->MulticastCipher; } while (FALSE); } /** * Prepare in information for disassociation indication. * The buffer is allocated by the caller. */ VOID AmPrepareDisassocPara( _In_ PDOT11_MAC_ADDRESS StaMacAddr, _In_ ULONG Reason, _Out_ PDOT11_DISASSOCIATION_PARAMETERS DisassocPara ) { // // NDIS header // MP_ASSIGN_NDIS_OBJECT_HEADER( DisassocPara->Header, NDIS_OBJECT_TYPE_DEFAULT, DOT11_DISASSOCIATION_PARAMETERS_REVISION_1, sizeof(DOT11_DISASSOCIATION_PARAMETERS) ); // // Copy Mac address // RtlCopyMemory( DisassocPara->MacAddr, *StaMacAddr, sizeof(DOT11_MAC_ADDRESS) ); // // Reason // DisassocPara->uReason = Reason; // // IHV specific // DisassocPara->uIHVDataSize = DisassocPara->uIHVDataOffset = 0; } /** * Timer related functions */ /** * MAC hash table enum callback function for inactive timeout. * An entry is removed from the MAC hash table if its inactive * time exceeds the predefined limit. * The caller must hold a write lock. */ BOOLEAN AmInactiveStaEntryCallback( _In_ PMAC_HASH_TABLE Table, _In_ PMAC_HASH_ENTRY MacEntry, _In_ PVOID CallbackCtxt ) { LIST_ENTRY * head = (LIST_ENTRY *)CallbackCtxt; // list of removed station entries PAP_STA_ENTRY staEntry = CONTAINING_RECORD(MacEntry, AP_STA_ENTRY, MacHashEntry); if (ApIncrementStaInactiveTime(staEntry) >= AP_NO_ACTIVITY_TIME) { // remove the entry from the table RemoveMacHashEntry(Table, MacEntry); // insert it into the list InsertTailList(head, &MacEntry->Linkage); } return TRUE; } /** * Timeout callback for the station inactive timer. * * \param param PAP_ASSOC_MGR */ VOID AmStaInactiveTimeoutCallback( PVOID SystemSpecific1, PVOID param, PVOID SystemSpecific2, PVOID SystemSpecific3 ) { PAP_ASSOC_MGR assocMgr = (PAP_ASSOC_MGR)param; LIST_ENTRY head; // list of stations to deauthenticate LIST_ENTRY * entry; PMAC_HASH_ENTRY macEntry; PAP_STA_ENTRY staEntry; MP_RW_LOCK_STATE lockState; UNREFERENCED_PARAMETER(SystemSpecific1); UNREFERENCED_PARAMETER(SystemSpecific2); UNREFERENCED_PARAMETER(SystemSpecific3); // // Initialize the list head // InitializeListHead(&head); // // Acquire write lock on the Mac table // MP_ACQUIRE_WRITE_LOCK(&assocMgr->MacHashTableLock, &lockState); // // increment inactive time for all stations // The stations whose inactive time exceeds the limit // are removed from the MAC table and insert in the list to deauthenticate // EnumMacEntry( &assocMgr->MacHashTable, AmInactiveStaEntryCallback, &head ); // // Release the write lock // MP_RELEASE_WRITE_LOCK(&assocMgr->MacHashTableLock, &lockState); // // Deauthenticate all stations in the list // while(!IsListEmpty(&head)) { entry = RemoveHeadList(&head); macEntry = CONTAINING_RECORD(entry, MAC_HASH_ENTRY, Linkage); staEntry = CONTAINING_RECORD(macEntry, AP_STA_ENTRY, MacHashEntry); MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): Deauthentication STA %02X-%02X-%02X-%02X-%02X-%02X because it has been inactive for a long time.", AP_GET_PORT_NUMBER(assocMgr->ApPort), staEntry->MacHashEntry.MacKey[0], staEntry->MacHashEntry.MacKey[1], staEntry->MacHashEntry.MacKey[2], staEntry->MacHashEntry.MacKey[3], staEntry->MacHashEntry.MacKey[4], staEntry->MacHashEntry.MacKey[5] )); AmDeauthenticateSta( assocMgr, staEntry, DOT11_DISASSOC_REASON_PEER_UNREACHABLE, TRUE, // Send deauth frame DOT11_MGMT_REASON_INACTIVITY ); } } /** * Timeout callback for the association timer. * The ref count of the station must be incremented * when the timer is scheduled. * * \param param PAP_STA_ENTRY */ VOID AmStaAssocTimeoutCallback( PVOID SystemSpecific1, PVOID param, PVOID SystemSpecific2, PVOID SystemSpecific3 ) { PAP_STA_ENTRY staEntry = (PAP_STA_ENTRY)param; PAP_ASSOC_MGR assocMgr = staEntry->AssocMgr; BOOLEAN deauthSta = FALSE; MP_RW_LOCK_STATE lockState; /** NDIS indications */ DOT11_INCOMING_ASSOC_STARTED_PARAMETERS assocStartPara; PDOT11_INCOMING_ASSOC_COMPLETION_PARAMETERS assocCompletePara = NULL; ULONG assocCompleteParaSize = 0; UNREFERENCED_PARAMETER(SystemSpecific1); UNREFERENCED_PARAMETER(SystemSpecific2); UNREFERENCED_PARAMETER(SystemSpecific3); // // Acquire write lock on the Mac table // MP_ACQUIRE_WRITE_LOCK(&assocMgr->MacHashTableLock, &lockState); // // Check the waiting for association request flag first // if (InterlockedExchange( &staEntry->WaitingForAssocReq, STA_NOT_WAITING_FOR_ASSOC_REQ ) == STA_WAITING_FOR_ASSOC_REQ) { // // Timeout for association request. // We need to remove the station from the MAC table // and deauth it // RemoveMacHashEntry( &assocMgr->MacHashTable, &staEntry->MacHashEntry ); deauthSta = TRUE; } // // Release the write lock // MP_RELEASE_WRITE_LOCK(&assocMgr->MacHashTableLock, &lockState); if (deauthSta) { // // Deauth the station // MpTrace(COMP_ASSOC_MGR, DBG_NORMAL,("Port(%u): Deauthentication STA %02X-%02X-%02X-%02X-%02X-%02X because of association request timeout.", AP_GET_PORT_NUMBER(assocMgr->ApPort), staEntry->MacHashEntry.MacKey[0], staEntry->MacHashEntry.MacKey[1], staEntry->MacHashEntry.MacKey[2], staEntry->MacHashEntry.MacKey[3], staEntry->MacHashEntry.MacKey[4], staEntry->MacHashEntry.MacKey[5] )); AmDeauthenticateSta( assocMgr, staEntry, DOT11_DISASSOC_REASON_OS, // This reason code is ignored because the station is not associated yet. TRUE, // Send deauth frame DOT11_MGMT_REASON_AUTH_NOT_VALID ); // // Send back to back association start and association complete indications to OS // // // Allocate association complete parameters // if (AmAllocAssocCompletePara( assocMgr, 0, // no association request frame 0, // no association response frame &assocCompletePara, &assocCompleteParaSize ) != NDIS_STATUS_SUCCESS) { MpTrace(COMP_ASSOC_MGR, DBG_SERIOUS, ("Port(%u): Failed to allocate association completion parameters.", AP_GET_PORT_NUMBER(assocMgr->ApPort))); } else { // // Fill in association complete information // AmPrepareAssocCompletePara( assocMgr, NULL, // no STA entry is required &staEntry->MacHashEntry.MacKey, DOT11_FRAME_STATUS_FAILURE, // TODO: a better status code? DOT11_ASSOC_ERROR_SOURCE_REMOTE, FALSE, // not reassociation NULL, // no association request frame 0, NULL, 0, // no association response frame assocCompletePara, &assocCompleteParaSize ); // // Prepare association start indication // AmPrepareAssocStartPara( &staEntry->MacHashEntry.MacKey, &assocStartPara ); // // Association start indication // ApIndicateDot11Status( assocMgr->ApPort, NDIS_STATUS_DOT11_INCOMING_ASSOC_STARTED, NULL, // no request ID &assocStartPara, sizeof(assocStartPara) ); // // Association complete indication // ApIndicateDot11Status( assocMgr->ApPort, NDIS_STATUS_DOT11_INCOMING_ASSOC_COMPLETION, NULL, // no request ID assocCompletePara, assocCompleteParaSize ); // // Free indications // MP_FREE_MEMORY(assocCompletePara); } } // // Deref the station // ApDerefSta(staEntry); }
Our Services
-
What our customers say about us?
Read our customer testimonials to find out why our clients keep returning for their projects.
View Testimonials