/**************************************************************************** * Ralink Tech Inc. * 4F, No. 2 Technology 5th Rd. * Science-based Industrial Park * Hsin-chu, Taiwan, R.O.C. * (c) Copyright 2002, Ralink Technology, Inc. * * All rights reserved. Ralink's source code is an unpublished work and the * use of a copyright notice does not imply otherwise. This source code * contains confidential trade secret material of Ralink Tech. Any attemp * or participation in deciphering, decoding, reverse engineering or in any * way altering the source code is stricitly prohibited, unless the prior * written consent of Ralink Technology, Inc. is obtained. **************************************************************************** Module Name: ft.c Abstract: Revision History: Who When What -------- ---------- ---------------------------------------------- Fonchi Wu 12-19-2008 */ #ifdef DOT11R_FT_SUPPORT #include "rt_config.h" #include "ft.h" #ifdef CONFIG_AP_SUPPORT static VOID FT_RrbEnqueue( IN PRTMP_ADAPTER pAd, IN PUCHAR pDA, IN PFT_ACTION pFtAction, IN UINT16 FtActLen, IN UINT32 ApIdx); static VOID FT_ReqActionParse( IN PRTMP_ADAPTER pAd, IN UINT16 Len, IN PUCHAR Ptr, OUT PFT_INFO pFtInfo); VOID FT_ConstructGTKSubIe( IN PRTMP_ADAPTER pAd, IN PMAC_TABLE_ENTRY pEntry, OUT PFT_FTIE_INFO pFtInfo); UINT16 FT_AuthReqRsnValidation( IN PRTMP_ADAPTER pAd, IN PMAC_TABLE_ENTRY pEntry, IN PFT_CFG pFtCfg, IN PFT_INFO pFtInfo_in, OUT PFT_INFO pFtInfo_out); UINT16 FT_AuthConfirmRsnValidation( IN PRTMP_ADAPTER pAd, IN PMAC_TABLE_ENTRY pEntry, IN PFT_INFO pFtInfo_in); UINT16 FT_AssocReqRsnValidation( IN PRTMP_ADAPTER pAd, IN PMAC_TABLE_ENTRY pEntry, IN PFT_INFO pFtInfo_in, OUT PFT_INFO pFtInfo_out); /* ======================================================================== Routine Description: Arguments: Return Value: Note: ======================================================================== */ VOID FT_EnqueueAuthReply( IN PRTMP_ADAPTER pAd, IN PHEADER_802_11 pHdr, IN USHORT Alg, IN USHORT Seq, IN USHORT StatusCode, IN PFT_MDIE_INFO pMdIeInfo, IN PFT_FTIE_INFO pFtIeInfo, IN PFT_RIC_INFO pRicInfo, IN PUCHAR pRsnIe, IN UCHAR RsnIeLen) { HEADER_802_11 AuthHdr; ULONG FrameLen = 0; PUCHAR pOutBuffer = NULL; NDIS_STATUS NStatus; PUINT8 ftie_ptr = NULL; UINT8 ftie_len = 0; PUINT8 mdie_ptr = NULL; UINT8 mdie_len = 0; NStatus = MlmeAllocateMemory(pAd, &pOutBuffer); if (NStatus != NDIS_STATUS_SUCCESS) return; DBGPRINT(RT_DEBUG_TRACE, ("AUTH_RSP - Send FT-AUTH response (%d)...\n", StatusCode)); MgtMacHeaderInit(pAd, &AuthHdr, SUBTYPE_AUTH, 0, pHdr->Addr2, pHdr->Addr1, pHdr->Addr1); MakeOutgoingFrame(pOutBuffer, &FrameLen, sizeof(HEADER_802_11), &AuthHdr, 2, &Alg, 2, &Seq, 2, &StatusCode, END_OF_ARGS); /* Insert MDIE. */ if((pMdIeInfo != NULL) && (pMdIeInfo->Len > 0)) { mdie_ptr = pOutBuffer + FrameLen; mdie_len = 5; FT_InsertMdIE(pAd, pOutBuffer + FrameLen, &FrameLen, pMdIeInfo->MdId, pMdIeInfo->FtCapPlc); } /* Insert FTIE. */ if ((pFtIeInfo != NULL) && (pFtIeInfo->Len != 0)) { ftie_ptr = pOutBuffer+FrameLen; ftie_len = (2 + pFtIeInfo->Len); FT_InsertFTIE(pAd, pOutBuffer+FrameLen, &FrameLen, pFtIeInfo->Len, pFtIeInfo->MICCtr, pFtIeInfo->MIC, pFtIeInfo->ANonce, pFtIeInfo->SNonce); /* Insert R1KH IE into FTIE. */ if (pFtIeInfo->R1khIdLen!= 0) FT_FTIE_InsertKhIdSubIE(pAd, pOutBuffer+FrameLen, &FrameLen, FT_R1KH_ID, pFtIeInfo->R1khId, pFtIeInfo->R1khIdLen); /* Insert R0KH IE into FTIE. */ if (pFtIeInfo->R0khIdLen!= 0) FT_FTIE_InsertKhIdSubIE(pAd, pOutBuffer+FrameLen, &FrameLen, FT_R0KH_ID, pFtIeInfo->R0khId, pFtIeInfo->R0khIdLen); } /* Insert RSNIE. */ if ((RsnIeLen != 0) && (pRsnIe != NULL)) { ULONG TmpLen; MakeOutgoingFrame(pOutBuffer+FrameLen, &TmpLen, RsnIeLen, pRsnIe, END_OF_ARGS); FrameLen += TmpLen; } /* Insert RIC. */ if ((pRicInfo != NULL) && (pRicInfo->Len != 0)) { ULONG TmpLen; MakeOutgoingFrame(pOutBuffer+FrameLen, &TmpLen, pRicInfo->Len, pRicInfo->pRicInfo, END_OF_ARGS); FrameLen += TmpLen; } /* Calculate MIC in authentication-ACK frame */ if (pFtIeInfo->MICCtr.field.IECnt) { PMAC_TABLE_ENTRY pEntry; if ((pEntry = MacTableLookup(pAd, pHdr->Addr2)) != NULL) { UINT8 ft_mic[16]; PFT_FTIE pFtIe; FT_CalculateMIC(pHdr->Addr2, pHdr->Addr1, pEntry->PTK, 4, pRsnIe, RsnIeLen, mdie_ptr, mdie_len, ftie_ptr, ftie_len, pRicInfo->pRicInfo, pRicInfo->Len, ft_mic); /* Update the MIC field of FTIE */ pFtIe = (PFT_FTIE)(ftie_ptr + 2); NdisMoveMemory(pFtIe->MIC, ft_mic, FT_MIC_LEN); } } MiniportMMRequest(pAd, 0, pOutBuffer, FrameLen); MlmeFreeMemory(pAd, pOutBuffer); } /* ======================================================================== Routine Description: Arguments: Return Value: Note: ======================================================================== */ static VOID FT_ReqActionParse( IN PRTMP_ADAPTER pAd, IN UINT16 Len, IN PUCHAR Ptr, OUT PFT_INFO pFtInfo) { PEID_STRUCT eid_ptr; UCHAR WPA1_OUI[4]={0x00,0x50,0xF2,0x01}; UCHAR WPA2_OUI[3]={0x00,0x0F,0xAC}; eid_ptr = (PEID_STRUCT) Ptr; NdisZeroMemory(pFtInfo, sizeof(FT_INFO)); /* get variable fields from payload and advance the pointer */ while(((UCHAR*)eid_ptr + eid_ptr->Len + 1) < ((PUCHAR)Ptr + Len)) { switch(eid_ptr->Eid) { case IE_FT_MDIE: FT_FillMdIeInfo(eid_ptr, &pFtInfo->MdIeInfo); break; case IE_FT_FTIE: FT_FillFtIeInfo(eid_ptr, &pFtInfo->FtIeInfo); break; case IE_FT_RIC_DATA: /* record the pointer of first RDIE. */ if (pFtInfo->RicInfo.pRicInfo == NULL) { pFtInfo->RicInfo.pRicInfo = &eid_ptr->Eid; pFtInfo->RicInfo.Len = ((UCHAR*)Ptr + Len) - (UCHAR*)eid_ptr + 1; } case IE_FT_RIC_DESCRIPTOR: if ((pFtInfo->RicInfo.RicIEsLen + eid_ptr->Len + 2) < MAX_RICIES_LEN) { NdisMoveMemory(&pFtInfo->RicInfo.RicIEs[pFtInfo->RicInfo.RicIEsLen], &eid_ptr->Eid, eid_ptr->Len + 2); pFtInfo->RicInfo.RicIEsLen += eid_ptr->Len + 2; } break; case IE_RSN: case IE_WPA: #ifdef WMM_ACM_SUPPORT if (ACM_WME_ELM_Check((UCHAR *)eid_ptr, ACM_WME_OUI_SUBTYPE_TSPEC) == ACM_RTN_OK) { if ((pFtInfo->RicInfo.RicIEsLen + eid_ptr->Len + 2) < MAX_RICIES_LEN) { NdisMoveMemory(&pFtInfo->RicInfo.RicIEs[pFtInfo->RicInfo.RicIEsLen], &eid_ptr->Eid, eid_ptr->Len + 2); pFtInfo->RicInfo.RicIEsLen += eid_ptr->Len + 2; } } #endif /* WMM_ACM_SUPPORT */ if (NdisEqualMemory(eid_ptr->Octet, WPA1_OUI, sizeof(WPA1_OUI)) || NdisEqualMemory(&eid_ptr->Octet[2], WPA2_OUI, sizeof(WPA2_OUI))) { NdisMoveMemory(pFtInfo->RSN_IE, eid_ptr, eid_ptr->Len + 2); pFtInfo->RSNIE_Len = eid_ptr->Len + 2; } break; default: break; } eid_ptr = (PEID_STRUCT)((UCHAR*)eid_ptr + 2 + eid_ptr->Len); } } /* ======================================================================== Routine Description: Arguments: Return Value: Note: ======================================================================== */ VOID FT_MakeFtActFrame( IN PRTMP_ADAPTER pAd, OUT PUCHAR pFrameBuf, OUT PULONG pFrameLen, IN UINT8 ActType, IN PUCHAR pStaMac, IN PUCHAR pTargetApMac, IN UINT16 StatusCode, IN PFT_INFO pFtInfo) { ULONG TmpLen = 0; UCHAR Category = FT_CATEGORY_BSS_TRANSITION; if (pFrameBuf == NULL) return; /* Build basic frame first */ MakeOutgoingFrame((pFrameBuf + *pFrameLen), &TmpLen, 1, &Category, 1, &ActType, 6, pStaMac, 6, pTargetApMac, END_OF_ARGS); *pFrameLen += TmpLen; if ((ActType == FT_ACTION_BT_RSP) || (ActType == FT_ACTION_BT_ACK)) { UINT16 StatusCodeBuf; TmpLen = 0; StatusCodeBuf = cpu2le16(StatusCode); MakeOutgoingFrame((pFrameBuf + *pFrameLen), &TmpLen, 2, &StatusCodeBuf, END_OF_ARGS); *pFrameLen += TmpLen; } /* Insert MD IE into packet. */ if (pFtInfo->MdIeInfo.Len != 0) FT_InsertMdIE(pAd, (pFrameBuf + *pFrameLen), pFrameLen, pFtInfo->MdIeInfo.MdId, pFtInfo->MdIeInfo.FtCapPlc); /* Insert FT IE into packet. */ if (pFtInfo->FtIeInfo.Len != 0) FT_InsertFTIE(pAd, (pFrameBuf + *pFrameLen), pFrameLen, pFtInfo->FtIeInfo.Len, pFtInfo->FtIeInfo.MICCtr, pFtInfo->FtIeInfo.MIC, pFtInfo->FtIeInfo.ANonce, pFtInfo->FtIeInfo.SNonce); if (pFtInfo->FtIeInfo.R0khIdLen!= 0) FT_FTIE_InsertKhIdSubIE(pAd, (pFrameBuf + *pFrameLen), pFrameLen, FT_R0KH_ID, pFtInfo->FtIeInfo.R0khId, pFtInfo->FtIeInfo.R0khIdLen); if (pFtInfo->FtIeInfo.R1khIdLen!= 0) FT_FTIE_InsertKhIdSubIE(pAd, (pFrameBuf + *pFrameLen), pFrameLen, FT_R1KH_ID, pFtInfo->FtIeInfo.R1khId, pFtInfo->FtIeInfo.R1khIdLen); if (pFtInfo->FtIeInfo.GtkLen!= 0) FT_FTIE_InsertGTKSubIE(pAd, (pFrameBuf + *pFrameLen), pFrameLen, pFtInfo->FtIeInfo.GtkSubIE, pFtInfo->FtIeInfo.GtkLen); /* Insert Ric IE into packet .*/ if ((ActType == FT_ACTION_BT_CONFIRM) || (ActType == FT_ACTION_BT_ACK)) { TmpLen = 0; MakeOutgoingFrame((pFrameBuf + *pFrameLen), &TmpLen, pFtInfo->RicInfo.Len, (PUCHAR)pFtInfo->RicInfo.pRicInfo, END_OF_ARGS); *pFrameLen += TmpLen; } return; } /* ======================================================================== Routine Description: Arguments: Return Value: Note: ======================================================================== */ VOID FT_CfgInitial( IN PRTMP_ADAPTER pAd) { INT apidx; PFT_CFG pFtCfg; RTMP_STRING R0khIdBuf[50]; NdisZeroMemory(R0khIdBuf, 50); for (apidx = 0; apidx < MAX_MBSSID_NUM(pAd); apidx++) { pFtCfg = &pAd->ApCfg.MBSSID[apidx].FtCfg; pFtCfg->FtCapFlag.Dot11rFtEnable = TRUE; pFtCfg->FtCapFlag.FtOverDs = TRUE; pFtCfg->FtCapFlag.RsrReqCap = TRUE; FT_SET_MDID(pFtCfg->FtMdId, FT_DEFAULT_MDID); snprintf(R0khIdBuf, sizeof(R0khIdBuf), "Ralink:%02x:%02x:%02x:%02x:%02x:%02x", RandomByte(pAd), RandomByte(pAd), RandomByte(pAd), RandomByte(pAd), RandomByte(pAd), RandomByte(pAd)); NdisZeroMemory(pFtCfg->FtR0khId, sizeof(pFtCfg->FtR0khId)); NdisMoveMemory(pFtCfg->FtR0khId, R0khIdBuf, strlen(R0khIdBuf)); pFtCfg->FtR0khIdLen = strlen(R0khIdBuf); } } VOID FT_Init( IN PRTMP_ADAPTER pAd) { FT_KDP_Init(pAd); FT_RIC_Init(pAd); FT_R1khEntryTabInit(pAd); } VOID FT_Release( IN PRTMP_ADAPTER pAd) { FT_KDP_Release(pAd); FT_RIC_Release(pAd); FT_R1khEntryTabDestroy(pAd); } /* ======================================================================== Routine Description: Arguments: Return Value: Note: ======================================================================== */ USHORT FT_AuthReqHandler( IN PRTMP_ADAPTER pAd, IN PMAC_TABLE_ENTRY pEntry, IN PFT_INFO pFtInfo, OUT PFT_INFO pFtInfoBuf) { USHORT result = MLME_SUCCESS; UCHAR ApIdx = pEntry->func_tb_idx; PFT_CFG pFtCfg; FT_CAP_AND_POLICY FtCapPlc; DBGPRINT(RT_DEBUG_TRACE, ("---> %s \n", __FUNCTION__)); if (ApIdx >= pAd->ApCfg.BssidNum) { DBGPRINT(RT_DEBUG_ERROR, ("%s: invalid apidx (%d)\n", \ __FUNCTION__, ApIdx)); return MLME_UNSPECIFY_FAIL; } pFtCfg = &pAd->ApCfg.MBSSID[ApIdx].FtCfg; NdisZeroMemory(pFtInfoBuf, sizeof(FT_INFO)); do { if ((pFtInfo->MdIeInfo.Len == 0) || (!FT_MDID_EQU(pFtInfo->MdIeInfo.MdId, pFtCfg->FtMdId))) { DBGPRINT_ERR(("%s : The MDIE is invalid\n", __FUNCTION__)); result = FT_STATUS_CODE_INVALID_MDIE; break; } /* FT auth-req in an RSN */ if (pFtInfo->RSNIE_Len != 0) { /* Sanity check */ result = FT_AuthReqRsnValidation(pAd, pEntry, pFtCfg, pFtInfo, pFtInfoBuf); if (result != MLME_SUCCESS) break; } else { /* FT auth-req with no RSN Ie (OPEN mode). reply auth-rsp with success. */ ; } NdisMoveMemory(&pEntry->MdIeInfo, &pFtInfo->MdIeInfo, pFtInfo->MdIeInfo.Len); /* prepare Ft IEs for association response. */ FT_SET_MDID(pFtInfoBuf->MdIeInfo.MdId, pFtCfg->FtMdId); FtCapPlc.field.FtOverDs = pFtCfg->FtCapFlag.FtOverDs; FtCapPlc.field.RsrReqCap = pFtCfg->FtCapFlag.RsrReqCap; pFtInfoBuf->MdIeInfo.FtCapPlc.word = pFtInfo->MdIeInfo.FtCapPlc.word & FtCapPlc.word; pFtInfoBuf->MdIeInfo.Len = 3; result = MLME_SUCCESS; break; } while(0); DBGPRINT(RT_DEBUG_TRACE, ("<--- %s done. StatusCode(%d)\n", __FUNCTION__, result)); return result; } /* ======================================================================== Routine Description: Arguments: Return Value: Note: ======================================================================== */ USHORT FT_AuthConfirmHandler( IN PRTMP_ADAPTER pAd, IN PMAC_TABLE_ENTRY pEntry, IN PFT_INFO pFtInfo, OUT PFT_INFO pFtInfoBuf) { USHORT result = MLME_SUCCESS; UCHAR ApIdx = pEntry->func_tb_idx; PFT_CFG pFtCfg; FT_CAP_AND_POLICY FtCapPlc; DBGPRINT(RT_DEBUG_TRACE, ("%s:\n", __FUNCTION__)); if (ApIdx >= pAd->ApCfg.BssidNum) { DBGPRINT(RT_DEBUG_ERROR, ("%s: invalid apidx (%d)\n", __FUNCTION__, ApIdx)); return MLME_UNSPECIFY_FAIL; } pFtCfg = &pAd->ApCfg.MBSSID[ApIdx].FtCfg; NdisZeroMemory(pFtInfoBuf, sizeof(FT_INFO)); do { if ((pFtInfo->MdIeInfo.Len == 0) || (!FT_MDID_EQU(pFtInfo->MdIeInfo.MdId, pFtCfg->FtMdId))) { /* invalid MDID. reject it. */ result = FT_STATUS_CODE_INVALID_MDIE; break; } if (pFtInfo->RSNIE_Len != 0) { UINT16 result; UINT8 rsnie_len = 0; PUINT8 rsnie_ptr = NULL; UINT8 ft_len = 0; DBGPRINT(RT_DEBUG_TRACE, ("%s: Fast BSS transition in a RSN \n", __FUNCTION__)); result = FT_AuthConfirmRsnValidation(pAd, pEntry, pFtInfo); if (result != MLME_SUCCESS) break; /* Prepare RSNIE for outgoing frame */ if ((pAd->ApCfg.MBSSID[pEntry->func_tb_idx].AuthMode == Ndis802_11AuthModeWPA1PSKWPA2PSK) || (pAd->ApCfg.MBSSID[pEntry->func_tb_idx].AuthMode == Ndis802_11AuthModeWPA1WPA2)) { rsnie_len = pAd->ApCfg.MBSSID[pEntry->func_tb_idx].RSNIE_Len[1]; rsnie_ptr = &pAd->ApCfg.MBSSID[pEntry->func_tb_idx].RSN_IE[1][0]; } else { rsnie_len = pAd->ApCfg.MBSSID[pEntry->func_tb_idx].RSNIE_Len[0]; rsnie_ptr = &pAd->ApCfg.MBSSID[pEntry->func_tb_idx].RSN_IE[0][0]; } pFtInfoBuf->RSNIE_Len = 0; RTMPInsertRSNIE(pFtInfoBuf->RSN_IE, (PULONG)&pFtInfoBuf->RSNIE_Len, rsnie_ptr, rsnie_len, pEntry->FT_PMK_R1_NAME, LEN_PMK_NAME); ft_len = sizeof(FT_FTIE); /* Prepare MIC-control and MIC field of FTIE for outgoing frame. */ pFtInfoBuf->FtIeInfo.MICCtr.field.IECnt = 3; NdisZeroMemory(pFtInfoBuf->FtIeInfo.MIC, 16); /* Prepare ANonce and Snonce field of FTIE for outgoing frame */ NdisMoveMemory(pFtInfoBuf->FtIeInfo.ANonce, pEntry->ANonce, LEN_NONCE); NdisMoveMemory(pFtInfoBuf->FtIeInfo.SNonce, pEntry->SNonce, LEN_NONCE); /* Prepare in the R0KHID and its length */ pFtInfoBuf->FtIeInfo.R0khIdLen = pFtCfg->FtR0khIdLen; NdisMoveMemory(pFtInfoBuf->FtIeInfo.R0khId, pFtCfg->FtR0khId, pFtCfg->FtR0khIdLen); ft_len += (2 + pFtInfoBuf->FtIeInfo.R0khIdLen); /* Prepare in the R1KHID and its length */ pFtInfoBuf->FtIeInfo.R1khIdLen = MAC_ADDR_LEN; NdisMoveMemory(pFtInfoBuf->FtIeInfo.R1khId, pAd->ApCfg.MBSSID[pEntry->func_tb_idx].Bssid, MAC_ADDR_LEN); ft_len += (2 + MAC_ADDR_LEN); /* Update the length of FTIE */ pFtInfoBuf->FtIeInfo.Len = ft_len; } /* FT auth-req with no RSN Ie (OPEN mode). reply auth-rsp with success. */ FT_RIC_ResourceRequestHandle(pAd, pEntry, (PUCHAR)pFtInfo->RicInfo.pRicInfo, pFtInfo->RicInfo.Len, (PUCHAR)pFtInfoBuf->RicInfo.pRicInfo, (PUINT32)&pFtInfoBuf->RicInfo.Len); /* In an RSN, The IE count need to include RIC for MIC calculation */ if (pFtInfoBuf->FtIeInfo.MICCtr.field.IECnt > 0 && pFtInfoBuf->RicInfo.Len > 0) pFtInfoBuf->FtIeInfo.MICCtr.field.IECnt += 1; /* prepare Ft IEs for association response. */ FT_SET_MDID(pFtInfoBuf->MdIeInfo.MdId, pFtCfg->FtMdId); FtCapPlc.field.FtOverDs = pFtCfg->FtCapFlag.FtOverDs; FtCapPlc.field.RsrReqCap = pFtCfg->FtCapFlag.RsrReqCap; pFtInfoBuf->MdIeInfo.FtCapPlc.word = pFtInfo->MdIeInfo.FtCapPlc.word & FtCapPlc.word; pFtInfoBuf->MdIeInfo.Len = 3; result = MLME_SUCCESS; break; } while(0); return result; } /* ======================================================================== Routine Description: Arguments: Return Value: Note: ======================================================================== */ USHORT FT_AssocReqHandler( IN PRTMP_ADAPTER pAd, IN BOOLEAN isReassoc, IN PFT_CFG pFtCfg, IN PMAC_TABLE_ENTRY pEntry, IN PFT_INFO pPeer_FtInfo, OUT PFT_INFO pFtInfoBuf) { USHORT statusCode = MLME_SUCCESS; FT_CAP_AND_POLICY FtCapPlc; DBGPRINT(RT_DEBUG_TRACE, ("%s:\n", __FUNCTION__)); NdisZeroMemory(pFtInfoBuf, sizeof(FT_INFO)); if ((pFtCfg->FtCapFlag.Dot11rFtEnable) && (pPeer_FtInfo != NULL) && (pPeer_FtInfo->MdIeInfo.Len != 0)) { DBGPRINT(RT_DEBUG_TRACE, ("%s: it's FT client \n", __FUNCTION__)); /* If the contents of the MDIE received by the AP do not match the ** contents advertised in the Beacon and Probe Response frames, the ** AP shall reject the (Re)association Request with status code 54 ** ("Invalid MDIE"). */ if (!FT_MDID_EQU(pPeer_FtInfo->MdIeInfo.MdId, pFtCfg->FtMdId)) statusCode = FT_STATUS_CODE_INVALID_MDIE; else { UINT8 ft_len = 0; ft_len = sizeof(FT_FTIE); /* Indicate this is a FT Initial Mobility Domain Association procedure */ if (!IS_FT_STA(pEntry)) { NdisMoveMemory(&pEntry->MdIeInfo, &pPeer_FtInfo->MdIeInfo, pPeer_FtInfo->MdIeInfo.Len); } if (pPeer_FtInfo->RSNIE_Len != 0) { /* This is Fast BSS transition procedure with RSN */ if (pPeer_FtInfo->FtIeInfo.Len > 0) { UINT16 result; DBGPRINT(RT_DEBUG_TRACE, ("%s: Fast BSS transition in a RSN \n", __FUNCTION__)); result = FT_AssocReqRsnValidation(pAd, pEntry, pPeer_FtInfo, pFtInfoBuf); if (result != MLME_SUCCESS) return result; /* Update the length of GTK in FTIE*/ if (pFtInfoBuf->FtIeInfo.GtkLen) ft_len += (2 + pFtInfoBuf->FtIeInfo.GtkLen); } /* Prepare in the R0KHID and its length */ if (isReassoc) { pFtInfoBuf->FtIeInfo.R0khIdLen = pPeer_FtInfo->FtIeInfo.R0khIdLen; NdisMoveMemory(pFtInfoBuf->FtIeInfo.R0khId, pPeer_FtInfo->FtIeInfo.R0khId, pFtInfoBuf->FtIeInfo.R0khIdLen); } else { pFtInfoBuf->FtIeInfo.R0khIdLen = pFtCfg->FtR0khIdLen; NdisMoveMemory(pFtInfoBuf->FtIeInfo.R0khId, pFtCfg->FtR0khId, pFtCfg->FtR0khIdLen); } ft_len += (2 + pFtInfoBuf->FtIeInfo.R0khIdLen); /* Prepare in the R1KHID and its length */ pFtInfoBuf->FtIeInfo.R1khIdLen = MAC_ADDR_LEN; NdisMoveMemory(pFtInfoBuf->FtIeInfo.R1khId, pAd->ApCfg.MBSSID[pEntry->func_tb_idx].Bssid, MAC_ADDR_LEN); ft_len += (2 + MAC_ADDR_LEN); /* Update the length of FTIE */ pFtInfoBuf->FtIeInfo.Len = ft_len; } /* prepare MDIE for association response. */ FT_SET_MDID(pFtInfoBuf->MdIeInfo.MdId, pFtCfg->FtMdId); FtCapPlc.field.FtOverDs = pFtCfg->FtCapFlag.FtOverDs; FtCapPlc.field.RsrReqCap = pFtCfg->FtCapFlag.RsrReqCap; pFtInfoBuf->MdIeInfo.FtCapPlc.word = pPeer_FtInfo->MdIeInfo.FtCapPlc.word & FtCapPlc.word; pFtInfoBuf->MdIeInfo.Len = 3; } } else { DBGPRINT(RT_DEBUG_TRACE, ("%s: it isn't FT client \n", __FUNCTION__)); } return statusCode; } /* ======================================================================== Routine Description: Arguments: Return Value: Note: ======================================================================== */ VOID FT_R1khEntryTabInit( IN PRTMP_ADAPTER pAd) { INT idx; /* init spin lock */ NdisAllocateSpinLock(pAd, &(pAd->ApCfg.FtTab.FT_R1khEntryTabLock)); pAd->ApCfg.FtTab.FT_R1khEntryTabSize = 0; for (idx = 0; idx < FT_R1KH_ENTRY_HASH_TABLE_SIZE; idx++) { /* init event list */ initList(&(pAd->ApCfg.FtTab.FT_R1khEntryTab[idx])); } pAd->ApCfg.FtTab.FT_R1khEntryTabReady = TRUE; } /* End of FT_R1khEntryTabInit */ /* ======================================================================== Routine Description: Arguments: Return Value: Note: ======================================================================== */ INT FT_R1khEntryInsert( IN PRTMP_ADAPTER pAd, IN PUINT8 pPmkR0Name, IN PUINT8 pPmkR1Name, IN PUINT8 pPmkR1Key, IN PUINT8 pPairwisChipher, IN PUINT8 pAkmSuite, IN UINT32 KeyLifeTime, IN UINT32 RassocDeadline, IN PUINT8 pR0khId, IN UINT8 R0khIdLen, IN PUINT8 pStaMac) { UINT8 HashId; PFT_R1HK_ENTRY pEntry; if (pAd->ApCfg.FtTab.FT_R1khEntryTabSize >= FT_R1KH_ENTRY_TABLE_SIZE) { DBGPRINT(RT_DEBUG_ERROR, ("%s: FT_R1khEntryTab full.\n", __FUNCTION__)); return -1; } if(os_alloc_mem(pAd, (PUCHAR *)&pEntry, sizeof(FT_R1HK_ENTRY)) == NDIS_STATUS_FAILURE) { DBGPRINT(RT_DEBUG_ERROR, ("%s: Unable to alloc memory.\n", __FUNCTION__)); return -1; } pEntry->pNext = NULL; if (pStaMac != NULL) NdisMoveMemory(pEntry->StaMac, pStaMac, MAC_ADDR_LEN); if (pR0khId != NULL && R0khIdLen > 0) { pEntry->R0khIdLen = R0khIdLen; NdisMoveMemory(pEntry->R0khId, pR0khId, R0khIdLen); } if (pPairwisChipher != NULL) NdisMoveMemory(pEntry->PairwisChipher, pPairwisChipher, 4); if (pAkmSuite != NULL) NdisMoveMemory(pEntry->AkmSuite, pAkmSuite, 4); if (pPmkR0Name != NULL) NdisMoveMemory(pEntry->PmkR0Name, pPmkR0Name, 16); if (pPmkR1Name != NULL) NdisMoveMemory(pEntry->PmkR1Name, pPmkR1Name, 16); if (pPmkR1Key != NULL) NdisMoveMemory(pEntry->PmkR1Key, pPmkR1Key, 32); pEntry->KeyLifeTime = KeyLifeTime; pEntry->RassocDeadline = RassocDeadline; HashId = FT_R1KH_HASH_INDEX(pEntry->PmkR1Name); RTMP_SEM_LOCK(&(pAd->ApCfg.FtTab.FT_R1khEntryTabLock)); insertTailList(&pAd->ApCfg.FtTab.FT_R1khEntryTab[HashId], (RT_LIST_ENTRY *)pEntry); pAd->ApCfg.FtTab.FT_R1khEntryTabSize++; RTMP_SEM_UNLOCK(&(pAd->ApCfg.FtTab.FT_R1khEntryTabLock)); return 0; } /* ======================================================================== Routine Description: Arguments: Return Value: Note: ======================================================================== */ VOID FT_R1khEntryDelete( IN PRTMP_ADAPTER pAd, IN PFT_R1HK_ENTRY pEntry) { UINT8 HashId; PFT_TAB pFtTab; pFtTab = &pAd->ApCfg.FtTab; HashId = FT_R1KH_HASH_INDEX(pEntry->PmkR1Name); RTMP_SEM_LOCK(&(pFtTab->FT_R1khEntryTabLock)); delEntryList(&pFtTab->FT_R1khEntryTab[HashId], (RT_LIST_ENTRY *)pEntry); os_free_mem(pAd, pEntry); pFtTab->FT_R1khEntryTabSize--; RTMP_SEM_UNLOCK(&(pFtTab->FT_R1khEntryTabLock)); } /* ======================================================================== Routine Description: Arguments: Return Value: Note: ======================================================================== */ VOID FT_R1khEntryTabDestroy( IN PRTMP_ADAPTER pAd) { INT idx; PFT_R1HK_ENTRY pEntry; PFT_TAB pFtTab; pFtTab = &pAd->ApCfg.FtTab; pFtTab->FT_R1khEntryTabReady = FALSE; RTMP_SEM_LOCK(&(pFtTab->FT_R1khEntryTabLock)); for (idx = 0; idx < FT_R1KH_ENTRY_HASH_TABLE_SIZE; idx ++) { do { pEntry = (PFT_R1HK_ENTRY)removeHeadList( &(pFtTab->FT_R1khEntryTab[idx])); if (pEntry != NULL) os_free_mem(pAd, (PUCHAR)pEntry); } while (pEntry != NULL); } RTMP_SEM_UNLOCK(&(pFtTab->FT_R1khEntryTabLock)); NdisFreeSpinLock(&(pFtTab->FT_R1khEntryTabLock)); } /* ======================================================================== Routine Description: Arguments: Return Value: Note: ======================================================================== */ PFT_R1HK_ENTRY FT_R1khEntryTabLookup( IN PRTMP_ADAPTER pAd, IN PUINT8 pPMKR1Name) { UINT8 HashId; PFT_R1HK_ENTRY pEntry; HashId = FT_R1KH_HASH_INDEX(pPMKR1Name); RTMP_SEM_LOCK(&(pAd->ApCfg.FtTab.FT_R1khEntryTabLock)); pEntry = (PFT_R1HK_ENTRY)pAd->ApCfg.FtTab.FT_R1khEntryTab[HashId].pHead; while(pEntry != NULL) { if (RTMPEqualMemory(pPMKR1Name, pEntry->PmkR1Name, FT_KDP_WPA_NAME_MAX_SIZE)) break; pEntry = pEntry->pNext; } RTMP_SEM_UNLOCK(&(pAd->ApCfg.FtTab.FT_R1khEntryTabLock)); return pEntry; } /* ======================================================================== Routine Description: Arguments: Return Value: Note: ======================================================================== */ VOID FT_FtAction( IN PRTMP_ADAPTER pAd, IN MLME_QUEUE_ELEM *Elem) { PMAC_TABLE_ENTRY pEntry; PHEADER_802_11 pHdr; PFT_ACTION pFtAction; UINT16 FtActLen; BOOLEAN IsTerminate; FT_INFO FtInfo; FT_INFO FtInfoBuf; USHORT result; NDIS_STATUS NStatus; PUCHAR pFtActFrame = NULL; ULONG FtActFrameLen = 0; INT apidx; PFT_CFG pFtCfg; pHdr = (PHEADER_802_11)Elem->Msg; pFtAction = (PFT_ACTION)(&Elem->Msg[LENGTH_802_11]); FtActLen = (Elem->MsgLen - LENGTH_802_11); /* Find which MBSSID to be authenticate */ apidx = get_apidx_by_addr(pAd, pHdr->Addr1); if (apidx >= pAd->ApCfg.BssidNum) return; pFtCfg = &pAd->ApCfg.MBSSID[apidx].FtCfg; /* decide self is terminate or not. */ IsTerminate = (MAC_ADDR_EQUAL(pFtAction->TargetApAddr, pAd->ApCfg.MBSSID[apidx].Bssid)) ? TRUE : FALSE; switch(pFtAction->Action) { case FT_ACTION_BT_REQ: DBGPRINT(RT_DEBUG_TRACE, ("Get FT_ACTION_BT_REQ IsTerminate=%d\n", IsTerminate)); if (IsTerminate) { NStatus = MlmeAllocateMemory(pAd, &pFtActFrame); if (NStatus != NDIS_STATUS_SUCCESS) { DBGPRINT(RT_DEBUG_ERROR,("%s: allocate memory failed.\n", __FUNCTION__)); return; } if (!pFtCfg->FtCapFlag.Dot11rFtEnable) return; pEntry = MacTableLookup(pAd, pHdr->Addr2); if (!pEntry) pEntry = MacTableInsertEntry(pAd, pHdr->Addr2, wdev, ENTRY_CLIENT, TRUE); /* Parse FT-Request action frame. */ FT_ReqActionParse(pAd, (FtActLen - sizeof(PFT_ACTION)), pFtAction->Oct, &FtInfo); /* FT-Request frame Handler. */ NdisZeroMemory(&FtInfoBuf, sizeof(FT_INFO)); result = FT_AuthReqHandler(pAd, pEntry, &FtInfo, &FtInfoBuf); if (result == MLME_SUCCESS) { NdisMoveMemory(&pEntry->MdIeInfo, &FtInfo.MdIeInfo, sizeof(FT_MDIE_INFO)); pEntry->AuthState = AS_AUTH_OPEN; pEntry->Sst = SST_AUTH; } /* Build Ft-Rsp action frame. */ FtActFrameLen = 0; FT_MakeFtActFrame(pAd, pFtActFrame, &FtActFrameLen, FT_ACTION_BT_RSP, pEntry->Addr, pFtAction->TargetApAddr, result, &FtInfoBuf); /* send FT-Rsp action frame to corresponding STA. */ FT_RrbEnqueue(pAd, pHdr->Addr3, (PFT_ACTION)pFtActFrame, FtActFrameLen, pEntry->func_tb_idx); MlmeFreeMemory(pAd, pFtActFrame); } else { FT_RrbEnqueue(pAd, pFtAction->TargetApAddr, (PFT_ACTION)pFtAction, FtActLen, apidx); } break; case FT_ACTION_BT_CONFIRM: DBGPRINT(RT_DEBUG_TRACE, ("Get FT_ACTION_BT_CONFIRM IsTerminate=%d\n", IsTerminate)); if (IsTerminate) { NDIS_STATUS NStatus = MlmeAllocateMemory(pAd, &pFtActFrame); if (NStatus != NDIS_STATUS_SUCCESS) { DBGPRINT(RT_DEBUG_ERROR,("%s: allocate memory failed.\n", __FUNCTION__)); return; } if (Elem->Wcid < MAX_LEN_OF_MAC_TABLE) pEntry = &pAd->MacTab.Content[Elem->Wcid]; else { DBGPRINT(RT_DEBUG_OFF, ("%s: invalid STA.\n", __FUNCTION__)); return; } if (pEntry->func_tb_idx > pAd->ApCfg.BssidNum) return; if (!pFtCfg->FtCapFlag.Dot11rFtEnable) return; /* Parse FT-Request action frame. */ FT_ReqActionParse(pAd, (FtActLen - sizeof(PFT_ACTION)), pFtAction->Oct, &FtInfo); /* FT-Request frame Handler. */ NdisZeroMemory(&FtInfoBuf, sizeof(FT_INFO)); os_alloc_mem(pAd, (UCHAR **)&(FtInfoBuf.RicInfo.pRicInfo), 512); if (FtInfoBuf.RicInfo.pRicInfo != NULL) { result = FT_AuthConfirmHandler(pAd, pEntry, &FtInfo, &FtInfoBuf); /* Build Ft-Ack action frame. */ FtActFrameLen = 0; FT_MakeFtActFrame(pAd, pFtActFrame, &FtActFrameLen, FT_ACTION_BT_ACK, pEntry->Addr, pHdr->Addr3, result, &FtInfoBuf); /* reply FT-Ack action frame to corresponding STA. */ FT_RrbEnqueue(pAd, pHdr->Addr3, (PFT_ACTION)pFtActFrame, FtActFrameLen, pEntry->func_tb_idx); os_free_mem(NULL, FtInfoBuf.RicInfo.pRicInfo); } else { return; } } else FT_RrbEnqueue(pAd, pFtAction->TargetApAddr, pFtAction, FtActLen, apidx); break; case FT_ACTION_BT_RSP: case FT_ACTION_BT_ACK: DBGPRINT(RT_DEBUG_TRACE, ("Get FT_ACTION_BT_RSP or FT_ACTION_BT_ACK \ IsTerminate=%d\n", IsTerminate)); /* forward it to corrspondign STA. */ NStatus = MlmeAllocateMemory(pAd, &pFtActFrame); if (NStatus != NDIS_STATUS_SUCCESS) { DBGPRINT(RT_DEBUG_ERROR,("%s: allocate memory failed.\n", __FUNCTION__)); return; } COPY_MAC_ADDR(pHdr->Addr1, pFtAction->StaMac); COPY_MAC_ADDR(pHdr->Addr2, pAd->ApCfg.MBSSID[apidx].Bssid); COPY_MAC_ADDR(pHdr->Addr3, pAd->ApCfg.MBSSID[apidx].Bssid); pHdr->FC.ToDs = 0; pHdr->FC.FrDs = 1; FtActFrameLen = 0; MakeOutgoingFrame(pFtActFrame, &FtActFrameLen, Elem->MsgLen, pHdr, END_OF_ARGS); MiniportMMRequest(pAd, 0, pFtActFrame, FtActFrameLen); MlmeFreeMemory(pAd, pFtActFrame); break; default: DBGPRINT(RT_DEBUG_ERROR, ("%s: unknow action type (%d).\n", \ __FUNCTION__, pFtAction->Action)); break; } } /* ======================================================================== Routine Description: Arguments: Return Value: Note: ======================================================================== */ static VOID FT_RrbEnqueue( IN PRTMP_ADAPTER pAd, IN PUCHAR pDA, IN PFT_ACTION pFtAction, IN UINT16 FtActLen, IN UINT32 ApIdx) { #if 1 FT_KDP_EVT_ACTION FtKdpEvtAction; COPY_MAC_ADDR(FtKdpEvtAction.MacDa, pDA); switch(pFtAction->Action) { case FT_ACTION_BT_REQ: case FT_ACTION_BT_CONFIRM: FtKdpEvtAction.RequestType = 0; COPY_MAC_ADDR(FtKdpEvtAction.MacSa, pFtAction->StaMac); COPY_MAC_ADDR(FtKdpEvtAction.MacAp, pAd->ApCfg.MBSSID[ApIdx].Bssid); break; case FT_ACTION_BT_RSP: case FT_ACTION_BT_ACK: FtKdpEvtAction.RequestType = 1; COPY_MAC_ADDR(FtKdpEvtAction.MacSa, pFtAction->TargetApAddr); COPY_MAC_ADDR(FtKdpEvtAction.MacAp, pAd->ApCfg.MBSSID[ApIdx].Bssid); break; } FT_KDP_EVENT_INFORM(pAd, ApIdx, FT_KDP_SIG_ACTION, pFtAction, FtActLen, &FtKdpEvtAction); #else UCHAR FtRrb[] = {0x89, 0x0d}; UCHAR Header802_3[14]; NDIS_PACKET *pPktComm; PUCHAR pSA = ZERO_MAC_ADDR; /* allocate a rx packet */ #ifdef FT_OS_LINUX /* pPktComm = (NDIS_PACKET *)dev_alloc_skb(sizeof(RT_SIGNAL_STRUC)+LENGTH_802_3); */ DEV_ALLOC_SKB(pAd, pPktComm, sizeof(RT_SIGNAL_STRUC)+LENGTH_802_3); #endif #ifdef FT_OS_VXWORKS pPktComm = RtmpOSNetPktAlloc(pAd, sizeof(RT_SIGNAL_STRUC)+LENGTH_802_3); #endif if (pPktComm != NULL) { /* The Remote Frame Type for FT Remote request/response messages shall be set to 1. */ UINT8 RemoteFrameType = 1; /* The FT Packet Type field shall be set to 0 for Remote Request, and to 1 for Remote Response. */ UINT8 FtPacketType = 0; pSA = pFtAction->StaMac; if ((pFtAction->Action == FT_ACTION_BT_RSP) || (pFtAction->Action == FT_ACTION_BT_ACK)) FtPacketType = 1; MAKE_802_3_HEADER(Header802_3, pDA, pSA, FtRrb); memcpy(skb_put(RTPKT_TO_OSPKT(pPktComm), LENGTH_802_3), &Header802_3, LENGTH_802_3); memcpy(skb_put(RTPKT_TO_OSPKT(pPktComm), sizeof(UINT8)), &RemoteFrameType, sizeof(UINT8)); memcpy(skb_put(RTPKT_TO_OSPKT(pPktComm), sizeof(UINT8)), &FtPacketType, sizeof(UINT8)); memcpy(skb_put(RTPKT_TO_OSPKT(pPktComm), sizeof(UINT16)), &FtActLen, sizeof(UINT16)); memcpy(skb_put(RTPKT_TO_OSPKT(pPktComm), MAC_ADDR_LEN), &pFtAction->TargetApAddr, MAC_ADDR_LEN); memcpy(skb_put(RTPKT_TO_OSPKT(pPktComm), FtActLen), pFtAction, FtActLen); /* convert 802.11 to 802.3 packet */ #ifdef FT_OS_LINUX GET_OS_PKT_NETDEV(pPktComm) = get_netdev_from_bssid(pAd, BSS0); #endif #ifdef FT_OS_VXWORKS SET_OS_PKT_NETDEV(pPktComm, get_netdev_from_bssid(pAd, BSS0)); #endif /* pass this packet to upper layer */ announce_802_3_packet(pAd, pPktComm); } else { DBGPRINT(RT_DEBUG_ERROR, ("%s: Allocate net buffer fail!\n", __FUNCTION__)); } /* End of if */ #endif } /* ======================================================================== Routine Description: Arguments: Return Value: Note: ======================================================================== */ VOID FT_RrbHandler( IN PRTMP_ADAPTER pAd, IN PUCHAR pPktSrc, IN INT32 PktLen) { PMAC_TABLE_ENTRY pEntry; NDIS_STATUS Status; PUCHAR pOutBuffer = NULL; ULONG FrameLen; HEADER_802_11 Hdr; PFT_RRB pRrb = (PFT_RRB)(pPktSrc + LENGTH_802_3); PUCHAR pDA; PUCHAR pSA; PFT_ACTION pFtAction; ULONG Wcid; Status = MlmeAllocateMemory(pAd, &pOutBuffer); if (Status != NDIS_STATUS_SUCCESS) { DBGPRINT(RT_DEBUG_OFF, ("%s: allocate auth buffer fail!\n", __FUNCTION__)); return; } /* End of if */ pFtAction = (PFT_ACTION)pRrb->Oct; pDA = pPktSrc; pSA = pFtAction->StaMac; pEntry = MacTableLookup(pAd, pSA); if (pEntry) Wcid = pEntry->wcid; else Wcid = RESERVED_WCID; /* Make 802.11 header. */ ActHeaderInit(pAd, &Hdr, pDA, pSA, pRrb->APAdr); /* Make ft action frame. */ MakeOutgoingFrame(pOutBuffer, &FrameLen, sizeof(HEADER_802_11), &Hdr, pRrb->FTActLen, (PUCHAR)pRrb->Oct, END_OF_ARGS); /* enqueue it into FT action state machine. */ REPORT_MGMT_FRAME_TO_MLME(pAd, Wcid, pOutBuffer, FrameLen, 0, 0, 0, 0, 0); if (pOutBuffer) os_free_mem(pAd, pOutBuffer); } /* ======================================================================== Routine Description: Arguments: Return Value: Note: ======================================================================== */ VOID FT_R1KHInfoMaintenance( IN PRTMP_ADAPTER pAd) { INT HashIdx; PFT_R1HK_ENTRY pEntry; PMAC_TABLE_ENTRY pMacEntry; PFT_TAB pFtTab; pFtTab = &pAd->ApCfg.FtTab; if (pFtTab->FT_R1khEntryTabReady != TRUE) return; RTMP_SEM_LOCK(&pFtTab->FT_R1khEntryTabLock); for (HashIdx = 0; HashIdx < FT_R1KH_ENTRY_HASH_TABLE_SIZE; HashIdx++) { pEntry = (PFT_R1HK_ENTRY)\ (pFtTab->FT_R1khEntryTab[HashIdx].pHead); while (pEntry != NULL) { if((pEntry->KeyLifeTime--) == 0) { PFT_R1HK_ENTRY pEntryTmp; MLME_DISASSOC_REQ_STRUCT DisassocReq; /* Kick out the station. and Info KDP daemon to delete the key. */ pMacEntry = MacTableLookup(pAd, pEntry->StaMac); if (pMacEntry) { DisassocParmFill(pAd, &DisassocReq, pEntry->StaMac, MLME_UNSPECIFY_FAIL); MlmeEnqueue(pAd, AP_ASSOC_STATE_MACHINE, APMT2_MLME_DISASSOC_REQ, sizeof(MLME_DISASSOC_REQ_STRUCT), (PVOID)&DisassocReq, 0); } /* Indicate IAPP daemon to delete R0KH-SA relative to the STA */ FT_KDP_EVENT_INFORM(pAd, 0, FT_KDP_SIG_KEY_TIMEOUT, pEntry->StaMac, 6, NULL); pEntryTmp = pEntry->pNext; delEntryList(&pFtTab->FT_R1khEntryTab[HashIdx], (RT_LIST_ENTRY *)pEntry); os_free_mem(pAd, pEntry); pFtTab->FT_R1khEntryTabSize--; pEntry = pEntryTmp; } else pEntry = pEntry->pNext; } } RTMP_SEM_UNLOCK(&pFtTab->FT_R1khEntryTabLock); } VOID FT_ConstructGTKSubIe( IN PRTMP_ADAPTER pAd, IN PMAC_TABLE_ENTRY pEntry, OUT PFT_FTIE_INFO pFtInfo) { UINT8 gtk_len = 0; UINT8 pad_len = 0; UINT8 key_buf[32]; UINT8 e_key_buf[40]; UINT8 key_len; UINT e_key_len; UCHAR apidx; UCHAR key_idx; UCHAR cipher_alg; PUINT8 gtk; ULONG TmpLen = 0; UINT8 remainder; FT_GTK_KEY_INFO KeyInfo; UCHAR rsc[8]; apidx = pEntry->func_tb_idx; gtk = pAd->ApCfg.MBSSID[apidx].GTK; key_idx = pAd->ApCfg.MBSSID[pEntry->func_tb_idx].DefaultKeyId; cipher_alg = pAd->SharedKey[pEntry->func_tb_idx][key_idx].CipherAlg; DBGPRINT(RT_DEBUG_TRACE, ("%s : key idx(%d) \n", __FUNCTION__, key_idx)); switch (cipher_alg) { case CIPHER_WEP64: gtk_len = 5; break; case CIPHER_WEP128: gtk_len = 13; break; case CIPHER_TKIP: gtk_len = 32; break; case CIPHER_AES: gtk_len = 16; break; } /* The Key field shall be padded before encrypting if the key length is less than 16 octets or if it is not a multiple of 8. */ NdisMoveMemory(key_buf, gtk, gtk_len); key_len = gtk_len; if ((remainder = gtk_len & 0x07) != 0) { INT i; pad_len = (8 - remainder); key_buf[gtk_len] = 0xDD; for (i = 1; i < pad_len; i++) key_buf[gtk_len + i] = 0; key_len += pad_len; } if (key_len < 16) { INT i; pad_len = 16 - key_len; for (i = 0; i < pad_len; i++) key_buf[key_len + i] = 0; key_len += pad_len; } NdisZeroMemory(&KeyInfo, sizeof(FT_GTK_KEY_INFO)); KeyInfo.field.KeyId = key_idx; KeyInfo.word = cpu2le16(KeyInfo.word); /* Get Group RSC form Asic */ NdisZeroMemory(rsc, 8); RTMPGetTxTscFromAsic(pAd, apidx, rsc); e_key_len = key_len; AES_Key_Wrap(key_buf, key_len, &pEntry->PTK[LEN_PTK_KCK], LEN_PTK_KEK, e_key_buf, &e_key_len); /* Construct FT GTK-IE*/ MakeOutgoingFrame(pFtInfo->GtkSubIE, &TmpLen, sizeof(FT_GTK_KEY_INFO), &KeyInfo, 1, >k_len, 8, rsc, e_key_len, e_key_buf, END_OF_ARGS); pFtInfo->GtkLen = TmpLen; } /* ======================================================================== Routine Description: Arguments: Return Value: Note: ======================================================================== */ BOOLEAN FT_QueryKeyInfoForKDP( IN PRTMP_ADAPTER pAd, IN UINT32 ApIdx, OUT FT_KDP_EVT_KEY_ELM *pEvtKeyReq) { INT CacheIdx; ULONG Now; UINT32 alive_tick; INT remain_time = 0; PAP_BSSID_INFO pkeyInfo; PFT_R1HK_ENTRY pR1khEntry; UCHAR OriPMKR1Name[FT_KDP_WPA_NAME_MAX_SIZE]; /* Search PMK Cache */ CacheIdx = RTMPSearchPMKIDCache(pAd, ApIdx, pEvtKeyReq->MacAddr); if (CacheIdx == -1) { DBGPRINT(RT_DEBUG_ERROR, ("%s : The PMKR0 doesn't exist for %02x:%02x:%02x:%02x:%02x:%02x\n", __FUNCTION__, PRINT_MAC(pEvtKeyReq->MacAddr))); return FALSE; } pkeyInfo = &pAd->ApCfg.MBSSID[ApIdx].PMKIDCache.BSSIDInfo[CacheIdx]; /* Derive the PMK-R1 and PMK-R1-Name for this R1KH */ FT_DerivePMKR1(pkeyInfo->PMK, pkeyInfo->PMKID, /*pEvtKeyReq->KeyInfo.R1KHID, */ /* peer R1KH-ID */ pAd->ApCfg.MBSSID[ApIdx].Bssid, pEvtKeyReq->MacAddr, pEvtKeyReq->PMKR1, OriPMKR1Name); pR1khEntry = FT_R1khEntryTabLookup(pAd, OriPMKR1Name); if (pR1khEntry == NULL) { DBGPRINT(RT_DEBUG_ERROR, ("%s : No initial association information 2 for %02x:%02x:%02x:%02x:%02x:%02x\n", __FUNCTION__, PRINT_MAC(pEvtKeyReq->MacAddr))); return FALSE; } FT_DerivePMKR1(pkeyInfo->PMK, pkeyInfo->PMKID, pEvtKeyReq->KeyInfo.R1KHID, /* peer R1KH-ID */ pEvtKeyReq->MacAddr, pEvtKeyReq->PMKR1, pEvtKeyReq->PMKR1Name); /* calculate the remaine time of PMKR0-Key */ NdisGetSystemUpTime(&Now); alive_tick = Now - pkeyInfo->RefreshTime; if (alive_tick < pAd->ApCfg.MBSSID[ApIdx].PMKCachePeriod) remain_time = (pAd->ApCfg.MBSSID[ApIdx].PMKCachePeriod - alive_tick)/OS_HZ; /* Assign KeyLifeTime and Reassociation Deadline */ pEvtKeyReq->KeyLifeTime = remain_time; pEvtKeyReq->ReassocDeadline = FT_REASSOC_DEADLINE; /* Assign R0KH-ID */ pEvtKeyReq->KeyInfo.R0KHIDLen = pAd->ApCfg.MBSSID[ApIdx].FtCfg.FtR0khIdLen; NdisMoveMemory(pEvtKeyReq->KeyInfo.R0KHID, pAd->ApCfg.MBSSID[ApIdx].FtCfg.FtR0khId, pAd->ApCfg.MBSSID[ApIdx].FtCfg.FtR0khIdLen); /* Assign PMK-R0-Name */ NdisMoveMemory(pEvtKeyReq->KeyInfo.PMKR0Name, pkeyInfo->PMKID, LEN_PMK_NAME); /* Assign cipher and AKM */ NdisMoveMemory(pEvtKeyReq->PairwisChipher, pR1khEntry->PairwisChipher, 4); NdisMoveMemory(pEvtKeyReq->AkmSuite, pR1khEntry->AkmSuite, 4); /* Assign R0KH MAC */ NdisMoveMemory(pEvtKeyReq->R0KH_MAC, pEvtKeyReq->MacAddr, 6); return TRUE; } UINT16 FT_AuthReqRsnValidation( IN PRTMP_ADAPTER pAd, IN PMAC_TABLE_ENTRY pEntry, IN PFT_CFG pFtCfg, IN PFT_INFO pFtInfo_in, OUT PFT_INFO pFtInfo_out) { UINT8 count = 0; PUINT8 pAkmSuite = NULL; PUINT8 pPmkR0Name = NULL; PUINT8 pCipher = NULL; PFT_R1HK_ENTRY pR1hkEntry = NULL; UINT8 ft_len = 0; UINT8 ptk_len; UINT8 rsnie_len = 0; PUINT8 rsnie_ptr = NULL; UINT16 result = MLME_SUCCESS; /* Check the validity of the received RSNIE */ if ((result = APValidateRSNIE(pAd, pEntry, pFtInfo_in->RSN_IE, pFtInfo_in->RSNIE_Len)) != MLME_SUCCESS) { return result; } /* Extract the PMK-R0-Name from the received RSNIE */ pPmkR0Name = WPA_ExtractSuiteFromRSNIE(pFtInfo_in->RSN_IE, pFtInfo_in->RSNIE_Len, PMKID_LIST, &count); if (pPmkR0Name) { #ifdef FT_RSN_DEBUG hex_dump("FT PMK-R0-NAME", pPmkR0Name, count * LEN_PMK_NAME); #endif /* FT_RSN_DEBUG */ /* The R1KH of the target AP uses the value of PMKR0Name and other information in the frame to calculate PMKR1Name. */ FT_DerivePMKR1Name(pPmkR0Name, pAd->ApCfg.MBSSID[pEntry->func_tb_idx].Bssid, pEntry->Addr, pEntry->FT_PMK_R1_NAME); hex_dump("pPmkR0Name=", pPmkR0Name, LEN_PMK_NAME); hex_dump("pAd->ApCfg.MBSSID[pEntry->func_tb_idx].Bssid=", pAd->ApCfg.MBSSID[pEntry->func_tb_idx].Bssid, 6); hex_dump("pEntry->Addr=", pEntry->Addr, 6); hex_dump("pEntry->FT_PMK_R1_NAME=", pEntry->FT_PMK_R1_NAME, sizeof(pEntry->FT_PMK_R1_NAME)); /* Look up the R1KH Table */ pR1hkEntry = FT_R1khEntryTabLookup(pAd, pEntry->FT_PMK_R1_NAME); /* If the target AP does not have the key identified by PMKR1Name, it may retrieve that key from the R0KH identified by the STA. */ if ((pR1hkEntry == NULL) || (RTMPEqualMemory(pEntry->FT_PMK_R1_NAME, pR1hkEntry->PmkR1Name, LEN_PMK_NAME) == FALSE)) { #if 0 FT_KDP_EVT_KEY_ELM EvtKeyReq; NdisZeroMemory(&EvtKeyReq, sizeof(FT_KDP_EVT_KEY_ELM)); /* make up request content */ EvtKeyReq.ElmId = FT_KDP_ELM_ID_PRI; EvtKeyReq.ElmLen = FT_KDP_ELM_PRI_LEN; EvtKeyReq.OUI[0] = FT_KDP_ELM_PRI_OUI_0; EvtKeyReq.OUI[1] = FT_KDP_ELM_PRI_OUI_1; EvtKeyReq.OUI[2] = FT_KDP_ELM_PRI_OUI_2; NdisMoveMemory(EvtKeyReq.MacAddr, pEntry->Addr, ETH_ALEN); NdisMoveMemory(EvtKeyReq.KeyInfo.S1KHID, pEntry->Addr, FT_KDP_S1KHID_MAX_SIZE); NdisMoveMemory(EvtKeyReq.KeyInfo.R1KHID, pAd->ApCfg.MBSSID[pEntry->func_tb_idx].Bssid, FT_KDP_R1KHID_MAX_SIZE); /* Issue an event to query PMKR1 information from R0KH */ FT_KDP_EVENT_INFORM(pAd, pEntry->func_tb_idx, FT_KDP_SIG_KEY_REQ_AUTO, &EvtKeyReq, sizeof(FT_KDP_EVT_KEY_ELM), NULL); #endif /* If the requested R0KH is not reachable, the AP shall respond to the Authentication Request with status code 28 ("R0KH unreachable"). */ #if 1 { return FT_STATUS_CODE_R0KH_UNREACHABLE; } #endif } /* If the RSNIE in the Authentication Request frame contains an invalid PMKR0Name, and the AP has determined that it is an invalid PMKR0Name, the AP shall reject the Authentication Request with status code 53 ("Invalid PMKID"). */ if ((pR1hkEntry == NULL) || (RTMPEqualMemory(pPmkR0Name, pR1hkEntry->PmkR0Name, LEN_PMK_NAME) == FALSE)) { DBGPRINT_ERR(("%s : The PMKID is invalid\n", __FUNCTION__)); hex_dump("Peer PMKR0Name", pPmkR0Name, LEN_PMK_NAME); hex_dump("Own PMKR0Name", pR1hkEntry->PmkR0Name, LEN_PMK_NAME); return FT_STATUS_CODE_INVALID_PMKID; } } else { /* reject the Authentication Request with status code 53 ("Invalid PMKID") */ DBGPRINT_ERR(("%s : The peer PMKID is emtpy\n", __FUNCTION__)); return FT_STATUS_CODE_INVALID_PMKID; } if (pR1hkEntry == NULL) { DBGPRINT_ERR(("%s : The R1KH table doesn't exist\n", __FUNCTION__)); return FT_STATUS_CODE_R0KH_UNREACHABLE; } /* Update the Reassocation Deadline */ pEntry->AssocDeadLine = pR1hkEntry->RassocDeadline; /* Extract the AKM suite from the received RSNIE */ pAkmSuite = WPA_ExtractSuiteFromRSNIE(pFtInfo_in->RSN_IE, pFtInfo_in->RSNIE_Len, AKM_SUITE, &count); if ((pAkmSuite == NULL) || (RTMPEqualMemory(pAkmSuite, pR1hkEntry->AkmSuite, 4) == FALSE)) { /* It doesn't a negotiated AKM of Fast BSS Transition, the AP shall reject the Authentication Request with status code 43 ("Invalid AKMP"). */ DBGPRINT_ERR(("%s : The AKM is invalid\n", __FUNCTION__)); return MLME_INVALID_AKMP; } /* Extract the pairwise cipher suite from the received RSNIE */ pCipher = WPA_ExtractSuiteFromRSNIE(pFtInfo_in->RSN_IE, pFtInfo_in->RSNIE_Len, PAIRWISE_SUITE, &count); if ((pCipher == NULL) || (RTMPEqualMemory(pCipher, pR1hkEntry->PairwisChipher, 4) == FALSE)) { /* If the non-AP STA selects a pairwise cipher suite in the RSNIE that is different than the ones used in the Initial Mobility Domain association, then the AP shall reject the Authentication Request with status code 19 ("Invalid Pair-wise Cipher"). */ DBGPRINT_ERR(("%s : The pairwise-cipher is invalid\n", __FUNCTION__)); return MLME_INVALID_PAIRWISE_CIPHER; } /* Check the validity of R0KHID */ if ((pFtInfo_in->FtIeInfo.R0khIdLen != pR1hkEntry->R0khIdLen) || (RTMPEqualMemory(pFtInfo_in->FtIeInfo.R0khId, pR1hkEntry->R0khId, pR1hkEntry->R0khIdLen) == FALSE)) { /* If the FTIE in the FT Request frame contains an invalid R0KH-ID, the AP shall reject the FT Request with status code 55 ("Invalid FTIE"). */ DBGPRINT_ERR(("%s : The FTIE is invalid\n", __FUNCTION__)); return FT_STATUS_CODE_INVALID_FTIE; } /* Get PMK-R1 from R1KH Table */ NdisMoveMemory(pEntry->FT_PMK_R1, pR1hkEntry->PmkR1Key, LEN_PMK); /* Get SNonce from Auth-req */ NdisMoveMemory(pEntry->SNonce, pFtInfo_in->FtIeInfo.SNonce, LEN_NONCE); /* Generate ANonce randomly */ GenRandom(pAd, pAd->ApCfg.MBSSID[pEntry->func_tb_idx].Bssid, pEntry->ANonce); hex_dump("anonce", pEntry->ANonce, 32); hex_dump("snonce", pEntry->SNonce, 32); if (pEntry->WepStatus == Ndis802_11TKIPEnable) ptk_len = 32+32; else ptk_len = 32+16; /* Derive FT PTK and PTK-NAME */ FT_DerivePTK(pEntry->FT_PMK_R1, pEntry->FT_PMK_R1_NAME, pEntry->ANonce, pEntry->SNonce, pAd->ApCfg.MBSSID[pEntry->func_tb_idx].Bssid, pEntry->Addr, ptk_len, pEntry->PTK, pEntry->PTK_NAME); ft_len = sizeof(FT_FTIE); /* Prepare some information for authentication response using */ NdisMoveMemory(pFtInfo_out->FtIeInfo.ANonce, pEntry->ANonce, LEN_NONCE); NdisMoveMemory(pFtInfo_out->FtIeInfo.SNonce, pEntry->SNonce, LEN_NONCE); pFtInfo_out->FtIeInfo.R0khIdLen = pR1hkEntry->R0khIdLen; NdisMoveMemory(pFtInfo_out->FtIeInfo.R0khId, pR1hkEntry->R0khId, pR1hkEntry->R0khIdLen); ft_len += (2 + pR1hkEntry->R0khIdLen); pFtInfo_out->FtIeInfo.R1khIdLen = MAC_ADDR_LEN; NdisMoveMemory(pFtInfo_out->FtIeInfo.R1khId, pAd->ApCfg.MBSSID[pEntry->func_tb_idx].Bssid, MAC_ADDR_LEN); ft_len += (2 + MAC_ADDR_LEN); /* Update the total length for FTIE */ pFtInfo_out->FtIeInfo.Len = ft_len; /* Prepare RSNIE for authentication response */ if ((pAd->ApCfg.MBSSID[pEntry->func_tb_idx].AuthMode == Ndis802_11AuthModeWPA1PSKWPA2PSK) || (pAd->ApCfg.MBSSID[pEntry->func_tb_idx].AuthMode == Ndis802_11AuthModeWPA1WPA2)) { rsnie_len = pAd->ApCfg.MBSSID[pEntry->func_tb_idx].RSNIE_Len[1]; rsnie_ptr = &pAd->ApCfg.MBSSID[pEntry->func_tb_idx].RSN_IE[1][0]; } else { rsnie_len = pAd->ApCfg.MBSSID[pEntry->func_tb_idx].RSNIE_Len[0]; rsnie_ptr = &pAd->ApCfg.MBSSID[pEntry->func_tb_idx].RSN_IE[0][0]; } pFtInfo_out->RSNIE_Len = 0; RTMPInsertRSNIE(pFtInfo_out->RSN_IE, (PULONG)&pFtInfo_out->RSNIE_Len, rsnie_ptr, rsnie_len, pPmkR0Name, LEN_PMK_NAME); return MLME_SUCCESS; } UINT16 FT_AuthConfirmRsnValidation( IN PRTMP_ADAPTER pAd, IN PMAC_TABLE_ENTRY pEntry, IN PFT_INFO pFtInfo_in) { PFT_FTIE_INFO pPeerFtIe; PUINT8 pmkid_ptr = NULL; UINT8 pmkid_count = 0; UINT8 ft_mic[16]; PFT_R1HK_ENTRY pR1hkEntry = NULL; PUINT8 pAkmSuite = NULL; UINT8 count = 0; /* The R1KH of the target AP verifies the MIC in the FTIE in the Reassociation Request frame, and shall discard the request if the MIC is incorrect. */ FT_CalculateMIC(pEntry->Addr, pAd->ApCfg.MBSSID[pEntry->func_tb_idx].Bssid, pEntry->PTK, 3, pFtInfo_in->RSN_IE, pFtInfo_in->RSNIE_Len, pFtInfo_in->MdIeInfo.pMdIe, pFtInfo_in->MdIeInfo.Len + 2, pFtInfo_in->FtIeInfo.pFtIe, pFtInfo_in->FtIeInfo.Len + 2, pFtInfo_in->RicInfo.RicIEs, pFtInfo_in->RicInfo.RicIEsLen, ft_mic); if (!RTMPEqualMemory(ft_mic, pFtInfo_in->FtIeInfo.MIC, FT_MIC_LEN)) { DBGPRINT_ERR(("%s : MIC is different\n", __FUNCTION__)); hex_dump("received MIC", pFtInfo_in->FtIeInfo.MIC, FT_MIC_LEN); hex_dump("desired MIC", ft_mic, FT_MIC_LEN); return 0xFFFF; } pPeerFtIe = &pFtInfo_in->FtIeInfo; /* Look up the R1KH Table */ pR1hkEntry = FT_R1khEntryTabLookup(pAd, pEntry->FT_PMK_R1_NAME); if (pR1hkEntry == NULL) { DBGPRINT_ERR(("%s : Invalid R1KH table in target AP\n", __FUNCTION__)); return FT_STATUS_CODE_RESERVED; } /* Extract the AKM suite from the received RSNIE */ pAkmSuite = WPA_ExtractSuiteFromRSNIE(pFtInfo_in->RSN_IE, pFtInfo_in->RSNIE_Len, AKM_SUITE, &count); if ((pAkmSuite == NULL) || (RTMPEqualMemory(pAkmSuite, pR1hkEntry->AkmSuite, 4) == FALSE)) { /* It doesn't a negotiated AKM of Fast BSS Transition, the AP shall reject the Authentication Request with status code 43 ("Invalid AKMP"). */ DBGPRINT_ERR(("%s : Invalid AKMP\n", __FUNCTION__)); return MLME_INVALID_AKMP; } /* If the FTIE in the Reassociation Request frame contains a different R0KH-ID, R1KH-ID, ANonce, or SNonce, the AP shall reject the Reassociation Request with status code 55 ("Invalid FTIE"). */ if ((RTMPEqualMemory(pPeerFtIe->R0khId, pR1hkEntry->R0khId, pR1hkEntry->R0khIdLen) == FALSE) || (RTMPEqualMemory(pPeerFtIe->R1khId, pAd->ApCfg.MBSSID[pEntry->func_tb_idx].Bssid, MAC_ADDR_LEN) == FALSE) || (RTMPEqualMemory(pPeerFtIe->ANonce, pEntry->ANonce, 32) == FALSE) || (RTMPEqualMemory(pPeerFtIe->SNonce, pEntry->SNonce, 32) == FALSE)) { DBGPRINT_ERR(("%s : Invalid FTIE\n", __FUNCTION__)); return FT_STATUS_CODE_INVALID_FTIE; } /* If the RSNIE in the Reassociation Request frame contains an invalid PMKR1Name, the AP shall reject the Reassociation Request with status code 53 ("Invalid PMKID"). */ pmkid_ptr = WPA_ExtractSuiteFromRSNIE(pFtInfo_in->RSN_IE, pFtInfo_in->RSNIE_Len, PMKID_LIST, &pmkid_count); if ((pmkid_ptr == NULL) || (RTMPEqualMemory(pmkid_ptr, pEntry->FT_PMK_R1_NAME, LEN_PMK_NAME) == FALSE)) { DBGPRINT_ERR(("%s : Invalid PMKID\n", __FUNCTION__)); return FT_STATUS_CODE_INVALID_PMKID; } return MLME_SUCCESS; } UINT16 FT_AssocReqRsnValidation( IN PRTMP_ADAPTER pAd, IN PMAC_TABLE_ENTRY pEntry, IN PFT_INFO pFtInfo_in, OUT PFT_INFO pFtInfo_out) { PFT_FTIE_INFO pPeerFtIe; PUINT8 pmkid_ptr = NULL; UINT8 pmkid_count = 0; UINT8 ft_mic[16]; UINT8 rsnie_len = 0; PUINT8 rsnie_ptr = NULL; PFT_R1HK_ENTRY pR1hkEntry = NULL; PUINT8 pAkmSuite = NULL; UINT8 count = 0; /* The R1KH of the target AP verifies the MIC in the FTIE in the Reassociation Request frame, and shall discard the request if the MIC is incorrect. */ FT_CalculateMIC(pEntry->Addr, pAd->ApCfg.MBSSID[pEntry->func_tb_idx].Bssid, pEntry->PTK, 5, pFtInfo_in->RSN_IE, pFtInfo_in->RSNIE_Len, pFtInfo_in->MdIeInfo.pMdIe, pFtInfo_in->MdIeInfo.Len + 2, pFtInfo_in->FtIeInfo.pFtIe, pFtInfo_in->FtIeInfo.Len + 2, pFtInfo_in->RicInfo.RicIEs, pFtInfo_in->RicInfo.RicIEsLen, ft_mic); if (!RTMPEqualMemory(ft_mic, pFtInfo_in->FtIeInfo.MIC, 16)) { DBGPRINT_ERR(("%s : MIC is different\n", __FUNCTION__)); hex_dump("received MIC", pFtInfo_in->FtIeInfo.MIC, 16); hex_dump("desired MIC", ft_mic, 16); return 0xFFFF; } pPeerFtIe = &pFtInfo_in->FtIeInfo; /* Look up the R1KH Table */ pR1hkEntry = FT_R1khEntryTabLookup(pAd, pEntry->FT_PMK_R1_NAME); if (pR1hkEntry == NULL) { DBGPRINT_ERR(("%s : Invalid R1KH table in target AP\n", __FUNCTION__)); return FT_STATUS_CODE_RESERVED; } /* Extract the AKM suite from the received RSNIE */ pAkmSuite = WPA_ExtractSuiteFromRSNIE(pFtInfo_in->RSN_IE, pFtInfo_in->RSNIE_Len, AKM_SUITE, &count); if ((pAkmSuite == NULL) || (RTMPEqualMemory(pAkmSuite, pR1hkEntry->AkmSuite, 4) == FALSE)) { /* It doesn't a negotiated AKM of Fast BSS Transition, the AP shall reject the Authentication Request with status code 43 ("Invalid AKMP"). */ DBGPRINT_ERR(("%s : Invalid AKMP\n", __FUNCTION__)); return MLME_INVALID_AKMP; } /* If the FTIE in the Reassociation Request frame contains a different R0KH-ID, R1KH-ID, ANonce, or SNonce, the AP shall reject the Reassociation Request with status code 55 ("Invalid FTIE"). */ if ((RTMPEqualMemory(pPeerFtIe->R0khId, pR1hkEntry->R0khId, pR1hkEntry->R0khIdLen) == FALSE) || (RTMPEqualMemory(pPeerFtIe->R1khId, pAd->ApCfg.MBSSID[pEntry->func_tb_idx].Bssid, MAC_ADDR_LEN) == FALSE) || (RTMPEqualMemory(pPeerFtIe->ANonce, pEntry->ANonce, 32) == FALSE) || (RTMPEqualMemory(pPeerFtIe->SNonce, pEntry->SNonce, 32) == FALSE)) { DBGPRINT_ERR(("%s : Invalid FTIE\n", __FUNCTION__)); return FT_STATUS_CODE_INVALID_FTIE; } /* If the RSNIE in the Reassociation Request frame contains an invalid PMKR1Name, the AP shall reject the Reassociation Request with status code 53 ("Invalid PMKID"). */ pmkid_ptr = WPA_ExtractSuiteFromRSNIE(pFtInfo_in->RSN_IE, pFtInfo_in->RSNIE_Len, PMKID_LIST, &pmkid_count); if ((pmkid_ptr == NULL) || (RTMPEqualMemory(pmkid_ptr, pEntry->FT_PMK_R1_NAME, LEN_PMK_NAME) == FALSE)) { DBGPRINT_ERR(("%s : Invalid PMKID\n", __FUNCTION__)); return FT_STATUS_CODE_INVALID_PMKID; } /* Prepare RSNIE for outgoing frame */ if ((pAd->ApCfg.MBSSID[pEntry->func_tb_idx].AuthMode == Ndis802_11AuthModeWPA1PSKWPA2PSK) || (pAd->ApCfg.MBSSID[pEntry->func_tb_idx].AuthMode == Ndis802_11AuthModeWPA1WPA2)) { rsnie_len = pAd->ApCfg.MBSSID[pEntry->func_tb_idx].RSNIE_Len[1]; rsnie_ptr = &pAd->ApCfg.MBSSID[pEntry->func_tb_idx].RSN_IE[1][0]; } else { rsnie_len = pAd->ApCfg.MBSSID[pEntry->func_tb_idx].RSNIE_Len[0]; rsnie_ptr = &pAd->ApCfg.MBSSID[pEntry->func_tb_idx].RSN_IE[0][0]; } pFtInfo_out->RSNIE_Len = 0; RTMPInsertRSNIE(pFtInfo_out->RSN_IE, (PULONG)&pFtInfo_out->RSNIE_Len, rsnie_ptr, rsnie_len, pEntry->FT_PMK_R1_NAME, LEN_PMK_NAME); /* Prepare MIC-control and MIC field of FTIE for outgoing frame. */ pFtInfo_out->FtIeInfo.MICCtr.field.IECnt = 3; NdisZeroMemory(pFtInfo_out->FtIeInfo.MIC, 16); /* Prepare ANonce and Snonce field of FTIE for outgoing frame */ NdisMoveMemory(pFtInfo_out->FtIeInfo.ANonce, pEntry->ANonce, LEN_NONCE); NdisMoveMemory(pFtInfo_out->FtIeInfo.SNonce, pEntry->SNonce, LEN_NONCE); /* Prepare GTK related information of FTIE for outgoing frame */ FT_ConstructGTKSubIe(pAd, pEntry, &pFtInfo_out->FtIeInfo); /*ft_len += (2 + pFtInfo_out->FtIeInfo.GtkLen); */ /* Prepare RIC-Response */ #if 0 if (pFtInfo_in->RicInfo.Len) { pFtInfo_out->FtIeInfo.MICCtr.field.IECnt += 1; } #endif return MLME_SUCCESS; } #endif /* CONFIG_AP_SUPPORT */ /* ======================================================================== Routine Description: Arguments: Return Value: Note: ======================================================================== */ VOID FT_FillMdIeInfo( PEID_STRUCT eid_ptr, PFT_MDIE_INFO pMdIeInfo) { PFT_MDIE pMdIe; pMdIeInfo->Len = 3; pMdIeInfo->pMdIe = eid_ptr; /* store the pointer of the original MD-IE for MIC calculating */ pMdIe = (PFT_MDIE)(eid_ptr->Octet); FT_SET_MDID(pMdIeInfo->MdId, pMdIe->MdId); NdisMoveMemory(&(pMdIeInfo->FtCapPlc.word), &pMdIe->FtCapPlc.word, sizeof(FT_CAP_AND_POLICY)); } /* ======================================================================== Routine Description: Arguments: Return Value: Note: ======================================================================== */ VOID FT_FillFtIeInfo( PEID_STRUCT eid_ptr, PFT_FTIE_INFO pFtIeInfo) { PFT_FTIE pFtIe; PFT_OPTION_FIELD subEidPtr; UINT16 MicCtrBuf; INT RemainLen; PUINT8 ptr; RemainLen = eid_ptr->Len; pFtIeInfo->Len = eid_ptr->Len; pFtIeInfo->pFtIe = eid_ptr; /* store the pointer of the original FT-IE for MIC calculating */ pFtIe = (PFT_FTIE)eid_ptr->Octet; NdisMoveMemory(&MicCtrBuf, &(pFtIe->MICCtr.word), sizeof(FT_MIC_CTR_FIELD)); pFtIeInfo->MICCtr.word = le2cpu16(MicCtrBuf); RemainLen -= 2; NdisMoveMemory(pFtIeInfo->MIC, pFtIe->MIC, 16); RemainLen -= 16; NdisMoveMemory(pFtIeInfo->ANonce, pFtIe->ANonce, 32); RemainLen -= 32; NdisMoveMemory(pFtIeInfo->SNonce, pFtIe->SNonce, 32); RemainLen -= 32; /* Pare sub-element field. */ /*subEidPtr = (PFT_OPTION_FIELD)(pFtIe->Option); */ ptr = pFtIe->Option; while (RemainLen > 0) { subEidPtr = (PFT_OPTION_FIELD)ptr; switch(subEidPtr->SubElementId) { case FT_R0KH_ID: if ((subEidPtr->Len > 0) && (subEidPtr->Len <=FT_ROKH_ID_LEN)) { pFtIeInfo->R0khIdLen = subEidPtr->Len; NdisMoveMemory(pFtIeInfo->R0khId, subEidPtr->Oct, pFtIeInfo->R0khIdLen); } else DBGPRINT(RT_DEBUG_ERROR, ("%s Invalid R0KHID Length (%d)\n", __FUNCTION__, subEidPtr->Len)); break; case FT_R1KH_ID: if (subEidPtr->Len == FT_R1KH_ID_LEN) { pFtIeInfo->R1khIdLen = subEidPtr->Len; NdisMoveMemory(pFtIeInfo->R1khId, subEidPtr->Oct, pFtIeInfo->R1khIdLen); } else DBGPRINT(RT_DEBUG_ERROR, ("%s Invalid R1KHID Length (%d)\n", __FUNCTION__, subEidPtr->Len)); break; case FT_GTK: if (subEidPtr->Len > 0) { pFtIeInfo->GtkLen = subEidPtr->Len; NdisMoveMemory(pFtIeInfo->GtkSubIE, &subEidPtr->Oct[0], subEidPtr->Len); } break; default: break; } ptr += (subEidPtr->Len + 2); RemainLen -= (subEidPtr->Len + 2); /* avoid infinite loop. */ if (subEidPtr->Len == 0) break; } } #ifdef CONFIG_STA_SUPPORT VOID FT_FTIeParse( IN UINT8 FtIeLen, IN PFT_FTIE pFtIe, OUT PUCHAR pR1KH_Id, OUT UCHAR *GTKLen, OUT PUCHAR pGTK, OUT UCHAR *R0KH_IdLen, OUT PUCHAR pR0KH_Id) { UCHAR *ptr; UINT8 RemainLen; PFT_OPTION_FIELD subEidPtr; *GTKLen = 0; *R0KH_IdLen = 0; ptr = (PUCHAR)&pFtIe->Option[0]; RemainLen = FtIeLen - sizeof(FT_FTIE); DBGPRINT(RT_DEBUG_TRACE, ("FT_TEMP- FtIeParse ( FtIeLen = %d )\n", FtIeLen)); DBGPRINT(RT_DEBUG_TRACE, ("FT_TEMP- FtIeParse ( Len that doesn't include subelement = %d )\n", RemainLen)); while (RemainLen > 0) { subEidPtr = (PFT_OPTION_FIELD)ptr; if (subEidPtr->SubElementId == FT_R1KH_ID) { RTMPMoveMemory(pR1KH_Id, subEidPtr->Oct, subEidPtr->Len); DBGPRINT(RT_DEBUG_TRACE, ("%s : R1KHID length(%d)\n", __FUNCTION__, subEidPtr->Len)); } else if (subEidPtr->SubElementId == FT_GTK) { *GTKLen = subEidPtr->Len; DBGPRINT(RT_DEBUG_TRACE, ("FT_TEMP- FtIeParse ( *GTKLen = %d )\n", *GTKLen)); if ((*GTKLen >= 15) && (*GTKLen <= 64)) { RTMPMoveMemory(pGTK, subEidPtr->Oct, subEidPtr->Len); } else { *GTKLen = 0; DBGPRINT(RT_DEBUG_ERROR, ("FT- FtIeParse ( Invalid GTKLen = %d)\n", *GTKLen)); } } else if (subEidPtr->SubElementId == FT_R0KH_ID) { *R0KH_IdLen = subEidPtr->Len; DBGPRINT(RT_DEBUG_TRACE, ("FT_TEMP- FtIeParse ( *R0KH_IdLen = %d )\n", *R0KH_IdLen)); if ((*R0KH_IdLen >= 1) && (*R0KH_IdLen <= 48)) { RTMPMoveMemory(pR0KH_Id, subEidPtr->Oct, subEidPtr->Len); } else { DBGPRINT(RT_DEBUG_ERROR, ("FT- FtIeParse ( Invalid R0KH_IdLen = %d )\n",*R0KH_IdLen)); *R0KH_IdLen = 0; } } ptr += (subEidPtr->Len + 2); RemainLen -= (subEidPtr->Len + 2); } DBGPRINT(RT_DEBUG_TRACE, ("%s done\n", __FUNCTION__)); } /* ========================================================================== Description: IRQL = DISPATCH_LEVEL Output: ========================================================================== */ BOOLEAN FT_CheckForRoaming( IN PRTMP_ADAPTER pAd) { USHORT i; BSS_TABLE *pRoamTab = &pAd->MlmeAux.RoamTab; BSS_ENTRY *pBss; CHAR max_rssi; DBGPRINT(RT_DEBUG_TRACE, ("==> FT_CheckForRoaming\n")); /* put all roaming candidates into RoamTab, and sort in RSSI order */ BssTableInit(pRoamTab); for (i = 0; i < pAd->ScanTab.BssNr; i++) { pBss = &pAd->ScanTab.BssEntry[i]; if (pBss->bHasMDIE == FALSE) continue; /* skip legacy AP */ if (MAC_ADDR_EQUAL(pBss->Bssid, pAd->CommonCfg.Bssid)) continue; /* skip current AP */ if (!FT_MDID_EQU(pBss->FT_MDIE.MdId, pAd->StaCfg.Dot11RCommInfo.MdIeInfo.MdId)) continue; /* skip different MDID */ if ((pBss->Rssi <= -85) && (pBss->Channel == pAd->CommonCfg.Channel)) continue; /* skip RSSI too weak at the same channel */ if ((pBss->Channel != pAd->CommonCfg.Channel) && (pBss->FT_MDIE.FtCapPlc.field.FtOverDs == FALSE)) continue; /* skip AP in different channel without supporting FtOverDs */ max_rssi = RTMPMaxRssi(pAd, pAd->StaCfg.RssiSample.LastRssi[0], pAd->StaCfg.RssiSample.LastRssi[1], pAd->StaCfg.RssiSample.LastRssi[2]); if (pBss->Rssi < (max_rssi + RSSI_DELTA)) continue; /* skip AP without better RSSI */ if ((pBss->AuthMode != pAd->StaCfg.AuthMode) || (pBss->WepStatus != pAd->StaCfg.WepStatus)) continue; /* skip different Security Setting */ DBGPRINT(RT_DEBUG_TRACE, ("max_rssi = %d, pBss->Rssi = %d\n", max_rssi, pBss->Rssi)); /* AP passing all above rules is put into roaming candidate table */ NdisMoveMemory(&pRoamTab->BssEntry[pRoamTab->BssNr], pBss, sizeof(BSS_ENTRY)); pRoamTab->BssNr += 1; } DBGPRINT(RT_DEBUG_TRACE, ("<== FT_CheckForRoaming (BssNr=%d)\n", pRoamTab->BssNr)); if (pRoamTab->BssNr > 0) { /* check CntlMachine.CurrState to avoid collision with NDIS SetOID request */ if (pAd->Mlme.CntlMachine.CurrState == CNTL_IDLE) { pAd->RalinkCounters.PoorCQIRoamingCount ++; DBGPRINT(RT_DEBUG_TRACE, ("MMCHK - Roaming attempt #%ld\n", pAd->RalinkCounters.PoorCQIRoamingCount)); MlmeEnqueue(pAd, MLME_CNTL_STATE_MACHINE, MT2_MLME_ROAMING_REQ, 0, NULL, 0); RTMP_MLME_HANDLER(pAd); return TRUE; } } return FALSE; } BOOLEAN FT_GetMDIE( IN PNDIS_802_11_VARIABLE_IEs pVIE, IN USHORT LengthVIE, OUT FT_MDIE_INFO *pMdIeInfo) { PEID_STRUCT pEid; USHORT Length = 0; pEid = (PEID_STRUCT) pVIE; pMdIeInfo->Len = 0; while ((Length + 2 + (USHORT)pEid->Len) <= LengthVIE) { switch(pEid->Eid) { case IE_FT_MDIE: if (pEid->Len == sizeof(FT_MDIE)) { NdisMoveMemory(&pMdIeInfo->MdId[0], &pEid->Octet[0], FT_MDID_LEN); pMdIeInfo->FtCapPlc.word = pEid->Octet[FT_MDID_LEN]; pMdIeInfo->Len = pEid->Len; } return TRUE; } Length = Length + 2 + pEid->Len; /* Eid[1] + Len[1]+ content[Len] */ pEid = (PEID_STRUCT)((UCHAR*)pEid + 2 + pEid->Len); } return FALSE; } BOOLEAN FT_ExtractGTKSubIe( IN PRTMP_ADAPTER pAd, IN PMAC_TABLE_ENTRY pEntry, IN PFT_FTIE_INFO pFtInfo) { PFT_GTK_KEY_INFO pKeyInfo; UCHAR gtk_len; UINT unwrap_len; PUINT8 pData; UINT8 data_offset = 0; UINT8 key_p[64]; if (pFtInfo->GtkLen < 11) { DBGPRINT_ERR(("%s : The length is invalid\n", __FUNCTION__)); return FALSE; } pData = pFtInfo->GtkSubIE; /* Extract the Key Info field */ pKeyInfo = (PFT_GTK_KEY_INFO)pData; pKeyInfo->word = cpu2le16(pKeyInfo->word); pAd->StaCfg.DefaultKeyId = pKeyInfo->field.KeyId; data_offset += sizeof(FT_GTK_KEY_INFO); DBGPRINT(RT_DEBUG_TRACE, ("%s : key idx(%d) \n", __FUNCTION__, pAd->StaCfg.DefaultKeyId)); /* Extract the Key Length field */ gtk_len = *(pData + data_offset); data_offset += 1; /* Extract the RSC field */ data_offset += 8; /* Decrypt the Key field by AES Key UNWRAP */ AES_Key_Unwrap(pData + data_offset, pFtInfo->GtkLen - data_offset, &pEntry->PTK[LEN_PTK_KCK], LEN_PTK_KEK, key_p, &unwrap_len); /* Compare the GTK length */ if (unwrap_len != gtk_len) { DBGPRINT_ERR(("%s : The GTK length is unmatched\n", __FUNCTION__)); return FALSE; } /* set key material, TxMic and RxMic */ NdisZeroMemory(pAd->StaCfg.GTK, MAX_LEN_GTK); NdisMoveMemory(pAd->StaCfg.GTK, key_p, gtk_len); return TRUE; } /* ======================================================================== Routine Description: Arguments: Return Value: Note: ======================================================================== */ VOID FT_ConstructAuthReqInRsn( IN PRTMP_ADAPTER pAd, IN PUCHAR pFrameBuf, OUT PULONG pFrameLen) { UINT8 FtIeLen = 0; FT_MIC_CTR_FIELD FtMicCtr; UINT8 ft_mic[16]; UINT8 anonce[32]; /* Insert RSNIE[PMKR0Name] */ RTMPInsertRSNIE(pFrameBuf + (*pFrameLen), pFrameLen, pAd->StaCfg.RSN_IE, pAd->StaCfg.RSNIE_Len, pAd->StaCfg.Dot11RCommInfo.PMKR0Name, LEN_PMK_NAME); /* Insert FTIE[SNonce, R0KH-ID] R0KH-ID: Optional parameter - Sub-EID(1 byte)+Len(1 byte)+Data(variable bytes) */ FtIeLen = sizeof(FT_FTIE) + 2 + pAd->StaCfg.Dot11RCommInfo.R0khIdLen; FtMicCtr.word = 0; GenRandom(pAd, pAd->CurrentAddress, pAd->MlmeAux.FtIeInfo.SNonce); NdisZeroMemory(ft_mic, 16); NdisZeroMemory(anonce, 32); FT_InsertFTIE(pAd, pFrameBuf + (*pFrameLen), pFrameLen, FtIeLen, FtMicCtr, ft_mic, anonce, &pAd->MlmeAux.FtIeInfo.SNonce[0]); FT_FTIE_InsertKhIdSubIE(pAd, pFrameBuf + (*pFrameLen), pFrameLen, FT_R0KH_ID, &pAd->StaCfg.Dot11RCommInfo.R0khId[0], pAd->StaCfg.Dot11RCommInfo.R0khIdLen); } #endif /* CONFIG_STA_SUPPORT */ /* ======================================================================== Routine Description: It is used to derive the first level FT Key Hierarchy key, PMK-R0, and its identifier PMKR0Name. (IEEE 802.11r/D9.0, 8.5.1.5.3) Arguments: Return Value: Note: R0-Key-Data = KDF-384(XXKey, "FT-R0", SSIDlength || SSID || MDID || R0KHlength || R0KH-ID || S0KH-ID) PMK-R0 = L(R0-Key-Data, 0, 256) PMK-R0Name-Salt = L(R0-Key-Data, 256, 128) PMKR0Name = Truncate-128(SHA-256("FT-R0N" || PMK-R0Name-Salt)) ======================================================================== */ VOID FT_DerivePMKR0( IN PUINT8 xxkey, IN INT xxkey_len, IN PUINT8 ssid, IN INT ssid_len, IN PUINT8 mdid, IN PUINT8 r0khid, IN INT r0khid_len, IN PUINT8 s0khid, OUT PUINT8 pmkr0, OUT PUINT8 pmkr0_name) { const char label_name[] = "FT-R0N"; UCHAR R0KeyData[96]; UCHAR PmkR0NameSalt[20]; UCHAR temp_result[64]; UCHAR context[128]; UINT c_len=0; /* =========================== */ /* PMK-R0 derivation */ /* =========================== */ /* construct the concatenated context for PMK-R0 */ /* SSIDlength(1 byte) */ /* SSID(0~32 bytes) */ /* MDID(2 bytes) */ /* R0KHlength(1 byte) */ /* R0KH-ID(5~48 bytes) */ /* S0KH-ID(6 bytes) */ hex_dump("xxkey", (PUCHAR)xxkey, xxkey_len); hex_dump("label", (PUCHAR)label_name, sizeof(label_name)); hex_dump("ssid", (PUCHAR)ssid, ssid_len); hex_dump("mdis", (PUCHAR)mdid, 2); hex_dump("r0khid", (PUCHAR)r0khid, r0khid_len); hex_dump("s0khid", (PUCHAR)s0khid, MAC_ADDR_LEN); /* Initial the related context */ NdisZeroMemory(temp_result, 64); NdisZeroMemory(context, 128); c_len = 0; /* concatenate the SSIDlength with a single octet */ context[0] = ssid_len; c_len += 1; /* concatenate the SSID with its length */ NdisMoveMemory(&context[c_len], ssid, ssid_len); c_len += ssid_len; /* concatenate the MDID with 2-octets */ NdisMoveMemory(&context[c_len], mdid, 2); c_len += 2; /* concatenate the R0KHlength with a single octet */ context[c_len] = r0khid_len; c_len += 1; /* concatenate the R0KH-ID with its length */ NdisMoveMemory(&context[c_len], r0khid, r0khid_len); c_len += r0khid_len; /* concatenate the S0KH-ID with its length */ NdisMoveMemory(&context[c_len], s0khid, MAC_ADDR_LEN); c_len += MAC_ADDR_LEN; /* Calculate a 48-bytes key material through FT-KDF */ KDF(xxkey, xxkey_len, (PUINT8)"FT-R0", 5, context, c_len, R0KeyData, 48); /* PMK-R0 key shall be computed as the first 256 bits (bits 0-255) */ /* of the R0-Key-Data. The latter 128 bits of R0-Key-Data shall */ /* be used as the PMK-R0Name-Salt to generate the PMKR0Name. */ NdisMoveMemory(pmkr0, R0KeyData, LEN_PMK); NdisMoveMemory(PmkR0NameSalt, &R0KeyData[32], LEN_PMK_NAME); /* =============================== */ /* PMK-R0-Name derivation */ /* =============================== */ /* Initial the related parameter for PMK-R0-Name derivation */ NdisZeroMemory(temp_result, 64); NdisZeroMemory(context, 128); c_len = 0; /* concatenate the label with its length */ NdisMoveMemory(context, label_name, strlen(label_name)); c_len += strlen(label_name); /* concatenate the PMK-R0Name-Salt with its length */ NdisMoveMemory(&context[c_len], PmkR0NameSalt, LEN_PMK_NAME); c_len += LEN_PMK_NAME; RT_SHA256(context, c_len, temp_result); NdisMoveMemory(pmkr0_name, temp_result, LEN_PMK_NAME); hex_dump("PMK-R0", (UCHAR *)pmkr0, LEN_PMK); hex_dump("PMK-R0-Name", (UCHAR *)pmkr0_name, LEN_PMK_NAME); } /* ======================================================================== Routine Description: It is used to derive the second level FT Key Hierarchy key identifier, PMK-R1-NAME. (IEEE 802.11r/D9.0, 8.5.1.5.4) Arguments: Return Value: Note: PMKR1Name = Truncate-128(SHA-256("FT-R1N" || PMKR0Name || R1KH-ID || S1KH-ID)) ======================================================================== */ VOID FT_DerivePMKR1Name( IN PUINT8 pmkr0_name, IN PUINT8 r1khid, IN PUINT8 s1khid, OUT PUINT8 pmkr1_name) { const char label_name[] = "FT-R1N"; UCHAR temp_result[64]; UCHAR context[128]; UINT c_len = 0; /* =============================== */ /* PMK-R1-Name derivation */ /* =============================== */ /* Initial the related parameter for PMK-R1-Name derivation */ NdisZeroMemory(temp_result, 64); NdisZeroMemory(context, 128); c_len = 0; /* concatenate the label with its length */ NdisMoveMemory(context, label_name, strlen(label_name)); c_len += strlen(label_name); /* concatenate the PMK-R1-Name with 16-octets */ NdisMoveMemory(&context[c_len], pmkr0_name, LEN_PMK_NAME); c_len += LEN_PMK_NAME; /* concatenate the R1KH-ID with 6-octets */ NdisMoveMemory(&context[c_len], r1khid, MAC_ADDR_LEN); c_len += MAC_ADDR_LEN; /* concatenate the S1KH-ID with 6-octets */ NdisMoveMemory(&context[c_len], s1khid, MAC_ADDR_LEN); c_len += MAC_ADDR_LEN; /* derive PMK-R1-Name */ RT_SHA256(context, c_len, temp_result); NdisMoveMemory(pmkr1_name, temp_result, LEN_PMK_NAME); hex_dump("PMK-R1-Name", (UCHAR *)pmkr1_name, LEN_PMK_NAME); } /* ======================================================================== Routine Description: It is used to derive the second level FT Key Hierarchy key, PMK-R1, and its identifier PMKR1Name. (IEEE 802.11r/D9.0, 8.5.1.5.4) Arguments: Return Value: Note: PMK-R1 = KDF-256(PMK-R0, "FT-R1", R1KH-ID || S1KH-ID) PMKR1Name = Truncate-128(SHA-256("FT-R1N" || PMKR0Name || R1KH-ID || S1KH-ID)) ======================================================================== */ VOID FT_DerivePMKR1( IN PUINT8 pmkr0, IN PUINT8 pmkr0_name, IN PUINT8 r1khid, IN PUINT8 s1khid, OUT PUINT8 pmkr1, OUT PUINT8 pmkr1_name) { /*const char label_name[] = "FT-R1N"; */ UCHAR temp_result[64]; UCHAR context[128]; UINT c_len=0; /* =========================== */ /* PMK-R1 derivation */ /* =========================== */ DBGPRINT(RT_DEBUG_TRACE, ("%s:\n", __FUNCTION__)); hex_dump("pmkr0", pmkr0, 32); hex_dump("pmkr0_name", pmkr0_name, LEN_PMK_NAME); hex_dump("r1khid", r1khid, MAC_ADDR_LEN); hex_dump("s1khid", s1khid, MAC_ADDR_LEN); hex_dump("pmkr1", pmkr1, 32); hex_dump("pmkr1_name", pmkr1_name, 16); /* construct the concatenated context for PMK-R1 */ /* R1KH-ID(6 bytes) */ /* S1KH-ID(6 bytes) */ /* Initial the related parameter */ NdisZeroMemory(temp_result, 64); NdisZeroMemory(context, 128); c_len = 0; /* concatenate the R1KH-ID with 6-octets */ NdisMoveMemory(&context[c_len], r1khid, MAC_ADDR_LEN); c_len += MAC_ADDR_LEN; /* concatenate the S1KH-ID with 6-octets */ NdisMoveMemory(&context[c_len], s1khid, MAC_ADDR_LEN); c_len += MAC_ADDR_LEN; /* Calculate a 32-bytes key material through FT-KDF */ KDF(pmkr0, LEN_PMK, (PUINT8)"FT-R1", 5, context, c_len, temp_result, 32); NdisMoveMemory(pmkr1, temp_result, LEN_PMK); /* =============================== */ /* PMK-R1-Name derivation */ /* =============================== */ FT_DerivePMKR1Name(pmkr0_name, r1khid, s1khid, pmkr1_name); hex_dump("PMK-R1", (UCHAR *)pmkr1, LEN_PMK); hex_dump("PMK-R1-Name", (UCHAR *)pmkr1_name, LEN_PMK_NAME); } /* ======================================================================== Routine Description: It is used to derive the third level FT Key Hierarchy key, PTK, and its identifier PTKName. (IEEE 802.11r/D9.0, 8.5.1.5.4) Arguments: Return Value: Note: PTK = KDF-PTKLen(PMK-R1, "FT-PTK", SNonce || ANonce || BSSID || STA-ADDR) PTKName = Truncate-128(SHA-256(PMKR1Name || "FT-PTKN" || SNonce || ANonce || BSSID || STA-ADDR)) ======================================================================== */ VOID FT_DerivePTK( IN PUINT8 pmkr1, IN PUINT8 pmkr1_name, IN PUINT8 a_nonce, IN PUINT8 s_nonce, IN PUINT8 bssid, IN PUINT8 sta_mac, IN UINT key_len, OUT PUINT8 ptk, OUT PUINT8 ptk_name) { const char label_name[] = "FT-PTKN"; UCHAR temp_result[64]; UCHAR context[128]; UINT c_len=0; /* =============================== */ /* PTK derivation */ /* =============================== */ /* construct the concatenated context for PTK */ /* SNonce (32 bytes) */ /* ANonce (32 bytes) */ /* BSSID (6 bytes) */ /* STA-ADDR (6-bytes) */ /* Initial the related parameter */ NdisZeroMemory(temp_result, 64); NdisZeroMemory(context, 128); c_len = 0; /* concatenate the SNonce with 32-octets */ NdisMoveMemory(&context[c_len], s_nonce, LEN_NONCE); c_len += LEN_NONCE; /* concatenate the ANonce with 32-octets */ NdisMoveMemory(&context[c_len], a_nonce, LEN_NONCE); c_len += LEN_NONCE; /* concatenate the BSSID with 6-octets */ NdisMoveMemory(&context[c_len], bssid, MAC_ADDR_LEN); c_len += MAC_ADDR_LEN; /* concatenate the STA-ADDR with 6-octets */ NdisMoveMemory(&context[c_len], sta_mac, MAC_ADDR_LEN); c_len += MAC_ADDR_LEN; /* Calculate a key material through FT-KDF */ KDF(pmkr1, LEN_PMK, (PUINT8)"FT-PTK", 6, context, c_len, temp_result, key_len); NdisMoveMemory(ptk, temp_result, key_len); /* =============================== */ /* PTK-Name derivation */ /* =============================== */ /* Initial the related parameter for PTK-Name derivation */ NdisZeroMemory(temp_result, 64); NdisZeroMemory(context, 128); c_len = 0; /* concatenate the PMK-R1-Name with 16-octets */ NdisMoveMemory(&context[c_len], pmkr1_name, LEN_PMK_NAME); c_len += LEN_PMK_NAME; /* concatenate the label with its length */ NdisMoveMemory(&context[c_len], label_name, strlen(label_name)); c_len += strlen(label_name); /* concatenate the SNonce with 32-octets */ NdisMoveMemory(&context[c_len], s_nonce, LEN_NONCE); c_len += LEN_NONCE; /* concatenate the ANonce with 32-octets */ NdisMoveMemory(&context[c_len], a_nonce, LEN_NONCE); c_len += LEN_NONCE; /* concatenate the BSSID with 6-octets */ NdisMoveMemory(&context[c_len], bssid, MAC_ADDR_LEN); c_len += MAC_ADDR_LEN; /* concatenate the STA-ADDR with 6-octets */ NdisMoveMemory(&context[c_len], sta_mac, MAC_ADDR_LEN); c_len += MAC_ADDR_LEN; /* Derive PTKName */ RT_SHA256(context, c_len, temp_result); NdisMoveMemory(ptk_name, temp_result, LEN_PMK_NAME); /*hex_dump("ANonce", (UCHAR *)a_nonce, LEN_NONCE); */ /*hex_dump("SNonce", (UCHAR *)s_nonce, LEN_NONCE); */ hex_dump("PTK", (UCHAR *)ptk, key_len); hex_dump("PTK-Name", (UCHAR *)ptk_name, LEN_PMK_NAME); } #if 0 VOID FT_TEST(int input) { char key_tmp[] = "abc"; char data_tmp[] = "data"; char hash[32]; char xxkey[32] = { 0x9C, 0x1E, 0x49, 0x18, 0x59, 0x8B, 0x67, 0xB2, 0x53, 0x5D, 0xB9, 0x7D, 0xF6, 0x5D, 0x70, 0x78, 0x26, 0x2D, 0xC8, 0x81, 0xFB, 0x55, 0x5B, 0x31, 0xB4, 0xF3, 0xDB, 0x3A, 0x8A, 0xF2, 0xD7, 0x67 }; char anonce[32] = { 0x31, 0xB4, 0xB5, 0xA6, 0xFE, 0xBE, 0xB3, 0xCB, 0xCA, 0xCA, 0xCB, 0xCE, 0xAE, 0x72, 0xE2, 0xC7, 0x12, 0xB7, 0x34, 0x7B, 0x3D, 0xE3, 0xBE, 0x29, 0x72, 0xE7, 0xAF, 0x1B, 0x7A, 0xBE, 0xE0, 0xA3 }; char snonce[32] = { 0x21, 0xF7, 0x7F, 0xF1, 0xF6, 0x2B, 0x78, 0xE2, 0xDD, 0xC0, 0x3F, 0x24, 0x7B, 0xBC, 0xDF, 0x10, 0x63, 0xA3, 0x25, 0xFB, 0x29, 0x61, 0x44, 0x04, 0x7B, 0xD8, 0x0F, 0x3C, 0x0D, 0xD7, 0xB2, 0x2C }; char ssid[11] = { 0x52, 0x61, 0x6c, 0x69, 0x6e, 0x6b, 0x57, 0x69, 0x46, 0x69, 0x31 }; char mdid[2] = {0x36, 0x34}; char r0kh_id[24] = { 0x30, 0x30, 0x30, 0x43, 0x34, 0x33, 0x31, 0x30, 0x31, 0x32, 0x35, 0x30, 0x00, 0x65, 0x35, 0x3a, 0x39, 0x61, 0x3a, 0x34, 0x38, 0x3a, 0x63, 0x32 }; char sta_addr[6] = {0x00, 0x0F, 0x86, 0x56, 0xA2, 0xB3}; char bssid[6] = {0x00, 0x0c, 0x43, 0x10, 0x12, 0x50}; char s0kh_id[6]; char r1kh_id[6]; char s1kh_id[6]; char pmk_r0[32]; char pmk_r0_name[16]; char pmk_r1[32]; /*= { 0xBE, 0x3F, 0x65, 0xF9, 0x9F, 0x19, 0x51, 0x82, 0xA7, 0x88, 0x53, 0x72, 0xFA, 0x50, 0xE4, 0xBF, 0x52, 0xD9, 0x49, 0x95, 0xD1, 0x90, 0x94, 0xA4, 0xBB, 0x45, 0x7D, 0x78, 0x71, 0x8D, 0x57, 0xCD };*/ char pmk_r1_name[16]; /* = { 0xEE, 0xF9, 0xCB, 0x73, 0x04, 0x48, 0xC3, 0xD3, 0xB4, 0xDF, 0xB8, 0x07, 0x17, 0x69, 0x04, 0x35 };*/ char ptk[48]; char ptk_name[16]; /*KDF(key_tmp, 3, "KDF test", 8, (RTMP_STRING *)data_tmp, 4, hash, sizeof(hash)); */ /*hex_dump("KDF", hash, sizeof(hash)); */ NdisMoveMemory(s0kh_id, sta_addr, sizeof(sta_addr)); NdisMoveMemory(s1kh_id, sta_addr, sizeof(sta_addr)); NdisMoveMemory(r1kh_id, bssid, sizeof(bssid)); FT_DerivePMKR0(xxkey, sizeof(xxkey), ssid, sizeof(ssid), mdid, r0kh_id, sizeof(r0kh_id), s0kh_id, pmk_r0, pmk_r0_name); FT_DerivePMKR1(pmk_r0, pmk_r0_name, r1kh_id, s1kh_id, pmk_r1, pmk_r1_name); FT_DerivePTK(pmk_r1, pmk_r1_name, anonce, snonce, bssid, sta_addr, 48, ptk, ptk_name); } #endif /* ======================================================================== Routine Description: Calcaulate FT MIC. It is used during Fast BSS transition. Arguments: Return Value: Note: It's defined in IEEE 802.11r D9.0 11A.8.4/8.5 The MIC shall be calculated using the KCK and the AES-128-CMAC algorithm. The output of the AES-128-CMAC shall be 128 bits. The MIC shall be calculated on the concatenation, in the following order, of: - non-AP STA MAC address (6 octets) - Target AP MAC address (6 octets) - Transaction sequence number (1 octet) - Contents of the RSN information element. - Contents of the MDIE. - Contents of the FTIE, with the MIC field of the FTIE set to 0. - Contents of the RIC-Request (if present) ======================================================================== */ VOID FT_CalculateMIC( IN PUINT8 sta_addr, IN PUINT8 ap_addr, IN PUINT8 kck, IN UINT8 seq, IN PUINT8 rsnie, IN UINT8 rsnie_len, IN PUINT8 mdie, IN UINT8 mdie_len, IN PUINT8 ftie, IN UINT8 ftie_len, IN PUINT8 ric, IN UINT8 ric_len, OUT PUINT8 mic) { UCHAR *OutBuffer; ULONG FrameLen = 0; ULONG TmpLen = 0; UINT mlen = AES_KEY128_LENGTH; DBGPRINT(RT_DEBUG_TRACE, ("%s\n", __FUNCTION__)); NdisZeroMemory(mic, sizeof(mic)); /* allocate memory for MIC calculation */ os_alloc_mem(NULL, (PUCHAR *)&OutBuffer, 512); if (OutBuffer == NULL) { DBGPRINT(RT_DEBUG_ERROR, ("!!!FT_CalculateMIC: no memory!!!\n")); return; } /* make a header frame for calculating MIC. */ MakeOutgoingFrame(OutBuffer, &TmpLen, MAC_ADDR_LEN, sta_addr, MAC_ADDR_LEN, ap_addr, 1, &seq, END_OF_ARGS); FrameLen += TmpLen; /* concatenate RSNIE */ if (rsnie_len != 0) { MakeOutgoingFrame(OutBuffer + FrameLen, &TmpLen, rsnie_len, rsnie, END_OF_ARGS); FrameLen += TmpLen; } /* concatenate MDIE */ if (mdie_len != 0) { MakeOutgoingFrame(OutBuffer + FrameLen, &TmpLen, mdie_len, mdie, END_OF_ARGS); FrameLen += TmpLen; } /* concatenate FTIE */ if (ftie != 0) { /* The MIC field of the FTIE set to 0 */ NdisZeroMemory(ftie + 4, 16); MakeOutgoingFrame(OutBuffer + FrameLen, &TmpLen, ftie_len, ftie, END_OF_ARGS); FrameLen += TmpLen; } /* concatenate RIC-Request/Response if present */ if (ric != 0) { MakeOutgoingFrame(OutBuffer + FrameLen, &TmpLen, ric_len, ric, END_OF_ARGS); FrameLen += TmpLen; } #if 0 hex_dump("OutBuffer", OutBuffer, FrameLen); #endif /* Calculate MIC */ AES_CMAC(OutBuffer, FrameLen, kck, LEN_PTK_KCK, mic, &mlen); os_free_mem(NULL, OutBuffer); } #ifdef CONFIG_AP_SUPPORT /* ======================================================================== Routine Description: Arguments: Return Value: Note: ======================================================================== */ void FT_rtmp_read_parameters_from_file( IN PRTMP_ADAPTER pAd, RTMP_STRING *tmpbuf, RTMP_STRING *pBuffer) { INT Loop; /* FtSupport */ if(RTMPGetKeyParameter("FtSupport", tmpbuf, 32, pBuffer, TRUE)) { RTMP_STRING *macptr; for (Loop = 0, macptr = rstrtok(tmpbuf,";"); macptr; macptr = rstrtok(NULL,";"), Loop++) { if (Loop >= MAX_MBSSID_NUM(pAd)) break; if(simple_strtol(macptr, 0, 10) != 0) /*Enable */ pAd->ApCfg.MBSSID[Loop].FtCfg.FtCapFlag.Dot11rFtEnable = TRUE; else /*Disable */ pAd->ApCfg.MBSSID[Loop].FtCfg.FtCapFlag.Dot11rFtEnable = FALSE; DBGPRINT(RT_DEBUG_TRACE, ("I/F(ra%d) Dot11rFtEnable=%d\n", Loop, pAd->ApCfg.MBSSID[Loop].FtCfg.FtCapFlag.Dot11rFtEnable)); } } /* FtRic */ if(RTMPGetKeyParameter("FtRic", tmpbuf, 32, pBuffer, TRUE)) { RTMP_STRING *macptr; for (Loop = 0, macptr = rstrtok(tmpbuf,";"); macptr; macptr = rstrtok(NULL,";"), Loop++) { if (Loop >= MAX_MBSSID_NUM(pAd)) break; if(simple_strtol(macptr, 0, 10) != 0) /*Enable */ pAd->ApCfg.MBSSID[Loop].FtCfg.FtCapFlag.RsrReqCap = TRUE; else /*Disable */ pAd->ApCfg.MBSSID[Loop].FtCfg.FtCapFlag.RsrReqCap = FALSE; DBGPRINT(RT_DEBUG_TRACE, ("I/F(ra%d) Dot11rFtRic=%d\n", Loop, pAd->ApCfg.MBSSID[Loop].FtCfg.FtCapFlag.RsrReqCap)); } } /* FtOtd */ if(RTMPGetKeyParameter("FtOtd", tmpbuf, 32, pBuffer, TRUE)) { RTMP_STRING *macptr; for (Loop = 0, macptr = rstrtok(tmpbuf,";"); macptr; macptr = rstrtok(NULL,";"), Loop++) { if (Loop >= MAX_MBSSID_NUM(pAd)) break; if(simple_strtol(macptr, 0, 10) != 0) /*Enable */ pAd->ApCfg.MBSSID[Loop].FtCfg.FtCapFlag.FtOverDs = TRUE; else /*Disable */ pAd->ApCfg.MBSSID[Loop].FtCfg.FtCapFlag.FtOverDs = FALSE; DBGPRINT(RT_DEBUG_TRACE, ("I/F(ra%d) Dot11rFtOtd=%d\n", Loop, pAd->ApCfg.MBSSID[Loop].FtCfg.FtCapFlag.FtOverDs)); } } for (Loop = 0; Loop < MAX_MBSSID_NUM(pAd); Loop++) { RTMP_STRING tok_str[16]; /* FtMdId: FtMdId shall be a value of two octets. */ NdisZeroMemory(tok_str, sizeof(tok_str)); snprintf(tok_str, sizeof(tok_str), "FtMdId%d", Loop + 1); if(RTMPGetKeyParameter(tok_str, tmpbuf, FT_MDID_LEN + 1, pBuffer, FALSE)) { if (strlen(tmpbuf) == FT_MDID_LEN) { NdisMoveMemory(pAd->ApCfg.MBSSID[Loop].FtCfg.FtMdId, tmpbuf, FT_MDID_LEN); DBGPRINT(RT_DEBUG_TRACE, ("%s::FtMdid(%d)=%c%c\n", __FUNCTION__, Loop, pAd->ApCfg.MBSSID[Loop].FtCfg.FtMdId[0], pAd->ApCfg.MBSSID[Loop].FtCfg.FtMdId[1])); } else { DBGPRINT(RT_DEBUG_TRACE, ("%s: Invalid MdId=%s\n", __FUNCTION__,tmpbuf)); } } /* FtR0khId: FtR0khId shall be in string of 1 ~ 48 octets. */ NdisZeroMemory(tok_str, sizeof(tok_str)); snprintf(tok_str, sizeof(tok_str), "FtR0khId%d", Loop + 1); if(RTMPGetKeyParameter(tok_str, tmpbuf, FT_ROKH_ID_LEN + 1, pBuffer, FALSE)) { if (strlen(tmpbuf) <= FT_ROKH_ID_LEN) { NdisMoveMemory(pAd->ApCfg.MBSSID[Loop].FtCfg.FtR0khId, tmpbuf, strlen(tmpbuf)); pAd->ApCfg.MBSSID[Loop].FtCfg.FtR0khId[strlen(tmpbuf)] = '\0'; DBGPRINT(RT_DEBUG_TRACE, ("%s::FtR0khId(%d)=%s\n", __FUNCTION__, Loop, pAd->ApCfg.MBSSID[Loop].FtCfg.FtR0khId)); } else { DBGPRINT(RT_DEBUG_TRACE, ("%s: Invalid R0khId(%d)=%s Len=%d\n", __FUNCTION__, Loop, tmpbuf, strlen(tmpbuf))); } } } } INT Set_FT_Enable(RTMP_ADAPTER *pAd, RTMP_STRING *arg) { POS_COOKIE pObj = (POS_COOKIE)pAd->OS_Cookie; INT apidx = pObj->ioctl_if; PFT_CFG pFtCfg; ULONG Value; Value = (UINT) simple_strtol(arg, 0, 10); if (apidx > pAd->ApCfg.BssidNum) { DBGPRINT(RT_DEBUG_ERROR, ("%s: Invalid interface number (%d).\n", __FUNCTION__, apidx)); return TRUE; } pFtCfg = &pAd->ApCfg.MBSSID[pObj->ioctl_if].FtCfg; pFtCfg->FtCapFlag.Dot11rFtEnable = (Value == 0 ? FALSE : TRUE); return TRUE; } INT Set_FT_Mdid(RTMP_ADAPTER *pAd, RTMP_STRING *arg) { POS_COOKIE pObj = (POS_COOKIE)pAd->OS_Cookie; INT apidx = pObj->ioctl_if; PFT_CFG pFtCfg; if (apidx > pAd->ApCfg.BssidNum) { DBGPRINT(RT_DEBUG_ERROR, ("%s: Invalid interface number (%d).\n", __FUNCTION__, apidx)); return TRUE; } pFtCfg = &pAd->ApCfg.MBSSID[pObj->ioctl_if].FtCfg; NdisMoveMemory(pFtCfg->FtMdId, arg, FT_MDID_LEN); return TRUE; } INT Set_FT_R0khid(RTMP_ADAPTER *pAd, RTMP_STRING *arg) { POS_COOKIE pObj = (POS_COOKIE)pAd->OS_Cookie; INT apidx = pObj->ioctl_if; PFT_CFG pFtCfg; if (apidx > pAd->ApCfg.BssidNum) { DBGPRINT(RT_DEBUG_ERROR, ("%s: Invalid interface number (%d).\n", __FUNCTION__, apidx)); return TRUE; } if (strlen(arg) > FT_ROKH_ID_LEN) { DBGPRINT(RT_DEBUG_ERROR, ("%s: Invalid R0KHID Length (%d).\n", __FUNCTION__, strlen(arg))); DBGPRINT(RT_DEBUG_ERROR, ("%s: The length shall be in range from 1 to 48 octects.\n", __FUNCTION__)); return TRUE; } pFtCfg = &pAd->ApCfg.MBSSID[pObj->ioctl_if].FtCfg; NdisMoveMemory(pFtCfg->FtR0khId, arg, strlen(arg)); pFtCfg->FtR0khIdLen = strlen(arg); return TRUE; } INT Set_FT_RIC(RTMP_ADAPTER *pAd, RTMP_STRING *arg) { POS_COOKIE pObj = (POS_COOKIE)pAd->OS_Cookie; INT apidx = pObj->ioctl_if; PFT_CFG pFtCfg; ULONG Value; Value = (UINT) simple_strtol(arg, 0, 10); if (apidx > pAd->ApCfg.BssidNum) { DBGPRINT(RT_DEBUG_ERROR, ("%s: Invalid interface number (%d).\n", __FUNCTION__, apidx)); return TRUE; } pFtCfg = &pAd->ApCfg.MBSSID[pObj->ioctl_if].FtCfg; pFtCfg->FtCapFlag.RsrReqCap = (Value == 0 ? FALSE : TRUE); return TRUE; } INT Set_FT_OTD(RTMP_ADAPTER *pAd, RTMP_STRING *arg) { POS_COOKIE pObj = (POS_COOKIE)pAd->OS_Cookie; INT apidx = pObj->ioctl_if; PFT_CFG pFtCfg; ULONG Value; Value = (UINT) simple_strtol(arg, 0, 10); if (apidx > pAd->ApCfg.BssidNum) { DBGPRINT(RT_DEBUG_ERROR, ("%s: Invalid interface number (%d).\n", __FUNCTION__, apidx)); return TRUE; } pFtCfg = &pAd->ApCfg.MBSSID[pObj->ioctl_if].FtCfg; pFtCfg->FtCapFlag.FtOverDs = (Value == 0 ? FALSE : TRUE); return TRUE; } INT Show_FTConfig_Proc(RTMP_ADAPTER *pAd, RTMP_STRING *arg) { POS_COOKIE pObj = (POS_COOKIE)pAd->OS_Cookie; INT apidx = pObj->ioctl_if; PFT_CFG pFtCfg; if (apidx >= pAd->ApCfg.BssidNum) return -1; pFtCfg = &pAd->ApCfg.MBSSID[pObj->ioctl_if].FtCfg; DBGPRINT(RT_DEBUG_OFF, ("MDID=%c%c\n", pFtCfg->FtMdId[0], pFtCfg->FtMdId[1])); DBGPRINT(RT_DEBUG_OFF, ("R0KHID=%s, Len=%d\n", pFtCfg->FtR0khId, pFtCfg->FtR0khIdLen)); DBGPRINT(RT_DEBUG_OFF, ("FT Enable=%d\n", pFtCfg->FtCapFlag.Dot11rFtEnable)); DBGPRINT(RT_DEBUG_OFF, ("FT RIC=%d\n", pFtCfg->FtCapFlag.RsrReqCap)); DBGPRINT(RT_DEBUG_OFF, ("FT OTD=%d\n", pFtCfg->FtCapFlag.FtOverDs)); return TRUE; } #endif /* CONFIG_AP_SUPPORT */ #endif /* DOT11R_FT_SUPPORT */