Sample Code

Windows Driver Samples/ Native Wi-Fi Miniport Sample Driver/ C++/ extsta/ st_assoc.c/

/*++

Copyright (c) Microsoft Corporation. All rights reserved.

Module Name:
    st_assoc.c

Abstract:
    STA layer association frame processing functions
    
Revision History:
      When        What
    ----------    ----------------------------------------------
    08-01-2005    Created
    01-15-2006    Renamed sta_assoc.c to st_assoc.c

Notes:

--*/
#include "precomp.h"
#include "st_assoc.h"
#include "st_conn.h"
#include "st_auth.h"
#include "st_adhoc.h"
#include "st_scan.h"
#include "st_send.h"

#if DOT11_TRACE_ENABLED
#include "st_assoc.tmh"
#endif

_Success_(return == NDIS_STATUS_SUCCESS)
NDIS_STATUS
StaCreateAssociateRequestPacket(
    _In_  PMP_EXTSTA_PORT        pStation,
    _In_  PMP_BSS_ENTRY  pAPEntry,
    _Outptr_result_buffer_to_(
        STA11_MAX_IE_BLOB_SIZE + sizeof(DOT11_MGMT_HEADER) + sizeof(DOT11_ASSOC_REQUEST_FRAME),
        *pAssocPacketLength)
	_Result_nullonfailure_
          PUCHAR* ppAssocPacket,
    _Out_ PUSHORT         pAssocPacketLength
    )
{
    PDOT11_MGMT_HEADER  pMgmtMacHeader = NULL;
    PDOT11_ASSOC_REQUEST_FRAME   pDot11AssocRequest = NULL;
    USHORT              IESize;
    NDIS_STATUS         ndisStatus = NDIS_STATUS_SUCCESS;
    PUCHAR              pCurrentIE = NULL;
    DOT11_RATE_SET      RateSet;
    BOOLEAN             set;
    PVNIC               pNic = STA_GET_VNIC(pStation);

    IESize = STA11_MAX_IE_BLOB_SIZE;                    // SSID + Rates IE
    *pAssocPacketLength = sizeof(DOT11_MGMT_HEADER) + 
            sizeof(DOT11_ASSOC_REQUEST_FRAME) +
            IESize;

    MP_ALLOCATE_MEMORY(STA_GET_MP_PORT(pStation)->MiniportAdapterHandle, ppAssocPacket, *pAssocPacketLength, EXTSTA_MEMORY_TAG);
    if (*ppAssocPacket == NULL)
    {
        MpTrace(COMP_ASSOC, DBG_SERIOUS, ("Unable to allocate association request packet\n"));
        return NDIS_STATUS_RESOURCES;
    }
    NdisZeroMemory(*ppAssocPacket, *pAssocPacketLength);    
    
    pMgmtMacHeader = (PDOT11_MGMT_HEADER)*ppAssocPacket;

    //
    // Fill the MAC header
    //
    pMgmtMacHeader->FrameControl.Version = 0x0;
    pMgmtMacHeader->FrameControl.Type = DOT11_FRAME_TYPE_MANAGEMENT;
    pMgmtMacHeader->FrameControl.Subtype = DOT11_MGMT_SUBTYPE_ASSOCIATION_REQUEST;
    pMgmtMacHeader->FrameControl.ToDS = 0x0;      // Default value for Mgmt frames
    pMgmtMacHeader->FrameControl.FromDS = 0x0;    // Default value for Mgmt frames
    pMgmtMacHeader->FrameControl.MoreFrag = 0x0;  
    pMgmtMacHeader->FrameControl.Retry = 0x0;
    pMgmtMacHeader->FrameControl.PwrMgt = 0x0;
    pMgmtMacHeader->FrameControl.MoreData = 0x0;
    pMgmtMacHeader->FrameControl.WEP = 0x0;       // no WEP
    pMgmtMacHeader->FrameControl.Order = 0x0;     // no order

    memcpy(pMgmtMacHeader->DA, 
           pAPEntry->MacAddress,
           DOT11_ADDRESS_SIZE
           );

    memcpy(pMgmtMacHeader->SA, 
           VNic11QueryMACAddress(pNic),
           DOT11_ADDRESS_SIZE
           );

    memcpy(pMgmtMacHeader->BSSID,
           pAPEntry->Dot11BSSID,
           DOT11_ADDRESS_SIZE
           );
           
    pDot11AssocRequest = (PDOT11_ASSOC_REQUEST_FRAME)((*ppAssocPacket) + sizeof(DOT11_MGMT_HEADER));

    do
    {
        //
        // Capabilities
        //
        NdisZeroMemory(&(pDot11AssocRequest->Capability), sizeof(DOT11_CAPABILITY));

        pDot11AssocRequest->Capability.ESS = 1;
        pDot11AssocRequest->Capability.IBSS = 0;
        pDot11AssocRequest->Capability.CFPollable = (VNic11QueryCFPollable(pNic) ? 1 : 0);
        pDot11AssocRequest->Capability.CFPollRequest = 0;

        pDot11AssocRequest->Capability.Privacy = 
            (pStation->Config.UnicastCipherAlgorithm != DOT11_CIPHER_ALGO_NONE) ? 1 : 0;

        switch (VNic11QueryCurrentPhyType(pNic)) 
        {
            case dot11_phy_type_erp:
                set = VNic11QueryShortSlotTimeOptionImplemented(pNic, FALSE);
                if (set)
                {
                    set = VNic11QueryShortSlotTimeOptionEnabled(pNic, FALSE);
                }
                pDot11AssocRequest->Capability.ShortSlotTime = set ? 1 : 0;
                
                set = VNic11QueryDsssOfdmOptionImplemented(pNic, FALSE);
                if (set)
                {
                    set = VNic11QueryDsssOfdmOptionEnabled(pNic, FALSE);
                }
                pDot11AssocRequest->Capability.DSSSOFDM = set ? 1 : 0;

                // fall through

            case dot11_phy_type_hrdsss:
                // Enable/verify short preamble
                set = VNic11QueryShortPreambleOptionImplemented(pNic, FALSE);
                pDot11AssocRequest->Capability.ShortPreamble = set ? 1: 0;

                set = VNic11QueryPbccOptionImplemented(pNic, FALSE);
                pDot11AssocRequest->Capability.PBCC = set ? 1: 0;

                set = VNic11QueryChannelAgilityPresent(pNic, FALSE);
                if (set)
                {
                    set = VNic11QueryChannelAgilityEnabled(pNic, FALSE);
                }
                pDot11AssocRequest->Capability.ChannelAgility = set ? 1 : 0;
        }

        pDot11AssocRequest->usListenInterval = pStation->Config.ListenInterval;

        pCurrentIE = ((PUCHAR)pDot11AssocRequest) + sizeof(DOT11_ASSOC_REQUEST_FRAME);
        
        //
        // Fill the SSID
        //
#pragma warning(suppress: 28020) // PREFast bug comparing IESize and uSSIDLength
        ndisStatus = Dot11AttachElement(
                &pCurrentIE,
                &IESize,
                DOT11_INFO_ELEMENT_ID_SSID,
                (UCHAR)pStation->Config.SSID.uSSIDLength,
                pStation->Config.SSID.ucSSID
                );
        if (ndisStatus != NDIS_STATUS_SUCCESS)
        {
            MpTrace(COMP_ASSOC, DBG_SERIOUS, ("Unable to populate SSID in association request\n"));
            break;
        }

        //
        // Fill the supported rates
        //
        VNic11QueryOperationalRateSet(pNic, &RateSet, FALSE);
        ndisStatus = StaFilterUnsupportedRates(pAPEntry, &RateSet);
        if (ndisStatus != NDIS_STATUS_SUCCESS)
        {
            MpTrace(COMP_ASSOC, DBG_SERIOUS, ("Unable to find compatible operational rates\n"));
            break;
        }

        ndisStatus = Dot11AttachElement(
                &pCurrentIE,
                &IESize,
                DOT11_INFO_ELEMENT_ID_SUPPORTED_RATES,
                (UCHAR)((RateSet.uRateSetLength > 8) ? 8 : RateSet.uRateSetLength),
                RateSet.ucRateSet
                );
        if (ndisStatus != NDIS_STATUS_SUCCESS)
        {
            MpTrace(COMP_ASSOC, DBG_SERIOUS, ("Unable to populate supported rates in association request\n"));
            break;
        }

        //
        // Add the extended rate set if needed
        //
        if (RateSet.uRateSetLength > (UCHAR)8) 
        {
            ndisStatus = Dot11AttachElement(
                &pCurrentIE,
                &IESize,
                DOT11_INFO_ELEMENT_ID_EXTD_SUPPORTED_RATES,
                (UCHAR)(RateSet.uRateSetLength - 8),
                (RateSet.ucRateSet + 8)
                );
            if (ndisStatus != NDIS_STATUS_SUCCESS)
            {
                MpTrace(COMP_ASSOC, DBG_SERIOUS, ("Unable to add extended rates in association request\n"));
                break;
            }
        }

        //
        // RSNA/WPA IE
        //
        if (pStation->Config.AuthAlgorithm == DOT11_AUTH_ALGO_RSNA ||
            pStation->Config.AuthAlgorithm == DOT11_AUTH_ALGO_RSNA_PSK ||
            pStation->Config.AuthAlgorithm == DOT11_AUTH_ALGO_WPA ||
            pStation->Config.AuthAlgorithm == DOT11_AUTH_ALGO_WPA_PSK)
        {
            ndisStatus = StaAttachInfraRSNIE(pStation, 
                                             pAPEntry, 
                                             &pCurrentIE,
                                             &IESize);
            if (ndisStatus != NDIS_STATUS_SUCCESS)
            {
                MpTrace(COMP_ASSOC, DBG_SERIOUS, ("Unable to add RSN IE in association request\n"));
                break;
            }
        }

        //
        // Add the association params IE
        //
        if (pStation->Config.AdditionalIESize > 0)
        {
            // Check that BSSID matches
            if (ETH_IS_BROADCAST(pStation->Config.AssocIEBSSID) ||
                MP_COMPARE_MAC_ADDRESS(pAPEntry->Dot11BSSID, pStation->Config.AssocIEBSSID))
            {
                // BSSID matches, add the IE
                if (pStation->Config.AdditionalIESize < IESize)
                {
                    NdisMoveMemory(pCurrentIE, pStation->Config.AdditionalIEData, pStation->Config.AdditionalIESize);
                    pCurrentIE += pStation->Config.AdditionalIESize;
                    IESize -= (USHORT)pStation->Config.AdditionalIESize;
                }
                else
                {
                    MpTrace(COMP_ASSOC, DBG_SERIOUS, ("Unable to add additional IE in association request\n"));
                    // This is not a fatal error
                }
            }
        }

        //
        // Adjust packet length for IE space we did not use. Eg. if the SSID is not 32 bytes long, 
        // we have empty space here that we dont want send out in the packet. 
        //
        *pAssocPacketLength = *pAssocPacketLength - IESize;
        

    } while (FALSE);

    if (ndisStatus != NDIS_STATUS_SUCCESS)
    {
        MP_FREE_MEMORY(*ppAssocPacket);
        *ppAssocPacket = NULL;
    }
    
    return ndisStatus;
}

