Sample Code
Windows Driver Samples/ Native Wi-Fi Miniport Sample Driver/ C++/ extsta/ st_aplst.c/
/*++
Copyright (c) Microsoft Corporation. All rights reserved.
Module Name:
St_aplst.c
Abstract:
STA layer BSS list functionality
Revision History:
When What
---------- ----------------------------------------------
08-01-2005 Created
Notes:
--*/
#include "precomp.h"
#include "st_aplst.h"
#include "st_adhoc.h"
#if DOT11_TRACE_ENABLED
#include "St_aplst.tmh"
#endif
VOID
StaReceiveBeacon(
_In_ PMP_EXTSTA_PORT pStation,
_In_ PMP_RX_MPDU pFragment,
_In_ ULONG TotalLength
)
{
NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS;
PUCHAR pPacketBuffer;
PDOT11_BEACON_FRAME pDot11BeaconFrame;
ULONG uOffsetOfInfoElemBlob =
FIELD_OFFSET(DOT11_BEACON_FRAME, InfoElements) + sizeof(DOT11_MGMT_HEADER);
ULONG uInfoElemBlobSize = 0;
pPacketBuffer = MP_RX_MPDU_DATA(pFragment);
do
{
//
// Drop if its doesnt contain atleast the
// fixed size portion (DOT11_BEACON_FRAME)
//
if (uOffsetOfInfoElemBlob > TotalLength)
{
break;
}
pDot11BeaconFrame = (PDOT11_BEACON_FRAME)(pPacketBuffer + sizeof(DOT11_MGMT_HEADER));
// Validate information elements blob
ndisStatus = Dot11GetInfoBlobSize(
pPacketBuffer,
TotalLength,
uOffsetOfInfoElemBlob,
&uInfoElemBlobSize
);
if (ndisStatus != NDIS_STATUS_SUCCESS)
{
break;
}
if (pDot11BeaconFrame->Capability.IBSS) {
ndisStatus = StaSaveAdHocStaInfo(
pStation,
pFragment,
pDot11BeaconFrame,
uInfoElemBlobSize
);
}
ndisStatus = StaProcessBeaconForConfigInfo(
pStation,
pFragment,
(PUCHAR)&pDot11BeaconFrame->InfoElements,
TotalLength
);
if (ndisStatus != NDIS_STATUS_SUCCESS)
{
break;
}
} while (FALSE);
}
BOOLEAN
StaHasAPConfigurationChanged(
_In_ PMP_EXTSTA_PORT pStation
)
{
if (pStation->ConnectContext.ActiveAP->Channel != pStation->ConnectContext.AssociationChannel)
{
//
// Channel has changed. Lets do a fresh association
//
MpTrace(COMP_ASSOC, DBG_LOUD, ("AP channel changed from %d to %d\n",
pStation->ConnectContext.AssociationChannel,
pStation->ConnectContext.ActiveAP->Channel));
return TRUE;
}
//
// Currently we dont check any other parameters.
// We can check the SSID. If the AP was stopped/restarted
// the SSID may have changed and we may still see the beacon
// but the AP has lost all our state. It should send us a
// disassociate, but it may not
//
return FALSE;
}
VOID
StaCheckForProtectionInERP(
_In_ PMP_EXTSTA_PORT pStation,
_In_reads_bytes_(infoBlobLength) PUCHAR pInfoBlob,
_In_ ULONG infoBlobLength
)
{
NDIS_STATUS status = NDIS_STATUS_SUCCESS;
UCHAR erpIELength = 0;
PUCHAR erpIEBuf = NULL;
status = Dot11GetInfoEle(
pInfoBlob,
infoBlobLength,
DOT11_INFO_ELEMENT_ID_ERP,
&erpIELength,
&erpIEBuf
);
if (status != NDIS_STATUS_SUCCESS || erpIELength != 1) // ERP IE length has to be 1
{
VNic11SetCTSToSelfOption(STA_GET_VNIC(pStation), FALSE);
}
else
{
if (((DOT11_ERP_IE*)erpIEBuf)->UseProtection == 1)
{
VNic11SetCTSToSelfOption(STA_GET_VNIC(pStation), TRUE);
}
else
{
VNic11SetCTSToSelfOption(STA_GET_VNIC(pStation), FALSE);
}
}
}
NDIS_STATUS
StaProcessBeaconForConfigInfo(
_In_ PMP_EXTSTA_PORT pStation,
_In_ PMP_RX_MPDU pFragment,
_In_reads_bytes_(TotalLength) PUCHAR pInfoBlob,
_In_ ULONG TotalLength
)
{
NDIS_STATUS status = NDIS_STATUS_SUCCESS;
BOOLEAN beaconIsFromAP = FALSE;
do
{
if (pStation->Config.CheckForProtectionInERP == FALSE &&
pStation->ConnectContext.UpdateLinkQuality == FALSE)
{
break;
}
NdisDprAcquireSpinLock(&(pStation->ConnectContext.Lock));
if (pStation->ConnectContext.AssociateState == ASSOC_STATE_ASSOCIATED &&
NdisEqualMemory(pStation->ConnectContext.ActiveAP->Dot11BSSID,
((DOT11_MGMT_HEADER*)MP_RX_MPDU_DATA(pFragment))->SA,
sizeof(DOT11_MAC_ADDRESS)))
{
beaconIsFromAP = TRUE;
}
NdisDprReleaseSpinLock(&(pStation->ConnectContext.Lock));
if (beaconIsFromAP == FALSE)
{
break;
}
// look for UseProtection bit in beacon's ERP IE
if (pStation->Config.CheckForProtectionInERP == TRUE)
{
StaCheckForProtectionInERP(pStation, pInfoBlob, TotalLength);
pStation->Config.CheckForProtectionInERP = FALSE;
}
// indicate link quality indication to the os
if (pStation->ConnectContext.UpdateLinkQuality == TRUE &&
pStation->Config.BSSType == dot11_BSS_type_infrastructure)
{
UCHAR buffer[sizeof(DOT11_LINK_QUALITY_PARAMETERS) + sizeof(DOT11_LINK_QUALITY_ENTRY)];
ULONG bufferLength = sizeof(buffer);
DOT11_LINK_QUALITY_PARAMETERS* pLinkQualityParams = (DOT11_LINK_QUALITY_PARAMETERS*)&buffer[0];
DOT11_LINK_QUALITY_ENTRY* pEntry = (DOT11_LINK_QUALITY_ENTRY*)&buffer[sizeof(DOT11_LINK_QUALITY_PARAMETERS)];
// reset the variable
pStation->ConnectContext.UpdateLinkQuality = FALSE;
// initialize indication buffer
NdisZeroMemory(&buffer[0], bufferLength);
MP_ASSIGN_NDIS_OBJECT_HEADER(pLinkQualityParams->Header,
NDIS_OBJECT_TYPE_DEFAULT,
DOT11_LINK_QUALITY_PARAMETERS_REVISION_1,
sizeof(DOT11_LINK_QUALITY_PARAMETERS));
pLinkQualityParams->uLinkQualityListSize = 1;
pLinkQualityParams->uLinkQualityListOffset = sizeof(DOT11_LINK_QUALITY_PARAMETERS);
// previous NdisZeroMemory already set pEntry->PeerMacAddr to all 0x00, which
// means the link quality is for current network
pEntry->ucLinkQuality = pFragment->Msdu->LinkQuality;
StaIndicateDot11Status(pStation,
NDIS_STATUS_DOT11_LINK_QUALITY,
NULL,
&buffer[0],
bufferLength);
}
} while (FALSE);
return status;
}
BOOLEAN
StaMatchAPSSID(
_In_ PMP_BSS_ENTRY pAPEntry,
_In_ PSTA_CURRENT_CONFIG pConfig
)
{
DOT11_SSID APSSID, *pMatchSSID;
NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS;
BOOLEAN bIsAcceptable = TRUE; // Default accept
ULONG i;
ULONG uOffsetOfInfoElemBlob = FIELD_OFFSET(DOT11_BEACON_FRAME, InfoElements);
do
{
// Zero length DesiredSSID is the wildcard SSID. That would match any AP and
// we dont need to compare
if (pConfig->SSID.uSSIDLength != 0)
{
// Get the SSID from the Beacon IE's
if (pAPEntry->BeaconFrameSize > uOffsetOfInfoElemBlob)
{
ndisStatus = Dot11CopySSIDFromMemoryBlob(
pAPEntry->pDot11BeaconFrame + uOffsetOfInfoElemBlob,
pAPEntry->BeaconFrameSize - uOffsetOfInfoElemBlob,
&APSSID
);
if (ndisStatus != NDIS_STATUS_SUCCESS)
{
// No SSID IE in the AP entry. We assume this is a bad AP and
// reject it
MpTrace(COMP_ASSOC, DBG_LOUD, (" - Reject (No SSID IE)\n"));
bIsAcceptable = FALSE;
break;
}
pMatchSSID = &APSSID;
}
else
{
pMatchSSID = NULL;
}
if ((pMatchSSID == NULL) ||
Dot11IsHiddenSSID(pMatchSSID->ucSSID, pMatchSSID->uSSIDLength))
{
if (pAPEntry->ProbeFrameSize > uOffsetOfInfoElemBlob)
{
// Hidden SSID. See if we have an ssid from a probe response
ndisStatus = Dot11CopySSIDFromMemoryBlob(
pAPEntry->pDot11ProbeFrame + uOffsetOfInfoElemBlob,
pAPEntry->ProbeFrameSize - uOffsetOfInfoElemBlob,
&APSSID
);
if (ndisStatus != NDIS_STATUS_SUCCESS)
{
// No SSID IE in the AP entry. We assume this is a bad AP and
// reject it
MpTrace(COMP_ASSOC, DBG_LOUD, (" - Reject (No SSID IE)\n"));
bIsAcceptable = FALSE;
break;
}
// Use this for matching
pMatchSSID = &APSSID;
MpTrace(COMP_ASSOC, DBG_LOUD, (" - Using probe response SSID\n"));
}
}
// Check that SSID matches (Note: case sensitive comparison)
if ((pMatchSSID == NULL) ||
(pMatchSSID->uSSIDLength != pConfig->SSID.uSSIDLength) ||
(NdisEqualMemory(pMatchSSID->ucSSID, pConfig->SSID.ucSSID, pMatchSSID->uSSIDLength) != TRUE))
{
MpTrace(COMP_ASSOC, DBG_LOUD, (" - Reject (mismatched SSID)\n"));
bIsAcceptable = FALSE;
break;
}
}
// Check the excluded MAC address list
if (pConfig->IgnoreAllMACAddresses)
{
MpTrace(COMP_ASSOC, DBG_LOUD, (" - Reject (Ignore all MAC addresses)\n"));
bIsAcceptable = FALSE;
break;
}
else
{
bIsAcceptable = TRUE;
// Walk through the excluded MAC address list
for (i = 0; i < pConfig->ExcludedMACAddressCount; i++)
{
if (MP_COMPARE_MAC_ADDRESS(pAPEntry->MacAddress,
pConfig->ExcludedMACAddressList[i]) == TRUE)
{
bIsAcceptable = FALSE;
break;
}
}
if (bIsAcceptable == FALSE)
{
MpTrace(COMP_ASSOC, DBG_LOUD, (" - Reject (Excluded MAC addresses)\n"));
break;
}
}
// Check the desired BSSID list
if (pConfig->AcceptAnyBSSID == FALSE)
{
bIsAcceptable = FALSE;
// Walk through the desired BSSID list
for (i = 0; i < pConfig->DesiredBSSIDCount; i++)
{
if (MP_COMPARE_MAC_ADDRESS(pAPEntry->Dot11BSSID,
pConfig->DesiredBSSIDList[i]) == TRUE)
{
// This is an acceptable BSSID
bIsAcceptable = TRUE;
break;
}
}
if (bIsAcceptable == FALSE)
{
MpTrace(COMP_ASSOC, DBG_LOUD, (" - Reject (Not in desired BSSID list)\n"));
break;
}
}
// The SSID matches
bIsAcceptable = TRUE;
break;
}while (FALSE);
return bIsAcceptable;
}
BOOLEAN
StaMatchPhyId(
_In_ PMP_EXTSTA_PORT pStation,
_In_ ULONG PhyId
)
{
ULONG index;
ASSERT(PhyId != DOT11_PHY_ID_ANY);
if (PhyId == DOT11_PHY_ID_ANY || PhyId == STA_INVALID_PHY_ID)
{
return FALSE;
}
if (pStation->Config.DesiredPhyList[0] == DOT11_PHY_ID_ANY)
return TRUE;
//
// Check if the PHY ID is in our desired PHY ID list
//
for (index = 0; index < pStation->Config.DesiredPhyCount; index++)
{
if (PhyId == pStation->Config.DesiredPhyList[index])
return TRUE;
}
MpTrace(COMP_ASSOC, DBG_LOUD, (" - Reject (Not in desired PHY list)\n"));
return FALSE;
}
BOOLEAN
StaMatchAPDataRates(
_In_ PMP_BSS_ENTRY pAPEntry,
_In_ PMP_EXTSTA_PORT pStation
)
{
NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS;
DOT11_RATE_SET rateSet = {0};
BOOLEAN bIsAcceptable = TRUE; // Default accept
do
{
ndisStatus = Dot11GetRateSetFromInfoEle(
pAPEntry->pDot11InfoElemBlob,
pAPEntry->InfoElemBlobSize,
TRUE,
&rateSet
);
if (ndisStatus != NDIS_STATUS_SUCCESS)
{
MpTrace(COMP_ASSOC, DBG_LOUD, (" - Reject (No Supported Rates IE)\n"));
bIsAcceptable = FALSE;
break;
}
//
// Check that all the basic data rates required by the
// access point are supported by us
//
ndisStatus = VNic11ValidateOperationalRates(
STA_GET_VNIC(pStation),
pAPEntry->PhyId,
rateSet.ucRateSet,
rateSet.uRateSetLength
);
if (ndisStatus != NDIS_STATUS_SUCCESS)
{
MpTrace(COMP_ASSOC, DBG_LOUD, (" - Reject (Mismatched data rate)\n"));
bIsAcceptable = FALSE;
break;
}
bIsAcceptable = TRUE;
}while (FALSE);
return bIsAcceptable;
}
BOOLEAN
StaMatchAPSecurity(
_In_ PMP_BSS_ENTRY pAPEntry,
_In_ PMP_EXTSTA_PORT pStation
)
{
BOOLEAN bIsAcceptable = FALSE;
UCHAR SecIELength = 0;
PUCHAR SecIEData = NULL;
RSN_IE_INFO RSNIEInfo;
NDIS_STATUS ndisStatus;
__try
{
//
// Privacy bit
//
if (pStation->Config.UnicastCipherAlgorithm == DOT11_CIPHER_ALGO_NONE)
{
if (pAPEntry->Dot11Capability.Privacy)
{
MpTrace(COMP_ASSOC, DBG_LOUD, (" - Reject (Privacy bit set)\n"));
__leave;
}
}
else if (pStation->Config.UnicastCipherAlgorithm == DOT11_CIPHER_ALGO_WEP)
{
//
// If ExcludeUnencrypted is false, we associate with an AP even if
// it is not beaconing privacy bit
//
if (pStation->Config.ExcludeUnencrypted == TRUE)
{
if (!pAPEntry->Dot11Capability.Privacy)
{
MpTrace(COMP_ASSOC, DBG_LOUD, (" - Reject (Privacy bit clear)\n"));
__leave;
}
}
}
else
{
if (!pAPEntry->Dot11Capability.Privacy)
{
MpTrace(COMP_ASSOC, DBG_LOUD, (" - Reject (Privacy bit clear)\n"));
__leave;
}
}
//
// Check RSNA (WPA2) or WPA
//
if (pStation->Config.AuthAlgorithm == DOT11_AUTH_ALGO_RSNA ||
pStation->Config.AuthAlgorithm == DOT11_AUTH_ALGO_RSNA_PSK)
{
ndisStatus = Dot11GetInfoEle(pAPEntry->pDot11InfoElemBlob,
pAPEntry->InfoElemBlobSize,
DOT11_INFO_ELEMENT_ID_RSN,
&SecIELength,
(PVOID)&SecIEData);
if (ndisStatus != NDIS_STATUS_SUCCESS)
{
MpTrace(COMP_ASSOC, DBG_LOUD, (" - Reject (AP not RSNA enabled)\n"));
__leave;
}
ndisStatus = Dot11ParseRNSIE(SecIEData, RSNA_OUI_PREFIX, SecIELength, &RSNIEInfo);
if (ndisStatus != NDIS_STATUS_SUCCESS)
{
MpTrace(COMP_ASSOC, DBG_LOUD, (" - Reject (AP contains invalid RSN IE)\n"));
__leave;
}
}
else if (pStation->Config.AuthAlgorithm == DOT11_AUTH_ALGO_WPA ||
pStation->Config.AuthAlgorithm == DOT11_AUTH_ALGO_WPA_PSK)
{
ndisStatus = Dot11GetWPAIE(pAPEntry->pDot11InfoElemBlob,
pAPEntry->InfoElemBlobSize,
&SecIELength,
(PVOID)&SecIEData);
if (ndisStatus != NDIS_STATUS_SUCCESS)
{
MpTrace(COMP_ASSOC, DBG_LOUD, (" - Reject (AP not WPA enabled)\n"));
__leave;
}
ndisStatus = Dot11ParseRNSIE(SecIEData, WPA_OUI_PREFIX, SecIELength, &RSNIEInfo);
if (ndisStatus != NDIS_STATUS_SUCCESS)
{
MpTrace(COMP_ASSOC, DBG_LOUD, (" - Reject (AP contains invalid WPA IE)\n"));
__leave;
}
}
else
{
bIsAcceptable = TRUE;
__leave;
}
//
// Check if the AP is running RNSA/WPA with AKM and ciphers that meet our requirement.
//
bIsAcceptable = StaMatchRSNInfo(pStation, &RSNIEInfo);
}
__finally
{
}
return bIsAcceptable;
}
//
// AP entry must be locked against change
//
BOOLEAN
StaMatchAPEntry(
_In_ PMP_BSS_ENTRY pAPEntry,
_In_ PMP_EXTSTA_PORT pStation
)
{
PSTA_CURRENT_CONFIG pConfig = &(pStation->Config);
MpTrace(COMP_ASSOC, DBG_LOUD, ("Matching AP: %02X-%02X-%02X-%02X-%02X-%02X",
pAPEntry->Dot11BSSID[0], pAPEntry->Dot11BSSID[1], pAPEntry->Dot11BSSID[2],
pAPEntry->Dot11BSSID[3], pAPEntry->Dot11BSSID[4], pAPEntry->Dot11BSSID[5]));
// Ignore entries ready for deletion
if (pAPEntry->RefCount < 1)
{
MpTrace(COMP_ASSOC, DBG_LOUD, (" - Reject (Deleting)\n"));
return FALSE;
}
// BSS Type
if (pConfig->BSSType != pAPEntry->Dot11BSSType)
{
MpTrace(COMP_ASSOC, DBG_LOUD, (" - Reject (mismatched BSS)\n"));
return FALSE;
}
// Check AP
if (StaMatchAPSSID(pAPEntry, pConfig) == FALSE)
{
return FALSE;
}
// Check PHY type
if (StaMatchPhyId(pStation, pAPEntry->PhyId) == FALSE)
{
return FALSE;
}
// Match data rates
if (StaMatchAPDataRates(pAPEntry, pStation) == FALSE)
{
return FALSE;
}
// Match WEP/WPA/WPA2 capabilities
if (StaMatchAPSecurity(pAPEntry, pStation) == FALSE)
{
return FALSE;
}
MpTrace(COMP_ASSOC, DBG_LOUD, (" - Accepted, RSSI %d\n", pAPEntry->RSSI));
// We can use this AP
return TRUE;
}
NDIS_STATUS
StaRankCandidateAPList(
_In_ PMP_EXTSTA_PORT pStation
)
{
PMP_BSS_ENTRY *APList;
PMP_BSS_ENTRY tmpAP;
ULONG APCount;
ULONG i, j;
APList = pStation->ConnectContext.CandidateAPList;
APCount = pStation->ConnectContext.CandidateAPCount;
if (APCount < 2)
return NDIS_STATUS_SUCCESS;
//
// Order all the candidate APs according to RSSI.
//
for (i = 0; i < APCount - 1; i++)
{
for (j = i + 1; j < APCount; j++)
{
if (APList[i]->LinkQuality < APList[j]->LinkQuality)
{
tmpAP = APList[i];
APList[i] = APList[j];
APList[j] = tmpAP;
}
}
}
return NDIS_STATUS_SUCCESS;
}
NDIS_STATUS
StaGetCandidateAPList(
_In_ PMP_EXTSTA_PORT pStation,
_In_ BOOLEAN bStrictFiltering
)
{
NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS;
MP_RW_LOCK_STATE LockState;
PMP_BSS_ENTRY pAPEntry = NULL;
PLIST_ENTRY pHead = NULL, pEntry = NULL;
PMP_BSS_LIST pDiscoveredAPList = NULL;
ULONGLONG ullOldestAllowedEntry;
//
// Determine the expiry time we will use for determining if we
// we will be picking an access point or not
//
NdisGetCurrentSystemTime((PLARGE_INTEGER)&ullOldestAllowedEntry);
if (bStrictFiltering)
{
//
// We get stricter when determining which APs have expired, only accepting
// APs we have seen in/after the last scan
//
ullOldestAllowedEntry = pStation->ScanContext.LastScanTime;
}
else
{
//
// We will be more relaxed in picking stale APs
//
if (pStation->RegInfo->BSSEntryExpireTime <= ullOldestAllowedEntry)
ullOldestAllowedEntry -= pStation->RegInfo->BSSEntryExpireTime;
}
pStation->ConnectContext.CandidateAPCount = 0;
pDiscoveredAPList = Mp11QueryAndRefBSSList(
STA_GET_MP_PORT(pStation)->Adapter,
STA_GET_MP_PORT(pStation),
&LockState
);
if (pDiscoveredAPList == NULL)
{
MpTrace(COMP_ASSOC, DBG_SERIOUS, ("Unable to obtain BSS list\n"));
return NDIS_STATUS_FAILURE;
}
pHead = &(pDiscoveredAPList->List);
pEntry = pHead->Flink;
//
// Try to find as many access points as we can
//
while((pEntry != pHead) &&
(pStation->ConnectContext.CandidateAPCount < STA_CANDIDATE_AP_MAX_COUNT)
)
{
pAPEntry = CONTAINING_RECORD(pEntry, MP_BSS_ENTRY, Link);
pEntry = pEntry->Flink;
NdisDprAcquireSpinLock(&(pAPEntry->Lock)); // Lock AP entry
//
// Ignore stale entries. We do periodic scanning, so it
// an AP is not reasonably fresh, we dont accept it
//
if (pAPEntry->HostTimestamp < ullOldestAllowedEntry)
{
NdisDprReleaseSpinLock(&(pAPEntry->Lock));
continue;
}
//
// If we have failed/dropped association with this AP too many
// times, we wont roam to it. This mainly helps us ensure
// that we dont keep jumping between APs
//
if ((bStrictFiltering) && (pAPEntry->AssocCost > STA_ASSOC_COST_MAX_DONT_CONNECT))
{
// If the AP we are connected to goes away and all others
// are costly, we could lose connectivity. The roaming caller
// needs to ensure that in such case, StrictFiltering is off
NdisDprReleaseSpinLock(&(pAPEntry->Lock));
continue;
}
//
// Match the AP information with our AP/roaming filter
//
if (StaMatchAPEntry(
pAPEntry,
pStation
) == TRUE)
{
// Add a refcount so that we can ensure that the access point
// does not go away while we rank/use it. Note we still have the
// list lock, so the entry hasnt yet been deleted or modified
if (NdisInterlockedIncrement(&(pAPEntry->RefCount)) > 1)
{
pStation->ConnectContext.CandidateAPList[pStation->ConnectContext.CandidateAPCount] = pAPEntry;
pStation->ConnectContext.CandidateAPCount++;
}
else
{
// This entry maybe going away, dont use it
NdisInterlockedDecrement(&(pAPEntry->RefCount));
}
}
NdisDprReleaseSpinLock(&(pAPEntry->Lock));
}
//
// Now reorder APs in our candidate list to have the most
// preferred AP first
//
ndisStatus = StaRankCandidateAPList(pStation);
Mp11ReleaseBSSListRef(STA_GET_MP_PORT(pStation)->Adapter,
pDiscoveredAPList,
&LockState
);
return ndisStatus;
}
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

