blob: 9a8881a61abad8b4eb231d7c867ba3f1e0cf4f6f [file] [log] [blame]
/*
* Copyright (c) 2012-2018 The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include <sir_common.h>
#include <ani_global.h>
#include <csr_inside_api.h>
#include <csr_neighbor_roam.h>
#include <sir_api.h>
/* Initialize the FT context. */
void sme_ft_open(mac_handle_t mac_handle, uint32_t sessionId)
{
tpAniSirGlobal mac = PMAC_STRUCT(mac_handle);
QDF_STATUS status = QDF_STATUS_SUCCESS;
struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId);
if (NULL != pSession) {
/* Clean up the context */
qdf_mem_set(&pSession->ftSmeContext, sizeof(tftSMEContext), 0);
pSession->ftSmeContext.pUsrCtx =
qdf_mem_malloc(sizeof(tFTRoamCallbackUsrCtx));
if (!pSession->ftSmeContext.pUsrCtx)
return;
pSession->ftSmeContext.pUsrCtx->mac = mac;
pSession->ftSmeContext.pUsrCtx->sessionId = sessionId;
status =
qdf_mc_timer_init(&pSession->ftSmeContext.
preAuthReassocIntvlTimer,
QDF_TIMER_TYPE_SW,
sme_preauth_reassoc_intvl_timer_callback,
(void *)pSession->ftSmeContext.pUsrCtx);
if (QDF_STATUS_SUCCESS != status) {
sme_err("Preauth Reassoc interval Timer allocation failed");
qdf_mem_free(pSession->ftSmeContext.pUsrCtx);
pSession->ftSmeContext.pUsrCtx = NULL;
return;
}
}
}
/* Cleanup the SME FT Global context. */
void sme_ft_close(mac_handle_t mac_handle, uint32_t sessionId)
{
tpAniSirGlobal mac = PMAC_STRUCT(mac_handle);
struct csr_roam_session *pSession = NULL;
/* Clear the FT Context */
sme_ft_reset(mac_handle, sessionId);
pSession = CSR_GET_SESSION(mac, sessionId);
if (NULL != pSession) {
/* check if the timer is running */
if (QDF_TIMER_STATE_RUNNING ==
qdf_mc_timer_get_current_state(&pSession->ftSmeContext.
preAuthReassocIntvlTimer)) {
qdf_mc_timer_stop(&pSession->ftSmeContext.
preAuthReassocIntvlTimer);
}
qdf_mc_timer_destroy(&pSession->ftSmeContext.
preAuthReassocIntvlTimer);
if (pSession->ftSmeContext.pUsrCtx != NULL) {
qdf_mem_free(pSession->ftSmeContext.pUsrCtx);
pSession->ftSmeContext.pUsrCtx = NULL;
}
}
}
void sme_set_ft_pre_auth_state(mac_handle_t mac_handle, uint32_t sessionId,
bool state)
{
tpAniSirGlobal mac = PMAC_STRUCT(mac_handle);
struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId);
if (pSession)
pSession->ftSmeContext.setFTPreAuthState = state;
}
bool sme_get_ft_pre_auth_state(mac_handle_t mac_handle, uint32_t sessionId)
{
tpAniSirGlobal mac = PMAC_STRUCT(mac_handle);
struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId);
if (pSession)
return pSession->ftSmeContext.setFTPreAuthState;
return false;
}
/**
* sme_set_ft_ies() - to set FT IEs
* @mac_handle: opaque handle to the global MAC context
* @session_id: sme session id
* @ft_ies: pointer to FT IEs
* @ft_ies_length: length of FT IEs
*
* Each time the supplicant sends down the FT IEs to the driver. This function
* is called in SME. This function packages and sends the FT IEs to PE.
*
* Return: none
*/
void sme_set_ft_ies(mac_handle_t mac_handle, uint32_t session_id,
const uint8_t *ft_ies, uint16_t ft_ies_length)
{
tpAniSirGlobal mac_ctx = PMAC_STRUCT(mac_handle);
struct csr_roam_session *session = CSR_GET_SESSION(mac_ctx, session_id);
QDF_STATUS status = QDF_STATUS_E_FAILURE;
if (NULL == session || NULL == ft_ies) {
sme_err("ft ies or session is NULL");
return;
}
status = sme_acquire_global_lock(&mac_ctx->sme);
if (!(QDF_IS_STATUS_SUCCESS(status)))
return;
sme_debug("FT IEs Req is received in state %d",
session->ftSmeContext.FTState);
/* Global Station FT State */
switch (session->ftSmeContext.FTState) {
case eFT_START_READY:
case eFT_AUTH_REQ_READY:
sme_debug("ft_ies_length: %d", ft_ies_length);
if ((session->ftSmeContext.auth_ft_ies) &&
(session->ftSmeContext.auth_ft_ies_length)) {
/* Free the one we recvd last from supplicant */
qdf_mem_free(session->ftSmeContext.auth_ft_ies);
session->ftSmeContext.auth_ft_ies_length = 0;
session->ftSmeContext.auth_ft_ies = NULL;
}
ft_ies_length = QDF_MIN(ft_ies_length, MAX_FTIE_SIZE);
/* Save the FT IEs */
session->ftSmeContext.auth_ft_ies =
qdf_mem_malloc(ft_ies_length);
if (!session->ftSmeContext.auth_ft_ies) {
sme_release_global_lock(&mac_ctx->sme);
return;
}
session->ftSmeContext.auth_ft_ies_length = ft_ies_length;
qdf_mem_copy((uint8_t *)session->ftSmeContext.auth_ft_ies,
ft_ies, ft_ies_length);
session->ftSmeContext.FTState = eFT_AUTH_REQ_READY;
break;
case eFT_AUTH_COMPLETE:
/*
* We will need to re-start preauth. If we received FT
* IEs in eFT_PRE_AUTH_DONE state, it implies there was
* a rekey in our pre-auth state. Hence this implies we
* need Pre-auth again. OK now inform SME we have no
* pre-auth list. Delete the pre-auth node locally. Set
* your self back to restart pre-auth
*/
sme_debug("Preauth done & rcving AUTHREQ in state %d",
session->ftSmeContext.FTState);
sme_debug("Unhandled reception of FT IES in state %d",
session->ftSmeContext.FTState);
break;
case eFT_REASSOC_REQ_WAIT:
/*
* We are done with pre-auth, hence now waiting for
* reassoc req. This is the new FT Roaming in place At
* this juncture we'r ready to start sending Reassoc req
*/
ft_ies_length = QDF_MIN(ft_ies_length, MAX_FTIE_SIZE);
sme_debug("New Reassoc Req: %pK in state %d",
ft_ies, session->ftSmeContext.FTState);
if ((session->ftSmeContext.reassoc_ft_ies) &&
(session->ftSmeContext.reassoc_ft_ies_length)) {
/* Free the one we recvd last from supplicant */
qdf_mem_free(session->ftSmeContext.reassoc_ft_ies);
session->ftSmeContext.reassoc_ft_ies_length = 0;
}
/* Save the FT IEs */
session->ftSmeContext.reassoc_ft_ies =
qdf_mem_malloc(ft_ies_length);
if (!session->ftSmeContext.reassoc_ft_ies) {
sme_release_global_lock(&mac_ctx->sme);
return;
}
session->ftSmeContext.reassoc_ft_ies_length =
ft_ies_length;
qdf_mem_copy((uint8_t *)session->ftSmeContext.reassoc_ft_ies,
ft_ies, ft_ies_length);
session->ftSmeContext.FTState = eFT_SET_KEY_WAIT;
sme_debug("ft_ies_length: %d state: %d", ft_ies_length,
session->ftSmeContext.FTState);
break;
default:
sme_warn("Unhandled state: %d", session->ftSmeContext.FTState);
break;
}
sme_release_global_lock(&mac_ctx->sme);
}
/**
* sme_ft_send_update_key_ind() - To send key update indication for FT session
* @mac: pointer to MAC context
* @session_id: sme session id
* @ftkey_info: FT key information
*
* To send key update indication for FT session
*
* Return: QDF_STATUS
*/
static
QDF_STATUS sme_ft_send_update_key_ind(tpAniSirGlobal mac, uint32_t session_id,
tCsrRoamSetKey *ftkey_info)
{
tSirFTUpdateKeyInfo *msg;
uint16_t msglen;
QDF_STATUS status = QDF_STATUS_E_FAILURE;
tSirKeyMaterial *keymaterial = NULL;
tAniEdType ed_type;
sme_debug("keyLength: %d", ftkey_info->keyLength);
if (ftkey_info->keyLength > CSR_MAX_KEY_LEN) {
sme_err("invalid keyLength: %d", ftkey_info->keyLength);
return QDF_STATUS_E_FAILURE;
}
msglen = sizeof(tSirFTUpdateKeyInfo);
msg = qdf_mem_malloc(msglen);
if (!msg)
return QDF_STATUS_E_NOMEM;
msg->messageType = eWNI_SME_FT_UPDATE_KEY;
msg->length = msglen;
keymaterial = &msg->keyMaterial;
keymaterial->length = ftkey_info->keyLength;
ed_type = csr_translate_encrypt_type_to_ed_type(ftkey_info->encType);
keymaterial->edType = ed_type;
keymaterial->numKeys = 1;
keymaterial->key[0].keyId = ftkey_info->keyId;
keymaterial->key[0].unicast = (uint8_t) true;
keymaterial->key[0].keyDirection = ftkey_info->keyDirection;
qdf_mem_copy(&keymaterial->key[0].keyRsc,
ftkey_info->keyRsc, CSR_MAX_RSC_LEN);
keymaterial->key[0].paeRole = ftkey_info->paeRole;
keymaterial->key[0].keyLength = ftkey_info->keyLength;
if (ftkey_info->keyLength)
qdf_mem_copy(&keymaterial->key[0].key, ftkey_info->Key,
ftkey_info->keyLength);
qdf_copy_macaddr(&msg->bssid, &ftkey_info->peerMac);
msg->smeSessionId = session_id;
sme_debug("BSSID = " MAC_ADDRESS_STR, MAC_ADDR_ARRAY(msg->bssid.bytes));
status = umac_send_mb_message_to_mac(msg);
return status;
}
bool sme_get_ftptk_state(mac_handle_t mac_handle, uint32_t sessionId)
{
tpAniSirGlobal mac = PMAC_STRUCT(mac_handle);
struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId);
if (!pSession) {
sme_err("pSession is NULL");
return false;
}
return pSession->ftSmeContext.setFTPTKState;
}
void sme_set_ftptk_state(mac_handle_t mac_handle, uint32_t sessionId,
bool state)
{
tpAniSirGlobal mac = PMAC_STRUCT(mac_handle);
struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId);
if (!pSession) {
sme_err("pSession is NULL");
return;
}
pSession->ftSmeContext.setFTPTKState = state;
}
QDF_STATUS sme_ft_update_key(mac_handle_t mac_handle, uint32_t sessionId,
tCsrRoamSetKey *pFTKeyInfo)
{
tpAniSirGlobal mac = PMAC_STRUCT(mac_handle);
struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId);
QDF_STATUS status = QDF_STATUS_E_FAILURE;
if (!pSession) {
sme_err("pSession is NULL");
return QDF_STATUS_E_FAILURE;
}
if (pFTKeyInfo == NULL) {
sme_err("pFTKeyInfo is NULL");
return QDF_STATUS_E_FAILURE;
}
status = sme_acquire_global_lock(&mac->sme);
if (!(QDF_IS_STATUS_SUCCESS(status)))
return QDF_STATUS_E_FAILURE;
sme_debug("FT update key is received in state %d",
pSession->ftSmeContext.FTState);
/* Global Station FT State */
switch (pSession->ftSmeContext.FTState) {
case eFT_SET_KEY_WAIT:
if (sme_get_ft_pre_auth_state(mac_handle, sessionId) == true) {
status = sme_ft_send_update_key_ind(mac, sessionId,
pFTKeyInfo);
if (status != 0) {
sme_err("Key set failure: %d", status);
pSession->ftSmeContext.setFTPTKState = false;
status = QDF_STATUS_FT_PREAUTH_KEY_FAILED;
} else {
pSession->ftSmeContext.setFTPTKState = true;
status = QDF_STATUS_FT_PREAUTH_KEY_SUCCESS;
sme_debug("Key set success");
}
sme_set_ft_pre_auth_state(mac_handle, sessionId, false);
}
pSession->ftSmeContext.FTState = eFT_START_READY;
sme_debug("state changed to %d status %d",
pSession->ftSmeContext.FTState, status);
break;
default:
sme_debug("Unhandled state:%d", pSession->ftSmeContext.FTState);
status = QDF_STATUS_E_FAILURE;
break;
}
sme_release_global_lock(&mac->sme);
return status;
}
/*
* HDD Interface to SME. SME now sends the Auth 2 and RIC IEs up to the
* supplicant. The supplicant will then proceed to send down the
* Reassoc Req.
*/
void sme_get_ft_pre_auth_response(mac_handle_t mac_handle, uint32_t sessionId,
uint8_t *ft_ies, uint32_t ft_ies_ip_len,
uint16_t *ft_ies_length)
{
tpAniSirGlobal mac = PMAC_STRUCT(mac_handle);
struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId);
QDF_STATUS status = QDF_STATUS_E_FAILURE;
if (!pSession) {
sme_err("pSession is NULL");
return;
}
*ft_ies_length = 0;
status = sme_acquire_global_lock(&mac->sme);
if (!(QDF_IS_STATUS_SUCCESS(status)))
return;
/* All or nothing - proceed only if both BSSID and FT IE fit */
if ((QDF_MAC_ADDR_SIZE +
pSession->ftSmeContext.psavedFTPreAuthRsp->ft_ies_length) >
ft_ies_ip_len) {
sme_release_global_lock(&mac->sme);
return;
}
/* hdd needs to pack the bssid also along with the */
/* auth response to supplicant */
qdf_mem_copy(ft_ies, pSession->ftSmeContext.preAuthbssId,
QDF_MAC_ADDR_SIZE);
/* Copy the auth resp FTIEs */
qdf_mem_copy(&(ft_ies[QDF_MAC_ADDR_SIZE]),
pSession->ftSmeContext.psavedFTPreAuthRsp->ft_ies,
pSession->ftSmeContext.psavedFTPreAuthRsp->ft_ies_length);
*ft_ies_length = QDF_MAC_ADDR_SIZE +
pSession->ftSmeContext.psavedFTPreAuthRsp->ft_ies_length;
pSession->ftSmeContext.FTState = eFT_REASSOC_REQ_WAIT;
sme_debug("Filled auth resp: %d", *ft_ies_length);
sme_release_global_lock(&mac->sme);
}
/*
* SME now sends the RIC IEs up to the supplicant.
* The supplicant will then proceed to send down the
* Reassoc Req.
*/
void sme_get_rici_es(mac_handle_t mac_handle, uint32_t sessionId,
uint8_t *ric_ies,
uint32_t ric_ies_ip_len, uint32_t *ric_ies_length)
{
tpAniSirGlobal mac = PMAC_STRUCT(mac_handle);
struct csr_roam_session *pSession = CSR_GET_SESSION(mac, sessionId);
QDF_STATUS status = QDF_STATUS_E_FAILURE;
if (!pSession) {
sme_err("pSession is NULL");
return;
}
*ric_ies_length = 0;
status = sme_acquire_global_lock(&mac->sme);
if (!(QDF_IS_STATUS_SUCCESS(status)))
return;
/* All or nothing */
if (pSession->ftSmeContext.psavedFTPreAuthRsp->ric_ies_length >
ric_ies_ip_len) {
sme_release_global_lock(&mac->sme);
return;
}
qdf_mem_copy(ric_ies,
pSession->ftSmeContext.psavedFTPreAuthRsp->ric_ies,
pSession->ftSmeContext.psavedFTPreAuthRsp->ric_ies_length);
*ric_ies_length =
pSession->ftSmeContext.psavedFTPreAuthRsp->ric_ies_length;
sme_debug("Filled ric ies: %d", *ric_ies_length);
sme_release_global_lock(&mac->sme);
}
/*
* Timer callback for the timer that is started between the preauth completion
* and reassoc request to the PE. In this interval, it is expected that the
* pre-auth response and RIC IEs are passed up to the WPA supplicant and
* received back the necessary FTIEs required to be sent in the reassoc request
*/
void sme_preauth_reassoc_intvl_timer_callback(void *context)
{
tFTRoamCallbackUsrCtx *pUsrCtx = (tFTRoamCallbackUsrCtx *) context;
if (pUsrCtx)
csr_neighbor_roam_request_handoff(pUsrCtx->mac,
pUsrCtx->sessionId);
}
/* Reset the FT context. */
void sme_ft_reset(mac_handle_t mac_handle, uint32_t sessionId)
{
tpAniSirGlobal mac = PMAC_STRUCT(mac_handle);
struct csr_roam_session *pSession = NULL;
if (mac == NULL) {
QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR,
FL("mac is NULL"));
return;
}
pSession = CSR_GET_SESSION(mac, sessionId);
if (NULL != pSession) {
if (pSession->ftSmeContext.auth_ft_ies != NULL) {
qdf_mem_free(pSession->ftSmeContext.auth_ft_ies);
pSession->ftSmeContext.auth_ft_ies = NULL;
}
pSession->ftSmeContext.auth_ft_ies_length = 0;
if (pSession->ftSmeContext.reassoc_ft_ies != NULL) {
qdf_mem_free(pSession->ftSmeContext.reassoc_ft_ies);
pSession->ftSmeContext.reassoc_ft_ies = NULL;
}
pSession->ftSmeContext.reassoc_ft_ies_length = 0;
if (pSession->ftSmeContext.psavedFTPreAuthRsp != NULL) {
qdf_mem_free(pSession->ftSmeContext.psavedFTPreAuthRsp);
pSession->ftSmeContext.psavedFTPreAuthRsp = NULL;
}
pSession->ftSmeContext.setFTPreAuthState = false;
pSession->ftSmeContext.setFTPTKState = false;
qdf_mem_zero(pSession->ftSmeContext.preAuthbssId,
QDF_MAC_ADDR_SIZE);
pSession->ftSmeContext.FTState = eFT_START_READY;
}
}
/* End of File */