VOID
StaFreeAssociateRequestPacket(
    _In_  PMP_EXTSTA_PORT        pStation,
    _In_  PMP_BSS_ENTRY  pAPEntry,
    _In_ PUCHAR          pAssocPacket,
    _In_ USHORT          AssocPacketLength
    )
{
    UNREFERENCED_PARAMETER(pStation);
    UNREFERENCED_PARAMETER(pAPEntry);
    UNREFERENCED_PARAMETER(AssocPacketLength);

    MP_FREE_MEMORY(pAssocPacket);
}

NDIS_STATUS
StaAssociate(
    _In_  PMP_EXTSTA_PORT        pStation,
    _In_  PMP_BSS_ENTRY  pAPEntry,
    _In_  ULONG           AssociateTimeout
    )
{
    NDIS_STATUS         ndisStatus = NDIS_STATUS_SUCCESS;
    PUCHAR              pAssocPacket = NULL;
    USHORT              AssocPacketLength;
    ULONG               StatusCode = 0;
    LARGE_INTEGER       timeoutTime;
    
    MpEntry;

    do
    {
        //
        // Create the associate packet
        //
        ndisStatus = StaCreateAssociateRequestPacket(
            pStation,
            pAPEntry,
            &pAssocPacket,
            &AssocPacketLength
            );
        if (ndisStatus != NDIS_STATUS_SUCCESS)
        {
            //
            // Complete the association process
            //
            StatusCode = ndisStatus;
            NdisAcquireSpinLock(&(pStation->ConnectContext.Lock));
            if (pStation->ConnectContext.AssociateState == ASSOC_STATE_RECEIVED_AUTHENTICATE)
            {
                //
                // Reset ourselves to ready to original state
                //
                pStation->ConnectContext.AssociateState = ASSOC_STATE_STARTED_ASSOCIATION;
            }
            NdisReleaseSpinLock(&(pStation->ConnectContext.Lock));

            break;
        }

        NdisAcquireSpinLock(&(pStation->ConnectContext.Lock));
        if ((pStation->ConnectContext.ConnectState >= CONN_STATE_READY_TO_CONNECT) &&
            (pStation->ConnectContext.AssociateState == ASSOC_STATE_RECEIVED_AUTHENTICATE))
        {
            //
            // Packet will be sent means we will be waiting for packet response
            //
            pStation->ConnectContext.AssociateState = ASSOC_STATE_WAITING_FOR_ASSOCIATE;

            //
            // Save association request packet inside the AP entry. This will be freed
            // on association completion indication
            //
            NdisDprAcquireSpinLock(&(pAPEntry->Lock));
            pAPEntry->pAssocRequest = pAssocPacket;
            pAPEntry->AssocRequestLength = AssocPacketLength;
            NdisDprReleaseSpinLock(&(pAPEntry->Lock));

            NdisReleaseSpinLock(&(pStation->ConnectContext.Lock));
        }
        else
        {
            //
            // Reset, disconnect, deauthenticated by AP after authentication succeeded
            //
            NdisReleaseSpinLock(&(pStation->ConnectContext.Lock));
            MpTrace(COMP_ASSOC, DBG_LOUD, ("Reset/Disconnect/Deauth while starting association\n"));

            //
            // Assoc packet pointer isnt saved, free it
            //
            StaFreeAssociateRequestPacket(
                pStation, 
                pAPEntry, 
                pAssocPacket, 
                AssocPacketLength
                );

            //
            // Abort association 
            //
            StatusCode = (ULONG)STATUS_CANCELLED;

            break;
        }

        //
        // Send the association request packet
        //
        ndisStatus = BasePortSendInternalPacket(
            STA_GET_MP_PORT(pStation),
             pAssocPacket,
            AssocPacketLength
            );
        if (ndisStatus != NDIS_STATUS_SUCCESS)
        {
            MpTrace(COMP_ASSOC, DBG_SERIOUS, ("Unable to send Association Request packet\n"));
            StatusCode = ndisStatus;

            NdisAcquireSpinLock(&(pStation->ConnectContext.Lock));
            if (pStation->ConnectContext.AssociateState == ASSOC_STATE_WAITING_FOR_AUTHENTICATE)
            {
                //
                // Reset ourselves to ready to original state
                //
                pStation->ConnectContext.AssociateState = ASSOC_STATE_STARTED_ASSOCIATION;
            }
            
            NdisReleaseSpinLock(&(pStation->ConnectContext.Lock));
            
            break;
        }
        else
        {
            // Add an extra refcount for the association timer
            STA_INCREMENT_REF(pStation->ConnectContext.AsyncFuncCount);

            //
            // Set the timeout timer for associate failure case. 
            //
            timeoutTime.QuadPart = Int32x32To64((LONG)AssociateTimeout, -10000);
            NdisSetTimerObject(pStation->ConnectContext.Timer_AssociateTimeout, 
                timeoutTime, 
                0, 
                NULL
                );
        }
        
        //
        // We dont let the hardware pend the request since we will be 
        // freeing the packet
        //
        MPASSERT(ndisStatus != NDIS_STATUS_PENDING);
    }while (FALSE);

    //
    // Fail the association if we failed inline, etc
    //
    if (StatusCode != 0)
    {
        StaAssociateComplete(pStation, DOT11_ASSOC_STATUS_SYSTEM_ERROR);
    }
    
    return ndisStatus;
}


VOID
StaAssociateComplete(
    _In_  PMP_EXTSTA_PORT        pStation,
    _In_  ULONG           Status
    )
{
    MpEntry;

    //
    // Complete the Association
    //
    StaCompleteAssociationProcess(pStation, Status);    
}

// Can be called at both passive and dispatch
VOID
StaAssociateTimeoutCallback(
    PMP_EXTSTA_PORT             Station
    )
{
    //
    // Cancel the associate operation if it has not already completed
    //
    NdisAcquireSpinLock(&(Station->ConnectContext.Lock));    
    if (Station->ConnectContext.AssociateState == ASSOC_STATE_WAITING_FOR_ASSOCIATE)
    {
        Station->ConnectContext.AssociateState = ASSOC_STATE_STARTED_ASSOCIATION;
        NdisReleaseSpinLock(&(Station->ConnectContext.Lock));

        MpTrace(COMP_ASSOC, DBG_NORMAL, ("Cancelled the association Operation as it took too long\n"));
        
        StaAssociateComplete(Station, DOT11_ASSOC_STATUS_UNREACHABLE);
    }
    else if (Station->ConnectContext.AssociateState == ASSOC_STATE_REMOTELY_DEAUTHENTICATED)
    {
        //
        // While we were waiting for an associate response packet, we got a deauth
        // We will complete the association attempt with failure
        //
        MpTrace(COMP_ASSOC, DBG_NORMAL, ("Deauthentication while waiting for association response\n"));

        // Put us back to default state
        Station->ConnectContext.AssociateState = ASSOC_STATE_STARTED_ASSOCIATION;
        NdisReleaseSpinLock(&(Station->ConnectContext.Lock));

        //
        // Complete the association process with failure
        //
        StaAssociateComplete(Station, DOT11_ASSOC_STATUS_PEER_DEAUTHENTICATED_START | Station->ConnectContext.DeAuthReason);
    }
    else
    {
        //
        // The association has already completed. dont do anything
        //
        NdisReleaseSpinLock(&(Station->ConnectContext.Lock));
        
        MpTrace(COMP_ASSOC, DBG_LOUD, ("Association completed/cancelled before timeout\n"));
    }

    // Remove the extra refcount added for the timer
    STA_DECREMENT_REF(Station->ConnectContext.AsyncFuncCount);    
}

VOID
StaAssociateTimer(
    PVOID            SystemSpecific1,
    PVOID			 param,
    PVOID            SystemSpecific2,
    PVOID            SystemSpecific3 
    )
{
    PMP_EXTSTA_PORT    pStation = (PMP_EXTSTA_PORT)param;

	UNREFERENCED_PARAMETER(SystemSpecific1);
	UNREFERENCED_PARAMETER(SystemSpecific2);
	UNREFERENCED_PARAMETER(SystemSpecific3);

    StaAssociateTimeoutCallback(pStation);
}

VOID 
StaReceiveAssociationResponse(
    _In_  PMP_EXTSTA_PORT                        pStation,
    _In_  PMP_RX_MPDU                    pFragment,
    _In_  ULONG                           TotalLength
    )
{
    NDIS_STATUS         ndisStatus = NDIS_STATUS_SUCCESS;
    PUCHAR              pPacketBuffer;
    PDOT11_MGMT_HEADER  pMgmtHeader;
    PDOT11_ASSOC_RESPONSE_FRAME   pDot11AssocFrame;
    USHORT              StatusCode;
    USHORT              AID = 0;
    PMP_BSS_ENTRY      pAPEntry = pStation->ConnectContext.ActiveAP;
    BOOLEAN             bTimerCancelled, bSetTxDataRate = FALSE;
    DOT11_RATE_SET      rateSet = {0};

    pPacketBuffer = MP_RX_MPDU_DATA(pFragment);

    //
    // Ref to make sure reset/halt does not leave while we are still working
    //
    STA_INCREMENT_REF(pStation->ConnectContext.AsyncFuncCount);
        
    NdisDprAcquireSpinLock(&(pStation->ConnectContext.Lock));
    if (pStation->ConnectContext.AssociateState == ASSOC_STATE_WAITING_FOR_ASSOCIATE)
    {
        if (pStation->ConnectContext.ConnectState < CONN_STATE_READY_TO_CONNECT)
        {
            MpTrace(COMP_ASSOC, DBG_LOUD, ("Reset/Disconnect before association completed\n"));
            
            //
            // Reset/disconnect, etc. We dont process this associate packet. Eventually, timeout
            // will happen and cleanup
            //
            NdisDprReleaseSpinLock(&(pStation->ConnectContext.Lock));
            STA_DECREMENT_REF(pStation->ConnectContext.AsyncFuncCount);
            return;
        }

        do
        {
            if (TotalLength < (sizeof(DOT11_MGMT_HEADER) + sizeof(DOT11_ASSOC_RESPONSE_FRAME)))
            {
                ndisStatus = NDIS_STATUS_NOT_ACCEPTED;
                MpTrace(COMP_ASSOC, DBG_LOUD, ("Association response packet too short\n"));
                break;
            }

            pMgmtHeader = (PDOT11_MGMT_HEADER)pPacketBuffer;

            //
            // Check that is a packet from the AP we are interested in
            //
            if (!MP_COMPARE_MAC_ADDRESS(pMgmtHeader->SA, pAPEntry->MacAddress) ||
                !MP_COMPARE_MAC_ADDRESS(pMgmtHeader->BSSID, pAPEntry->Dot11BSSID) ||
                !MP_COMPARE_MAC_ADDRESS(pMgmtHeader->DA, VNic11QueryMACAddress(STA_GET_VNIC(pStation))))
            {
                ndisStatus = NDIS_STATUS_NOT_ACCEPTED;
                MpTrace(COMP_ASSOC, DBG_LOUD, ("Association response packet not for me\n"));
                break;
            }

        } while (FALSE);

        if (ndisStatus != NDIS_STATUS_NOT_ACCEPTED)
        {
            //
            // This was a valid response to our association request
            // Complete the association with appropriate status
            //        
            pDot11AssocFrame = (PDOT11_ASSOC_RESPONSE_FRAME)(pPacketBuffer + sizeof(DOT11_MGMT_HEADER));

            //
            // Get association status code from the packet
            //
            StatusCode = pDot11AssocFrame->usStatusCode;

            if (StatusCode == DOT11_FRAME_STATUS_SUCCESSFUL)
            {
                //
                // Association attempt succeeded
                //
                ndisStatus = NDIS_STATUS_SUCCESS;
                MpTrace(COMP_ASSOC, DBG_NORMAL, ("Association response status SUCCESS\n"));

                //
                // Validate AID
                //
                AID = pDot11AssocFrame->usAID;
                if ((AID & 0xc000) != 0xc000) 
                {
                    // Invalid AID. 
                    pStation->Config.ValidAID = FALSE;
                    
                    // Connecting to a non-conformant AP. Continue with association instead of bailing out.
                    MpTrace(COMP_ASSOC, DBG_SERIOUS, ("Association response contains invalid AID %d\n", AID));
                }
                else
                {
                    pStation->Config.ValidAID = TRUE;
                }

                AID  &= ~(0xc000);
                if (AID > 2007) 
                {
                    // AID too big
                    ndisStatus = NDIS_STATUS_FAILURE;
                    MpTrace(COMP_ASSOC, DBG_SERIOUS, ("Association response contains invalid AID %d\n", AID));
                }

                if (ndisStatus == NDIS_STATUS_SUCCESS)
                {
                    //
                    // Get data rate
                    //
                    ndisStatus = Dot11GetRateSetFromInfoEle(
                                    Add2Ptr(pDot11AssocFrame, sizeof(DOT11_ASSOC_RESPONSE_FRAME)),
                                    TotalLength - sizeof(DOT11_MGMT_HEADER) - sizeof(DOT11_ASSOC_RESPONSE_FRAME),
                                    FALSE,
                                    &rateSet);
                }
                
                if (ndisStatus == NDIS_STATUS_SUCCESS)
                {
                    // Association has succeeded
                    MpTrace(COMP_ASSOC, DBG_NORMAL, ("Association ID %d\n", AID));
                    
                    pStation->ConnectContext.AssociateState = ASSOC_STATE_RECEIVED_ASSOCIATE;                
                    pStation->Config.AID = AID;

                    //
                    // Clear non-static WEP keys.
                    //
                    VNic11DeleteNonPersistentKey(STA_GET_VNIC(pStation));

                    bSetTxDataRate = TRUE;

                    //
                    // Set active PhyId
                    //
                    pStation->Config.ActivePhyId = pAPEntry->PhyId;

                    //
                    // Set multicast cipher algorithm if the exact algorithm was not selected.
                    //
                    if (pStation->Config.MulticastCipherAlgorithmCount > 1)
                    {
                        VNic11SetCipher(STA_GET_VNIC(pStation), FALSE, pStation->Config.MulticastCipherAlgorithm);
                    }
                }
                else
                {
                    pStation->ConnectContext.AssociateState = ASSOC_STATE_STARTED_ASSOCIATION;
                    StatusCode = DOT11_FRAME_STATUS_FAILURE;         // Unspecified failure
                }
            }
            else
            {
                // The association attempt failed
                MpTrace(COMP_ASSOC, DBG_SERIOUS, ("Association failed by the access point with status %d\n", StatusCode));
                pStation->ConnectContext.AssociateState = ASSOC_STATE_STARTED_ASSOCIATION;
            }

            //
            // Copy the association response buffer into the AP Entry for completion indication
            //
            NdisDprAcquireSpinLock(&(pAPEntry->Lock));
            pAPEntry->AssocResponseLength = (USHORT)TotalLength;
            MP_ALLOCATE_MEMORY(STA_GET_MP_PORT(pStation)->MiniportAdapterHandle, &(pAPEntry->pAssocResponse), TotalLength, EXTSTA_MEMORY_TAG);
            if (pAPEntry->pAssocResponse == NULL)
            {
                MpTrace(COMP_ASSOC, DBG_SERIOUS, ("Unable to save association request packet\n"));
                //
                // We still maintain the association
                //
                pAPEntry->AssocResponseLength = 0;
            }
            else
            {
                //
                // Copy the association response into the packet
                //
                NdisMoveMemory(pAPEntry->pAssocResponse, pPacketBuffer, TotalLength);
            }

            // Save association ID, time, etc
            pAPEntry->AssocID = AID;
            NdisGetCurrentSystemTime(&(pAPEntry->AssociationUpTime));
            pAPEntry->AssocState = dot11_assoc_state_auth_assoc;
            NdisDprReleaseSpinLock(&(pAPEntry->Lock));

            NdisDprReleaseSpinLock(&(pStation->ConnectContext.Lock));

            if (bSetTxDataRate == TRUE)
            {
                //
                // Set data TX rate
                // TODO: This should be based per peer
                //
                VNic11SelectTXDataRate(STA_GET_VNIC(pStation), 
                    &rateSet, 
                    pFragment->Msdu->LinkQuality
                    );
            }

            //
            // Attempt to cancel the timer
            //
            bTimerCancelled = NdisCancelTimerObject(pStation->ConnectContext.Timer_AssociateTimeout);
            if (bTimerCancelled)
            {
                STA_DECREMENT_REF(pStation->ConnectContext.AsyncFuncCount);
            }

            if (StatusCode) 
            {
                StaAssociateComplete(pStation, StatusCode | DOT11_ASSOC_STATUS_ASSOCIATION_RESPONSE_START);
            } 
            else 
            {
                StaAssociateComplete(pStation, DOT11_ASSOC_STATUS_SUCCESS);
            }
        }
        else
        {
            NdisDprReleaseSpinLock(&(pStation->ConnectContext.Lock));
        }        
    }
    else
    {
        NdisDprReleaseSpinLock(&(pStation->ConnectContext.Lock));
        MpTrace(COMP_ASSOC, DBG_LOUD, ("Association already timed out\n"));
    }

    STA_DECREMENT_REF(pStation->ConnectContext.AsyncFuncCount);    
}


VOID 
StaReceiveDisassociation(
    _In_  PMP_EXTSTA_PORT                        pStation,
    _In_  PMP_RX_MPDU                    pFragment,
    _In_  ULONG                           TotalLength
    )
{
    NDIS_STATUS         ndisStatus = NDIS_STATUS_SUCCESS;
    PUCHAR              pPacketBuffer;
    PDOT11_DISASSOC_FRAME   pDot11DisassocFrame = NULL;
    PDOT11_MGMT_HEADER  pMgmtHeader;
    PMP_BSS_ENTRY      pAPEntry;
    BOOLEAN             bDisassociate = TRUE;
    
    if (TotalLength < (sizeof(DOT11_MGMT_HEADER) + sizeof(DOT11_DISASSOC_FRAME)))
    {
        MpTrace(COMP_ASSOC, DBG_LOUD, ("Disassociation packet too short\n"));
        return ;
    }

    pPacketBuffer = MP_RX_MPDU_DATA(pFragment);

    //
    // Ref to make sure reset/halt does not leave while we are still working
    //
    STA_INCREMENT_REF(pStation->ConnectContext.AsyncFuncCount);

    //
    // Proceed only if we have received association packet
    //
    NdisDprAcquireSpinLock(&(pStation->ConnectContext.Lock));
    if ((pStation->ConnectContext.ConnectState >= CONN_STATE_READY_TO_CONNECT) &&
        (pStation->ConnectContext.AssociateState > ASSOC_STATE_WAITING_FOR_ASSOCIATE))
    {        
        pAPEntry = pStation->ConnectContext.ActiveAP;
        pMgmtHeader = (PDOT11_MGMT_HEADER)pPacketBuffer;
        
        //
        // Check that is a packet from the AP we are interested in
        //
        if (!MP_COMPARE_MAC_ADDRESS(pMgmtHeader->SA, pAPEntry->MacAddress) ||
            !MP_COMPARE_MAC_ADDRESS(pMgmtHeader->BSSID, pAPEntry->Dot11BSSID) ||
            !MP_COMPARE_MAC_ADDRESS(pMgmtHeader->DA, VNic11QueryMACAddress(STA_GET_VNIC(pStation))))
        {
            NdisDprReleaseSpinLock(&(pStation->ConnectContext.Lock));
            MpTrace(COMP_ASSOC, DBG_LOUD, ("Disassociate packet not for me\n"));
            ndisStatus = NDIS_STATUS_NOT_ACCEPTED;
        }
        else
        {
            pDot11DisassocFrame = (PDOT11_DISASSOC_FRAME)(pPacketBuffer + sizeof(DOT11_MGMT_HEADER));

            MpTrace(COMP_ASSOC, DBG_NORMAL, ("Received disassociation, reason %d - ", pDot11DisassocFrame->usReasonCode));

            //
            // On a Disassociated, we would just send the deauthenticate
            // packet to the access point & move ourselves to ready state
            //
            if (pStation->ConnectContext.AssociateState == ASSOC_STATE_ASSOCIATED)
            {
                MpTrace(COMP_ASSOC, DBG_LOUD, ("After we were associated\n"));        
            
                //
                // We were associated, cleanup state and indicate disassociation
                //
                pStation->ConnectContext.ActiveAP = NULL;
                bDisassociate = TRUE;
                pAPEntry->AssocState = dot11_assoc_state_unauth_unassoc;    // Moving to unauthenticated
                pStation->ConnectContext.AssociateState = ASSOC_STATE_NOT_ASSOCIATED;                
            }
            else
            {
                MpTrace(COMP_ASSOC, DBG_LOUD, ("While we were associating\n"));        
            
                //
                // Receive disassociate while still attempting to associate. Set associate failed
                // so appropriate routine knows to fail the association attempt
                //
                pStation->ConnectContext.AssociateState = ASSOC_STATE_REMOTELY_DISASSOCIATED;
                bDisassociate = FALSE;
            }
            
            NdisDprReleaseSpinLock(&(pStation->ConnectContext.Lock));

            if (bDisassociate)
            {
                //
                // Send deauthenticate (ignoring status)
                //
                StaSendDeauthentication(
                    pStation, 
                    pAPEntry, 
                    DOT11_MGMT_REASON_UPSPEC_REASON
                    );
                
                // Indicate disassociation
                StaIndicateDisassociation(
                    pStation, 
                    pAPEntry, 
                    DOT11_ASSOC_STATUS_PEER_DISASSOCIATED_START | pDot11DisassocFrame->usReasonCode
                    );

                NdisDprAcquireSpinLock(&(pStation->ConnectContext.Lock));
                if (pStation->ConnectContext.ConnectState >= CONN_STATE_READY_TO_CONNECT)
                {
                    //
                    // We got disassociated, but we can still connect. We will
                    // queue the periodic scan routine to attempt association
                    //
                    StaForceInternalScan(pStation, TRUE);
                }
                NdisDprReleaseSpinLock(&(pStation->ConnectContext.Lock));
            }
            
            //
            // Raise cost and remove connection ref from the AP entry and raise cost
            //
            pAPEntry->AssocCost += STA_ASSOC_COST_REMOTE_DISCONNECT;
            STA_DECREMENT_REF(pAPEntry->RefCount);
        }
    }
    else
    {
        NdisDprReleaseSpinLock(&(pStation->ConnectContext.Lock));
    }
    
    // Done
    STA_DECREMENT_REF(pStation->ConnectContext.AsyncFuncCount);    
}

NDIS_STATUS
StaSendDisassociation(
    _In_  PMP_EXTSTA_PORT        pStation,
    _In_  PMP_BSS_ENTRY  pAPEntry,
    _In_  USHORT          ReasonCode
    )
{
    PDOT11_MGMT_HEADER  pMgmtMacHeader = NULL;
    PDOT11_DISASSOC_FRAME   pDot11DisassocFrame = NULL;
    NDIS_STATUS         ndisStatus = NDIS_STATUS_SUCCESS;
    PUCHAR              pPacketBuffer = NULL;
    USHORT              PacketSize;

    MpEntry;
    
    PacketSize = sizeof(DOT11_MGMT_HEADER) + sizeof(DOT11_DISASSOC_FRAME);

    MP_ALLOCATE_MEMORY(STA_GET_MP_PORT(pStation)->MiniportAdapterHandle, &pPacketBuffer, PacketSize, EXTSTA_MEMORY_TAG);
    if (pPacketBuffer == NULL)
    {
        MpTrace(COMP_ASSOC, DBG_SERIOUS, ("Unable to allocate disassociation request packet\n"));
        return NDIS_STATUS_RESOURCES;
    }
    NdisZeroMemory(pPacketBuffer, PacketSize);        

    pMgmtMacHeader = (PDOT11_MGMT_HEADER)pPacketBuffer;
    
    //
    // Fill the MAC header
    //
    pMgmtMacHeader->FrameControl.Version = 0x0;
    pMgmtMacHeader->FrameControl.Type = DOT11_FRAME_TYPE_MANAGEMENT;
    pMgmtMacHeader->FrameControl.Subtype = DOT11_MGMT_SUBTYPE_DISASSOCIATION;
    pMgmtMacHeader->FrameControl.ToDS = 0x0;      // Default value for Mgmt frames
    pMgmtMacHeader->FrameControl.FromDS = 0x0;    // Default value for Mgmt frames
    pMgmtMacHeader->FrameControl.MoreFrag = 0x0;  
    pMgmtMacHeader->FrameControl.Retry = 0x0;
    pMgmtMacHeader->FrameControl.PwrMgt = 0x0;
    pMgmtMacHeader->FrameControl.MoreData = 0x0;
    pMgmtMacHeader->FrameControl.WEP = 0x0;       // no WEP
    pMgmtMacHeader->FrameControl.Order = 0x0;     // no order

    memcpy(pMgmtMacHeader->DA, 
           pAPEntry->MacAddress,
           DOT11_ADDRESS_SIZE
           );

    memcpy(pMgmtMacHeader->SA, 
           VNic11QueryMACAddress(STA_GET_VNIC(pStation)),
           DOT11_ADDRESS_SIZE
           );

    memcpy(pMgmtMacHeader->BSSID,
           pAPEntry->Dot11BSSID,
           DOT11_ADDRESS_SIZE
           );

    pDot11DisassocFrame = (PDOT11_DISASSOC_FRAME)(pPacketBuffer + sizeof(DOT11_MGMT_HEADER));
    pDot11DisassocFrame->usReasonCode = ReasonCode;

    //
    // Send the disassociation request packet
    ndisStatus = BasePortSendInternalPacket(
        STA_GET_MP_PORT(pStation),
         pPacketBuffer,
        PacketSize
        );
    if (ndisStatus != NDIS_STATUS_SUCCESS)
    {
        MpTrace(COMP_ASSOC, DBG_SERIOUS, ("Unable to send disassociation packet\n"));
    }

    // Send Mgmt Packet cannot pend
    MPASSERT(ndisStatus != NDIS_STATUS_PENDING);
    
    if (pPacketBuffer)
    {
        MP_FREE_MEMORY(pPacketBuffer);
    }

    return ndisStatus;
}

Our Services

  • What our customers say about us?

© 2011-2024 All Rights Reserved. Joya Systems. 4425 South Mopac Building II Suite 101 Austin, TX 78735 Tel: 800-DEV-KERNEL

Privacy Policy. Terms of use. Valid XHTML & CSS