/*
 * Copyright (c) 2011-2017 The Linux Foundation. All rights reserved.
 *
 * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
 *
 *
 * 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.
 */

/*
 * This file was originally distributed by Qualcomm Atheros, Inc.
 * under proprietary terms before Copyright ownership was assigned
 * to the Linux Foundation.
 */

/*
 *
 * This file lim_process_auth_frame.cc contains the code
 * for processing received Authentication Frame.
 * Author:        Chandra Modumudi
 * Date:          03/11/02
 * History:-
 * Date           Modified by    Modification Information
 * --------------------------------------------------------------------
 * 05/12/2010     js             To support Shared key authentication at AP side
 *
 */

#include "wni_api.h"
#include "wni_cfg.h"
#include "ani_global.h"
#include "cfg_api.h"

#include "utils_api.h"
#include "lim_utils.h"
#include "lim_assoc_utils.h"
#include "lim_security_utils.h"
#include "lim_ser_des_utils.h"
#include "lim_ft.h"
#include "cds_utils.h"

/**
 * is_auth_valid
 *
 ***FUNCTION:
 * This function is called by lim_process_auth_frame() upon Authentication
 * frame reception.
 *
 ***LOGIC:
 * This function is used to test validity of auth frame:
 * - AUTH1 and AUTH3 must be received in AP mode
 * - AUTH2 and AUTH4 must be received in STA mode
 * - AUTH3 and AUTH4 must have challenge text IE, that is,'type' field has been set to
 *                 SIR_MAC_CHALLENGE_TEXT_EID by parser
 * -
 *
 ***ASSUMPTIONS:
 *
 ***NOTE:
 *
 * @param  *auth - Pointer to extracted auth frame body
 *
 * @return 0 or 1 (Valid)
 */

static inline unsigned int is_auth_valid(tpAniSirGlobal pMac,
					 tpSirMacAuthFrameBody auth,
					 tpPESession sessionEntry)
{
	unsigned int valid = 1;

	if (((auth->authTransactionSeqNumber == SIR_MAC_AUTH_FRAME_1) ||
	    (auth->authTransactionSeqNumber == SIR_MAC_AUTH_FRAME_3)) &&
	    (LIM_IS_STA_ROLE(sessionEntry)))
		valid = 0;

	if (((auth->authTransactionSeqNumber == SIR_MAC_AUTH_FRAME_2) ||
	    (auth->authTransactionSeqNumber == SIR_MAC_AUTH_FRAME_4)) &&
	    (LIM_IS_AP_ROLE(sessionEntry)))
		valid = 0;

	if (((auth->authTransactionSeqNumber == SIR_MAC_AUTH_FRAME_3) ||
	    (auth->authTransactionSeqNumber == SIR_MAC_AUTH_FRAME_4)) &&
	    (auth->type != SIR_MAC_CHALLENGE_TEXT_EID) &&
	    (auth->authAlgoNumber != eSIR_SHARED_KEY))
		valid = 0;

	return valid;
}

static void lim_process_auth_shared_system_algo(tpAniSirGlobal mac_ctx,
		tpSirMacMgmtHdr mac_hdr,
		tSirMacAuthFrameBody *rx_auth_frm_body,
		tSirMacAuthFrameBody *auth_frame,
		uint8_t *challenge_txt_arr,
		tpPESession pe_session)
{
	uint32_t val;
	uint8_t cfg_privacy_opt_imp, *challenge;
	struct tLimPreAuthNode *auth_node;

	pe_debug("=======> eSIR_SHARED_KEY");
	if (LIM_IS_AP_ROLE(pe_session))
		val = pe_session->privacy;
	else if (wlan_cfg_get_int(mac_ctx,
			WNI_CFG_PRIVACY_ENABLED, &val) != eSIR_SUCCESS)
		pe_warn("couldnt retrieve Privacy option");
	cfg_privacy_opt_imp = (uint8_t) val;
	if (!cfg_privacy_opt_imp) {
		pe_err("rx Auth frame for unsupported auth algorithm %d "
			MAC_ADDRESS_STR,
			rx_auth_frm_body->authAlgoNumber,
			MAC_ADDR_ARRAY(mac_hdr->sa));

		/*
		 * Authenticator does not have WEP
		 * implemented.
		 * Reject by sending Authentication frame
		 * with Auth algorithm not supported status
		 * code.
		 */
		auth_frame->authAlgoNumber = rx_auth_frm_body->authAlgoNumber;
		auth_frame->authTransactionSeqNumber =
			rx_auth_frm_body->authTransactionSeqNumber + 1;
		auth_frame->authStatusCode =
			eSIR_MAC_AUTH_ALGO_NOT_SUPPORTED_STATUS;

		lim_send_auth_mgmt_frame(mac_ctx, auth_frame,
				mac_hdr->sa, LIM_NO_WEP_IN_FC,
				pe_session, false);
		return;
	} else {
		/* Create entry for this STA in pre-auth list */
		auth_node = lim_acquire_free_pre_auth_node(mac_ctx,
					&mac_ctx->lim.gLimPreAuthTimerTable);
		if (auth_node == NULL) {
			pe_warn("Max preauth-nodes reached");
			lim_print_mac_addr(mac_ctx, mac_hdr->sa, LOGW);
			return;
		}

		qdf_mem_copy((uint8_t *) auth_node->peerMacAddr, mac_hdr->sa,
				sizeof(tSirMacAddr));
		auth_node->mlmState = eLIM_MLM_WT_AUTH_FRAME3_STATE;
		auth_node->authType =
			(tAniAuthType) rx_auth_frm_body->authAlgoNumber;
		auth_node->fSeen = 0;
		auth_node->fTimerStarted = 0;
		auth_node->seq_num = ((mac_hdr->seqControl.seqNumHi << 4) |
						(mac_hdr->seqControl.seqNumLo));
		auth_node->timestamp = qdf_mc_timer_get_system_ticks();
		lim_add_pre_auth_node(mac_ctx, auth_node);

		pe_debug("Alloc new data: %p id: %d peer ",
			auth_node, auth_node->authNodeIdx);
		lim_print_mac_addr(mac_ctx, mac_hdr->sa, LOG1);
		/* / Create and activate Auth Response timer */
		if (tx_timer_change_context(&auth_node->timer,
				auth_node->authNodeIdx) != TX_SUCCESS) {
			/* Could not start Auth response timer. Log error */
			pe_warn("Unable to chg context auth response timer for peer");
			lim_print_mac_addr(mac_ctx, mac_hdr->sa, LOGW);

			/*
			 * Send Auth frame with unspecified failure status code.
			 */

			auth_frame->authAlgoNumber =
				rx_auth_frm_body->authAlgoNumber;
			auth_frame->authTransactionSeqNumber =
				rx_auth_frm_body->authTransactionSeqNumber + 1;
			auth_frame->authStatusCode =
				eSIR_MAC_UNSPEC_FAILURE_STATUS;

			lim_send_auth_mgmt_frame(mac_ctx, auth_frame,
				mac_hdr->sa, LIM_NO_WEP_IN_FC,
				pe_session, false);
			lim_delete_pre_auth_node(mac_ctx, mac_hdr->sa);
			return;
		}
		lim_activate_auth_rsp_timer(mac_ctx, auth_node);
		auth_node->fTimerStarted = 1;
		/*
		 * get random bytes and use as challenge text.
		 * If it fails we already have random stack bytes.
		 */
		if (!QDF_IS_STATUS_SUCCESS(cds_rand_get_bytes(0,
				(uint8_t *) challenge_txt_arr,
				SIR_MAC_AUTH_CHALLENGE_LENGTH)))
			pe_err("Challenge text preparation failed");
		challenge = auth_node->challengeText;
		qdf_mem_copy(challenge, (uint8_t *)challenge_txt_arr,
				sizeof(challenge_txt_arr));
		/*
		 * Sending Authenticaton frame with challenge.
		 */
		auth_frame->authAlgoNumber = rx_auth_frm_body->authAlgoNumber;
		auth_frame->authTransactionSeqNumber =
			rx_auth_frm_body->authTransactionSeqNumber + 1;
		auth_frame->authStatusCode = eSIR_MAC_SUCCESS_STATUS;
		auth_frame->type = SIR_MAC_CHALLENGE_TEXT_EID;
		auth_frame->length = SIR_MAC_AUTH_CHALLENGE_LENGTH;
		qdf_mem_copy(auth_frame->challengeText,
				auth_node->challengeText,
				SIR_MAC_AUTH_CHALLENGE_LENGTH);
		lim_send_auth_mgmt_frame(mac_ctx, auth_frame,
				mac_hdr->sa, LIM_NO_WEP_IN_FC,
				pe_session, false);
	}
}

static void lim_process_auth_open_system_algo(tpAniSirGlobal mac_ctx,
		tpSirMacMgmtHdr mac_hdr,
		tSirMacAuthFrameBody *rx_auth_frm_body,
		tSirMacAuthFrameBody *auth_frame,
		tpPESession pe_session)
{
	struct tLimPreAuthNode *auth_node;

	pe_debug("=======> eSIR_OPEN_SYSTEM");
	/* Create entry for this STA in pre-auth list */
	auth_node = lim_acquire_free_pre_auth_node(mac_ctx,
				&mac_ctx->lim.gLimPreAuthTimerTable);
	if (auth_node == NULL) {
		pe_warn("Max pre-auth nodes reached ");
		lim_print_mac_addr(mac_ctx, mac_hdr->sa, LOGW);
		return;
	}
	pe_debug("Alloc new data: %p peer", auth_node);
	lim_print_mac_addr(mac_ctx, mac_hdr->sa, LOGD);
	qdf_mem_copy((uint8_t *) auth_node->peerMacAddr,
			mac_hdr->sa, sizeof(tSirMacAddr));
	auth_node->mlmState = eLIM_MLM_AUTHENTICATED_STATE;
	auth_node->authType = (tAniAuthType) rx_auth_frm_body->authAlgoNumber;
	auth_node->fSeen = 0;
	auth_node->fTimerStarted = 0;
	auth_node->seq_num = ((mac_hdr->seqControl.seqNumHi << 4) |
				(mac_hdr->seqControl.seqNumLo));
	auth_node->timestamp = qdf_mc_timer_get_system_ticks();
	lim_add_pre_auth_node(mac_ctx, auth_node);
	/*
	 * Send Authenticaton frame with Success
	 * status code.
	 */
	auth_frame->authAlgoNumber = rx_auth_frm_body->authAlgoNumber;
	auth_frame->authTransactionSeqNumber =
			rx_auth_frm_body->authTransactionSeqNumber + 1;
	auth_frame->authStatusCode = eSIR_MAC_SUCCESS_STATUS;
	lim_send_auth_mgmt_frame(mac_ctx, auth_frame, mac_hdr->sa,
					LIM_NO_WEP_IN_FC,
					pe_session, false);
}

static void lim_process_auth_frame_type1(tpAniSirGlobal mac_ctx,
		tpSirMacMgmtHdr mac_hdr,
		tSirMacAuthFrameBody *rx_auth_frm_body,
		uint8_t *rx_pkt_info, uint16_t curr_seq_num,
		tSirMacAuthFrameBody *auth_frame, tpPESession pe_session)
{
	tpDphHashNode sta_ds_ptr = NULL;
	struct tLimPreAuthNode *auth_node;
	uint8_t challenge_txt_arr[SIR_MAC_AUTH_CHALLENGE_LENGTH];
	uint32_t maxnum_preauth;
	uint16_t associd = 0;

	/* AuthFrame 1 */
	sta_ds_ptr = dph_lookup_hash_entry(mac_ctx, mac_hdr->sa,
				&associd, &pe_session->dph.dphHashTable);
	if (sta_ds_ptr) {
		tLimMlmDisassocReq *pMlmDisassocReq = NULL;
		tLimMlmDeauthReq *pMlmDeauthReq = NULL;
		bool isConnected = true;

		pMlmDisassocReq =
			mac_ctx->lim.limDisassocDeauthCnfReq.pMlmDisassocReq;
		if (pMlmDisassocReq &&
			(!qdf_mem_cmp((uint8_t *) mac_hdr->sa, (uint8_t *)
				&pMlmDisassocReq->peer_macaddr.bytes,
				QDF_MAC_ADDR_SIZE))) {
			pe_debug("TODO:Ack for disassoc frame is pending Issue delsta for "
				MAC_ADDRESS_STR,
				MAC_ADDR_ARRAY(
					pMlmDisassocReq->peer_macaddr.bytes));
			lim_process_disassoc_ack_timeout(mac_ctx);
			isConnected = false;
		}
		pMlmDeauthReq =
			mac_ctx->lim.limDisassocDeauthCnfReq.pMlmDeauthReq;
		if (pMlmDeauthReq &&
			(!qdf_mem_cmp((uint8_t *) mac_hdr->sa, (uint8_t *)
				&pMlmDeauthReq->peer_macaddr.bytes,
				QDF_MAC_ADDR_SIZE))) {
			pe_debug("TODO:Ack for deauth frame is pending Issue delsta for "
				MAC_ADDRESS_STR,
				MAC_ADDR_ARRAY(
					pMlmDeauthReq->peer_macaddr.bytes));
			lim_process_deauth_ack_timeout(mac_ctx);
			isConnected = false;
		}

		/*
		 * pStaDS != NULL and isConnected = 1 means the STA is already
		 * connected, But SAP received the Auth from that station.
		 * For non PMF connection send Deauth frame as STA will retry
		 * to connect back.
		 *
		 * For PMF connection the AP should not tear down or otherwise
		 * modify the state of the existing association until the
		 * SA-Query procedure determines that the original SA is
		 * invalid.
		 */
		if (isConnected
#ifdef WLAN_FEATURE_11W
			&& !sta_ds_ptr->rmfEnabled
#endif
		   ) {
			pe_err("STA is already connected but received auth frame"
					"Send the Deauth and lim Delete Station Context"
					"(staId: %d, associd: %d) ",
				sta_ds_ptr->staIndex, associd);
			lim_send_deauth_mgmt_frame(mac_ctx,
				eSIR_MAC_UNSPEC_FAILURE_REASON,
				(uint8_t *) mac_hdr->sa,
				pe_session, false);
			lim_trigger_sta_deletion(mac_ctx, sta_ds_ptr,
				pe_session);
			return;
		}
	}
	/* Check if there exists pre-auth context for this STA */
	auth_node = lim_search_pre_auth_list(mac_ctx, mac_hdr->sa);
	if (auth_node) {
		/* Pre-auth context exists for the STA */
		if (!(mac_hdr->fc.retry == 0 ||
					auth_node->seq_num != curr_seq_num)) {
			/*
			 * This can happen when first authentication frame is
			 * received but ACK lost at STA side, in this case 2nd
			 * auth frame is already in transmission queue
			 */
			pe_warn("STA is initiating Auth after ACK lost");
			return;
		}
		/*
		 * STA is initiating brand-new Authentication
		 * sequence after local Auth Response timeout Or STA
		 * retrying to transmit First Auth frame due to packet
		 * drop OTA Delete Pre-auth node and fall through.
		 */
		if (auth_node->fTimerStarted)
			lim_deactivate_and_change_per_sta_id_timer(
					mac_ctx, eLIM_AUTH_RSP_TIMER,
					auth_node->authNodeIdx);
		pe_debug("STA is initiating brand-new Auth");
		lim_delete_pre_auth_node(mac_ctx, mac_hdr->sa);
		/*
		 *  SAP Mode:Disassociate the station and
		 *  delete its entry if we have its entry
		 *  already and received "auth" from the
		 *  same station.
		 *  SAP dphHashTable.size = 8
		 */
		for (associd = 0; associd < pe_session->dph.dphHashTable.size;
			associd++) {
			sta_ds_ptr = dph_get_hash_entry(mac_ctx, associd,
						&pe_session->dph.dphHashTable);
			if (NULL == sta_ds_ptr)
				continue;
			if (sta_ds_ptr->valid && (!qdf_mem_cmp(
					(uint8_t *)&sta_ds_ptr->staAddr,
					(uint8_t *) &(mac_hdr->sa),
					(uint8_t) sizeof(tSirMacAddr))))
				break;
			sta_ds_ptr = NULL;
		}

		if (NULL != sta_ds_ptr
#ifdef WLAN_FEATURE_11W
			&& !sta_ds_ptr->rmfEnabled
#endif
		   ) {
			pe_debug("lim Delete Station Context staId: %d associd: %d",
				sta_ds_ptr->staIndex, associd);
			lim_send_deauth_mgmt_frame(mac_ctx,
				eSIR_MAC_UNSPEC_FAILURE_REASON,
				(uint8_t *)auth_node->peerMacAddr,
				pe_session, false);
			lim_trigger_sta_deletion(mac_ctx, sta_ds_ptr,
				pe_session);
			return;
		}
	}
	if (wlan_cfg_get_int(mac_ctx, WNI_CFG_MAX_NUM_PRE_AUTH,
				(uint32_t *) &maxnum_preauth) != eSIR_SUCCESS)
		pe_warn("could not retrieve MaxNumPreAuth");

	if (mac_ctx->lim.gLimNumPreAuthContexts == maxnum_preauth &&
			!lim_delete_open_auth_pre_auth_node(mac_ctx)) {
		pe_err("Max no of preauth context reached");
		/*
		 * Maximum number of pre-auth contexts reached.
		 * Send Authentication frame with unspecified failure
		 */
		auth_frame->authAlgoNumber = rx_auth_frm_body->authAlgoNumber;
		auth_frame->authTransactionSeqNumber =
			rx_auth_frm_body->authTransactionSeqNumber + 1;
		auth_frame->authStatusCode =
			eSIR_MAC_UNSPEC_FAILURE_STATUS;

		lim_send_auth_mgmt_frame(mac_ctx, auth_frame,
				mac_hdr->sa, LIM_NO_WEP_IN_FC,
				pe_session, false);
		return;
	}
	/* No Pre-auth context exists for the STA. */
	if (lim_is_auth_algo_supported(mac_ctx,
			(tAniAuthType) rx_auth_frm_body->authAlgoNumber,
			pe_session)) {

		if (lim_get_session_by_macaddr(mac_ctx, mac_hdr->sa)) {

			auth_frame->authAlgoNumber =
				rx_auth_frm_body->authAlgoNumber;
			auth_frame->authTransactionSeqNumber =
				rx_auth_frm_body->authTransactionSeqNumber + 1;
			auth_frame->authStatusCode =
				eSIR_MAC_WME_INVALID_PARAMS_STATUS;

			lim_send_auth_mgmt_frame(mac_ctx, auth_frame,
				mac_hdr->sa, LIM_NO_WEP_IN_FC,
				pe_session, false);
			return;
		}

		switch (rx_auth_frm_body->authAlgoNumber) {
		case eSIR_OPEN_SYSTEM:
			lim_process_auth_open_system_algo(mac_ctx, mac_hdr,
				rx_auth_frm_body, auth_frame, pe_session);
			break;

		case eSIR_SHARED_KEY:
			lim_process_auth_shared_system_algo(mac_ctx, mac_hdr,
				rx_auth_frm_body, auth_frame,
				challenge_txt_arr, pe_session);
			break;
		default:
			pe_err("rx Auth frm for unsupported auth algo %d "
				MAC_ADDRESS_STR,
				rx_auth_frm_body->authAlgoNumber,
				MAC_ADDR_ARRAY(mac_hdr->sa));

			/*
			 * Responding party does not support the
			 * authentication algorithm requested by
			 * sending party.
			 * Reject by sending Authentication frame
			 * with auth algorithm not supported status code
			 */
			auth_frame->authAlgoNumber =
				rx_auth_frm_body->authAlgoNumber;
			auth_frame->authTransactionSeqNumber =
				rx_auth_frm_body->authTransactionSeqNumber + 1;
			auth_frame->authStatusCode =
				eSIR_MAC_AUTH_ALGO_NOT_SUPPORTED_STATUS;
			lim_send_auth_mgmt_frame(mac_ctx, auth_frame,
				mac_hdr->sa, LIM_NO_WEP_IN_FC,
				pe_session, false);
			return;
		}
	} else {
		pe_err("received Authentication frame for unsupported auth algorithm %d "
			MAC_ADDRESS_STR,
			rx_auth_frm_body->authAlgoNumber,
			MAC_ADDR_ARRAY(mac_hdr->sa));

		/*
		 * Responding party does not support the
		 * authentication algorithm requested by sending party.
		 * Reject Authentication with StatusCode=13.
		 */
		auth_frame->authAlgoNumber = rx_auth_frm_body->authAlgoNumber;
		auth_frame->authTransactionSeqNumber =
			rx_auth_frm_body->authTransactionSeqNumber + 1;
		auth_frame->authStatusCode =
			eSIR_MAC_AUTH_ALGO_NOT_SUPPORTED_STATUS;

		lim_send_auth_mgmt_frame(mac_ctx, auth_frame,
				mac_hdr->sa,
				LIM_NO_WEP_IN_FC,
				pe_session, false);
		return;
	}
}

static void lim_process_auth_frame_type2(tpAniSirGlobal mac_ctx,
		tpSirMacMgmtHdr mac_hdr,
		tSirMacAuthFrameBody *rx_auth_frm_body,
		tSirMacAuthFrameBody *auth_frame,
		uint8_t *plainbody,
		uint8_t *body_ptr, uint16_t frame_len,
		tpPESession pe_session)
{
	uint8_t key_id, cfg_privacy_opt_imp;
	uint32_t val, key_length = 8;
	uint8_t defaultkey[SIR_MAC_KEY_LENGTH];
	struct tLimPreAuthNode *auth_node;
	uint8_t encr_auth_frame[LIM_ENCR_AUTH_BODY_LEN];

	/* AuthFrame 2 */
	if (pe_session->limMlmState != eLIM_MLM_WT_AUTH_FRAME2_STATE) {
		/**
		 * Check if a Reassociation is in progress and this is a
		 * Pre-Auth frame
		 */
		if (LIM_IS_STA_ROLE(pe_session) &&
		    (pe_session->limSmeState == eLIM_SME_WT_REASSOC_STATE) &&
		    (rx_auth_frm_body->authStatusCode ==
				eSIR_MAC_SUCCESS_STATUS) &&
		    (pe_session->ftPEContext.pFTPreAuthReq != NULL) &&
		    (!qdf_mem_cmp(
			pe_session->ftPEContext.pFTPreAuthReq->preAuthbssId,
			mac_hdr->sa, sizeof(tSirMacAddr)))) {

			/* Update the FTIEs in the saved auth response */
			pe_warn("rx PreAuth frm2 in smestate: %d from: %pM",
				pe_session->limSmeState, mac_hdr->sa);
			pe_session->ftPEContext.saved_auth_rsp_length = 0;

			if ((body_ptr != NULL) && (frame_len < MAX_FTIE_SIZE)) {
				qdf_mem_copy(
					pe_session->ftPEContext.saved_auth_rsp,
					body_ptr, frame_len);
				pe_session->ftPEContext.saved_auth_rsp_length =
					frame_len;
			}
		} else {
			/*
			 * Received Auth frame2 in an unexpected state.
			 * Log error and ignore the frame.
			 */
			pe_debug("rx Auth frm2 from peer in state: %d addr",
				pe_session->limMlmState);
			lim_print_mac_addr(mac_ctx, mac_hdr->sa, LOG1);
		}
		return;
	}

	if (qdf_mem_cmp((uint8_t *) mac_hdr->sa,
			(uint8_t *) &mac_ctx->lim.gpLimMlmAuthReq->peerMacAddr,
			sizeof(tSirMacAddr))) {
		/*
		 * Received Authentication frame from an entity
		 * other than one request was initiated.
		 * Wait until Authentication Failure Timeout.
		 */

		pe_warn("received Auth frame2 from unexpected peer"
			MAC_ADDRESS_STR, MAC_ADDR_ARRAY(mac_hdr->sa));
		return;
	}

	if (rx_auth_frm_body->authStatusCode ==
			eSIR_MAC_AUTH_ALGO_NOT_SUPPORTED_STATUS) {
		/*
		 * Interoperability workaround: Linksys WAP4400N is returning
		 * wrong authType in OpenAuth response in case of
		 * SharedKey AP configuration. Pretend we don't see that,
		 * so upper layer can fallback to SharedKey authType,
		 * and successfully connect to the AP.
		 */
		if (rx_auth_frm_body->authAlgoNumber !=
				mac_ctx->lim.gpLimMlmAuthReq->authType) {
			rx_auth_frm_body->authAlgoNumber =
				mac_ctx->lim.gpLimMlmAuthReq->authType;
		}
	}

	if (rx_auth_frm_body->authAlgoNumber !=
			mac_ctx->lim.gpLimMlmAuthReq->authType) {
		/*
		 * Received Authentication frame with an auth
		 * algorithm other than one requested.
		 * Wait until Authentication Failure Timeout.
		 */

		pe_warn("rx Auth frame2 for unexpected auth algo number %d "
			MAC_ADDRESS_STR,
			rx_auth_frm_body->authAlgoNumber,
			MAC_ADDR_ARRAY(mac_hdr->sa));
		return;
	}

	if (rx_auth_frm_body->authStatusCode != eSIR_MAC_SUCCESS_STATUS) {
		/*
		 * Authentication failure.
		 * Return Auth confirm with received failure code to SME
		 */
		pe_err("rx Auth frame from peer with failure code %d "
			MAC_ADDRESS_STR,
			rx_auth_frm_body->authStatusCode,
			MAC_ADDR_ARRAY(mac_hdr->sa));
		lim_restore_from_auth_state(mac_ctx, eSIR_SME_AUTH_REFUSED,
			rx_auth_frm_body->authStatusCode,
			pe_session);
		return;
	}

	if (rx_auth_frm_body->authAlgoNumber == eSIR_OPEN_SYSTEM) {
		pe_session->limCurrentAuthType = eSIR_OPEN_SYSTEM;
		auth_node = lim_acquire_free_pre_auth_node(mac_ctx,
				&mac_ctx->lim.gLimPreAuthTimerTable);
		if (auth_node == NULL) {
			pe_warn("Max pre-auth nodes reached");
			lim_print_mac_addr(mac_ctx, mac_hdr->sa, LOGW);
			return;
		}

		pe_debug("Alloc new data: %p peer", auth_node);
		lim_print_mac_addr(mac_ctx, mac_hdr->sa, LOGD);
		qdf_mem_copy((uint8_t *) auth_node->peerMacAddr,
				mac_ctx->lim.gpLimMlmAuthReq->peerMacAddr,
				sizeof(tSirMacAddr));
		auth_node->fTimerStarted = 0;
		auth_node->authType =
			mac_ctx->lim.gpLimMlmAuthReq->authType;
		auth_node->seq_num =
			((mac_hdr->seqControl.seqNumHi << 4) |
			 (mac_hdr->seqControl.seqNumLo));
		auth_node->timestamp = qdf_mc_timer_get_system_ticks();
		lim_add_pre_auth_node(mac_ctx, auth_node);
		lim_restore_from_auth_state(mac_ctx, eSIR_SME_SUCCESS,
				rx_auth_frm_body->authStatusCode, pe_session);
	} else {
		/* Shared key authentication */
		if (LIM_IS_AP_ROLE(pe_session))
			val = pe_session->privacy;
		else if (wlan_cfg_get_int(mac_ctx,
					WNI_CFG_PRIVACY_ENABLED,
					&val) != eSIR_SUCCESS)
			pe_warn("couldnt retrieve Privacy option");
		cfg_privacy_opt_imp = (uint8_t) val;
		if (!cfg_privacy_opt_imp) {
			/*
			 * Requesting STA does not have WEP implemented.
			 * Reject with unsupported authentication algo
			 * Status code & wait until auth failure timeout
			 */

			pe_err("rx Auth frm from peer for unsupported auth algo %d "
						MAC_ADDRESS_STR,
					rx_auth_frm_body->authAlgoNumber,
					MAC_ADDR_ARRAY(mac_hdr->sa));

			auth_frame->authAlgoNumber =
				rx_auth_frm_body->authAlgoNumber;
			auth_frame->authTransactionSeqNumber =
				rx_auth_frm_body->authTransactionSeqNumber + 1;
			auth_frame->authStatusCode =
				eSIR_MAC_AUTH_ALGO_NOT_SUPPORTED_STATUS;
			lim_send_auth_mgmt_frame(mac_ctx, auth_frame,
					mac_hdr->sa, LIM_NO_WEP_IN_FC,
					pe_session, false);
			return;
		}
		if (rx_auth_frm_body->type != SIR_MAC_CHALLENGE_TEXT_EID) {
			pe_err("rx auth frm with invalid challenge txtie");
			return;
		}
		if (wlan_cfg_get_int(mac_ctx, WNI_CFG_WEP_DEFAULT_KEYID,
					&val) != eSIR_SUCCESS)
			pe_warn("could not retrieve Default key_id");
		key_id = (uint8_t) val;
		val = SIR_MAC_KEY_LENGTH;
		if (LIM_IS_AP_ROLE(pe_session)) {
			tpSirKeys key_ptr =
				&pe_session->WEPKeyMaterial[key_id].key[0];
			qdf_mem_copy(defaultkey, key_ptr->key,
					key_ptr->keyLength);
		} else if (wlan_cfg_get_str(mac_ctx,
				(uint16_t)(WNI_CFG_WEP_DEFAULT_KEY_1 + key_id),
				defaultkey, &val) != eSIR_SUCCESS) {
			/* Couldnt get Default key from CFG. */
			pe_warn("cant retrieve Defaultkey");
			auth_frame->authAlgoNumber =
				rx_auth_frm_body->authAlgoNumber;
			auth_frame->authTransactionSeqNumber =
				rx_auth_frm_body->authTransactionSeqNumber + 1;
			auth_frame->authStatusCode =
				eSIR_MAC_CHALLENGE_FAILURE_STATUS;
			lim_send_auth_mgmt_frame(mac_ctx,
					auth_frame, mac_hdr->sa,
					LIM_NO_WEP_IN_FC,
					pe_session, false);
			lim_restore_from_auth_state(mac_ctx,
					eSIR_SME_INVALID_WEP_DEFAULT_KEY,
					eSIR_MAC_UNSPEC_FAILURE_REASON,
					pe_session);
			return;
		}
		key_length = val;
		((tpSirMacAuthFrameBody)plainbody)->authAlgoNumber =
			sir_swap_u16if_needed(rx_auth_frm_body->authAlgoNumber);
		((tpSirMacAuthFrameBody)plainbody)->authTransactionSeqNumber =
			sir_swap_u16if_needed((uint16_t)(
				rx_auth_frm_body->authTransactionSeqNumber
				+ 1));
		((tpSirMacAuthFrameBody)plainbody)->authStatusCode =
			eSIR_MAC_SUCCESS_STATUS;
		((tpSirMacAuthFrameBody)plainbody)->type =
			SIR_MAC_CHALLENGE_TEXT_EID;
		((tpSirMacAuthFrameBody)plainbody)->length =
			SIR_MAC_AUTH_CHALLENGE_LENGTH;
		qdf_mem_copy((uint8_t *) (
			(tpSirMacAuthFrameBody)plainbody)->challengeText,
			rx_auth_frm_body->challengeText,
			SIR_MAC_AUTH_CHALLENGE_LENGTH);
		lim_encrypt_auth_frame(mac_ctx, key_id,
				defaultkey, plainbody,
				encr_auth_frame, key_length);
		pe_session->limMlmState = eLIM_MLM_WT_AUTH_FRAME4_STATE;
		MTRACE(mac_trace(mac_ctx, TRACE_CODE_MLM_STATE,
					pe_session->peSessionId,
					pe_session->limMlmState));
		lim_send_auth_mgmt_frame(mac_ctx,
				(tpSirMacAuthFrameBody)encr_auth_frame,
				mac_hdr->sa, LIM_WEP_IN_FC,
				pe_session, false);

		return;
	}
}

static void lim_process_auth_frame_type3(tpAniSirGlobal mac_ctx,
		tpSirMacMgmtHdr mac_hdr,
		tSirMacAuthFrameBody *rx_auth_frm_body,
		tSirMacAuthFrameBody *auth_frame,
		tpPESession pe_session)
{
	struct tLimPreAuthNode *auth_node;

	/* AuthFrame 3 */
	if (rx_auth_frm_body->authAlgoNumber != eSIR_SHARED_KEY) {
		pe_err("rx Auth frame3 from peer with auth algo number %d "
			MAC_ADDRESS_STR,
			rx_auth_frm_body->authAlgoNumber,
			MAC_ADDR_ARRAY(mac_hdr->sa));
		/*
		 * Received Authentication frame3 with algorithm other than
		 * Shared Key authentication type. Reject with Auth frame4
		 * with 'out of sequence' status code.
		 */
		auth_frame->authAlgoNumber = eSIR_SHARED_KEY;
		auth_frame->authTransactionSeqNumber = SIR_MAC_AUTH_FRAME_4;
		auth_frame->authStatusCode =
			eSIR_MAC_AUTH_FRAME_OUT_OF_SEQ_STATUS;
		lim_send_auth_mgmt_frame(mac_ctx, auth_frame,
			mac_hdr->sa, LIM_NO_WEP_IN_FC,
			pe_session, false);
		return;
	}

	if (LIM_IS_AP_ROLE(pe_session) ||
			LIM_IS_IBSS_ROLE(pe_session)) {
		/*
		 * Check if wep bit was set in FC. If not set,
		 * reject with Authentication frame4 with
		 * 'challenge failure' status code.
		 */
		if (!mac_hdr->fc.wep) {
			pe_err("received Auth frame3 from peer with no WEP bit set "
				MAC_ADDRESS_STR,
				MAC_ADDR_ARRAY(mac_hdr->sa));
			/* WEP bit is not set in FC of Auth Frame3 */
			auth_frame->authAlgoNumber = eSIR_SHARED_KEY;
			auth_frame->authTransactionSeqNumber =
				SIR_MAC_AUTH_FRAME_4;
			auth_frame->authStatusCode =
				eSIR_MAC_CHALLENGE_FAILURE_STATUS;
			lim_send_auth_mgmt_frame(mac_ctx, auth_frame,
					mac_hdr->sa,
					LIM_NO_WEP_IN_FC,
					pe_session, false);
			return;
		}

		auth_node = lim_search_pre_auth_list(mac_ctx, mac_hdr->sa);
		if (auth_node == NULL) {
			pe_warn("received AuthFrame3 from peer that has no preauth context "
				MAC_ADDRESS_STR,
				MAC_ADDR_ARRAY(mac_hdr->sa));
			/*
			 * No 'pre-auth' context exists for this STA that sent
			 * an Authentication frame3. Send Auth frame4 with
			 * 'out of sequence' status code.
			 */
			auth_frame->authAlgoNumber = eSIR_SHARED_KEY;
			auth_frame->authTransactionSeqNumber =
				SIR_MAC_AUTH_FRAME_4;
			auth_frame->authStatusCode =
				eSIR_MAC_AUTH_FRAME_OUT_OF_SEQ_STATUS;
			lim_send_auth_mgmt_frame(mac_ctx, auth_frame,
				mac_hdr->sa, LIM_NO_WEP_IN_FC,
				pe_session, false);
			return;
		}

		if (auth_node->mlmState == eLIM_MLM_AUTH_RSP_TIMEOUT_STATE) {
			pe_warn("auth response timer timedout for peer "
				MAC_ADDRESS_STR,
				MAC_ADDR_ARRAY(mac_hdr->sa));
			/*
			 * Received Auth Frame3 after Auth Response timeout.
			 * Reject by sending Auth Frame4 with
			 * Auth respone timeout Status Code.
			 */
			auth_frame->authAlgoNumber = eSIR_SHARED_KEY;
			auth_frame->authTransactionSeqNumber =
				SIR_MAC_AUTH_FRAME_4;
			auth_frame->authStatusCode =
				eSIR_MAC_AUTH_RSP_TIMEOUT_STATUS;

			lim_send_auth_mgmt_frame(mac_ctx, auth_frame,
				mac_hdr->sa, LIM_NO_WEP_IN_FC,
				pe_session, false);
			/* Delete pre-auth context of STA */
			lim_delete_pre_auth_node(mac_ctx, mac_hdr->sa);
			return;
		}
		if (rx_auth_frm_body->authStatusCode !=
				eSIR_MAC_SUCCESS_STATUS) {
			/*
			 * Received Authenetication Frame 3 with status code
			 * other than success. Wait until Auth response timeout
			 * to delete STA context.
			 */
			pe_err("rx Auth frm3 from peer with status code %d "
				MAC_ADDRESS_STR,
				rx_auth_frm_body->authStatusCode,
				MAC_ADDR_ARRAY(mac_hdr->sa));
				return;
		}
		/*
		 * Check if received challenge text is same as one sent in
		 * Authentication frame3
		 */
		if (!qdf_mem_cmp(rx_auth_frm_body->challengeText,
					auth_node->challengeText,
					SIR_MAC_AUTH_CHALLENGE_LENGTH)) {
			/*
			 * Challenge match. STA is autheticated
			 * Delete Authentication response timer if running
			 */
			lim_deactivate_and_change_per_sta_id_timer(mac_ctx,
				eLIM_AUTH_RSP_TIMER, auth_node->authNodeIdx);

			auth_node->fTimerStarted = 0;
			auth_node->mlmState = eLIM_MLM_AUTHENTICATED_STATE;

			/*
			 * Send Auth Frame4 with 'success' Status Code.
			 */
			auth_frame->authAlgoNumber = eSIR_SHARED_KEY;
			auth_frame->authTransactionSeqNumber =
				SIR_MAC_AUTH_FRAME_4;
			auth_frame->authStatusCode =
				eSIR_MAC_SUCCESS_STATUS;
			lim_send_auth_mgmt_frame(mac_ctx, auth_frame,
				mac_hdr->sa, LIM_NO_WEP_IN_FC,
				pe_session, false);
			return;
		} else {
			pe_warn("Challenge failure for peer "MAC_ADDRESS_STR,
				MAC_ADDR_ARRAY(mac_hdr->sa));
			/*
			 * Challenge Failure.
			 * Send Authentication frame4 with 'challenge failure'
			 * status code and wait until Auth response timeout to
			 * delete STA context.
			 */
			auth_frame->authAlgoNumber =
				rx_auth_frm_body->authAlgoNumber;
			auth_frame->authTransactionSeqNumber =
				SIR_MAC_AUTH_FRAME_4;
			auth_frame->authStatusCode =
				eSIR_MAC_CHALLENGE_FAILURE_STATUS;
			lim_send_auth_mgmt_frame(mac_ctx, auth_frame,
				mac_hdr->sa, LIM_NO_WEP_IN_FC,
				pe_session, false);
			return;
		}
	}
}

static void lim_process_auth_frame_type4(tpAniSirGlobal mac_ctx,
		tpSirMacMgmtHdr mac_hdr,
		tSirMacAuthFrameBody *rx_auth_frm_body,
		tpPESession pe_session)
{
	struct tLimPreAuthNode *auth_node;

	if (pe_session->limMlmState != eLIM_MLM_WT_AUTH_FRAME4_STATE) {
		/*
		 * Received Authentication frame4 in an unexpected state.
		 * Log error and ignore the frame.
		 */
		pe_warn("received unexpected Auth frame4 from peer in state %d, addr "
			MAC_ADDRESS_STR,
			pe_session->limMlmState,
			MAC_ADDR_ARRAY(mac_hdr->sa));
		return;
	}

	if (rx_auth_frm_body->authAlgoNumber != eSIR_SHARED_KEY) {
		/*
		 * Received Authentication frame4 with algorithm other than
		 * Shared Key authentication type.
		 * Wait until Auth failure timeout to report authentication
		 * failure to SME.
		 */
		pe_err("received Auth frame4 from peer with invalid auth algo %d"
			MAC_ADDRESS_STR,
			rx_auth_frm_body->authAlgoNumber,
			MAC_ADDR_ARRAY(mac_hdr->sa));
		return;
	}

	if (qdf_mem_cmp((uint8_t *) mac_hdr->sa,
			(uint8_t *) &mac_ctx->lim.gpLimMlmAuthReq->peerMacAddr,
			sizeof(tSirMacAddr))) {
		/*
		 * Received Authentication frame from an entity
		 * other than one to which request was initiated.
		 * Wait until Authentication Failure Timeout.
		 */

		pe_warn("received Auth frame4 from unexpected peer "MAC_ADDRESS_STR,
			MAC_ADDR_ARRAY(mac_hdr->sa));
		return;
	}

	if (rx_auth_frm_body->authAlgoNumber !=
			mac_ctx->lim.gpLimMlmAuthReq->authType) {
		/*
		 * Received Authentication frame with an auth algorithm
		 * other than one requested.
		 * Wait until Authentication Failure Timeout.
		 */

		pe_err("received Authentication frame from peer with invalid auth seq number %d "
			MAC_ADDRESS_STR,
			rx_auth_frm_body->authTransactionSeqNumber,
			MAC_ADDR_ARRAY(mac_hdr->sa));
		return;
	}

	if (rx_auth_frm_body->authStatusCode == eSIR_MAC_SUCCESS_STATUS) {
		/*
		 * Authentication Success, Inform SME of same.
		 */
		pe_session->limCurrentAuthType = eSIR_SHARED_KEY;
		auth_node = lim_acquire_free_pre_auth_node(mac_ctx,
					&mac_ctx->lim.gLimPreAuthTimerTable);
		if (auth_node == NULL) {
			pe_warn("Max pre-auth nodes reached");
			lim_print_mac_addr(mac_ctx, mac_hdr->sa, LOGW);
			return;
		}
		pe_debug("Alloc new data: %p peer", auth_node);
		lim_print_mac_addr(mac_ctx, mac_hdr->sa, LOGD);
		qdf_mem_copy((uint8_t *) auth_node->peerMacAddr,
				mac_ctx->lim.gpLimMlmAuthReq->peerMacAddr,
				sizeof(tSirMacAddr));
		auth_node->fTimerStarted = 0;
		auth_node->authType = mac_ctx->lim.gpLimMlmAuthReq->authType;
		auth_node->seq_num = ((mac_hdr->seqControl.seqNumHi << 4) |
					(mac_hdr->seqControl.seqNumLo));
		auth_node->timestamp = qdf_mc_timer_get_system_ticks();
		lim_add_pre_auth_node(mac_ctx, auth_node);
		lim_restore_from_auth_state(mac_ctx, eSIR_SME_SUCCESS,
				rx_auth_frm_body->authStatusCode, pe_session);
	} else {
		/*
		 * Authentication failure.
		 * Return Auth confirm with received failure code to SME
		 */
		pe_err("Authentication failure from peer "MAC_ADDRESS_STR,
			MAC_ADDR_ARRAY(mac_hdr->sa));
		lim_restore_from_auth_state(mac_ctx, eSIR_SME_AUTH_REFUSED,
				rx_auth_frm_body->authStatusCode,
				pe_session);
	}
}

/**
 * lim_process_auth_frame() - to process auth frame
 * @mac_ctx - Pointer to Global MAC structure
 * @rx_pkt_info - A pointer to Rx packet info structure
 * @session - A pointer to session
 *
 * This function is called by limProcessMessageQueue() upon Authentication
 * frame reception.
 *
 * LOGIC:
 * This function processes received Authentication frame and responds
 * with either next Authentication frame in sequence to peer MAC entity
 * or LIM_MLM_AUTH_IND on AP or LIM_MLM_AUTH_CNF on STA.
 *
 * NOTE:
 * 1. Authentication failures are reported to SME with same status code
 *    received from the peer MAC entity.
 * 2. Authentication frame2/4 received with alogirthm number other than
 *    one requested in frame1/3 are logged with an error and auth confirm
 *    will be sent to SME only after auth failure timeout.
 * 3. Inconsistency in the spec:
 *    On receiving Auth frame2, specs says that if WEP key mapping key
 *    or default key is NULL, Auth frame3 with a status code 15 (challenge
 *    failure to be returned to peer entity. However, section 7.2.3.10,
 *    table 14 says that status code field is 'reserved' for frame3 !
 *    In the current implementation, Auth frame3 is returned with status
 *    code 15 overriding section 7.2.3.10.
 * 4. If number pre-authentications reach configrable max limit,
 *    Authentication frame with 'unspecified failure' status code is
 *    returned to requesting entity.
 *
 * Return: None
 */
void
lim_process_auth_frame(tpAniSirGlobal mac_ctx, uint8_t *rx_pkt_info,
		       tpPESession pe_session)
{
	uint8_t *body_ptr, key_id, cfg_privacy_opt_imp;
	uint8_t defaultkey[SIR_MAC_KEY_LENGTH];
	uint8_t plainbody[LIM_ENCR_AUTH_BODY_LEN];
	uint8_t decrypt_result;
	uint16_t frame_len, curr_seq_num = 0;
	uint32_t val, key_length = 8;
	tSirMacAuthFrameBody *rx_auth_frm_body, rx_auth_frame, auth_frame;
	tpSirMacMgmtHdr mac_hdr;
	struct tLimPreAuthNode *auth_node;

	/* Get pointer to Authentication frame header and body */
	mac_hdr = WMA_GET_RX_MAC_HEADER(rx_pkt_info);
	frame_len = WMA_GET_RX_PAYLOAD_LEN(rx_pkt_info);

	if (!frame_len) {
		/* Log error */
		pe_err("received Auth frame with no body from: %pM",
			mac_hdr->sa);
		return;
	}

	if (lim_is_group_addr(mac_hdr->sa)) {
		/*
		 * Received Auth frame from a BC/MC address
		 * Log error and ignore it
		 */
		pe_err("received Auth frame from a BC/MC addr: %pM",
			mac_hdr->sa);
		return;
	}
	curr_seq_num = (mac_hdr->seqControl.seqNumHi << 4) |
		(mac_hdr->seqControl.seqNumLo);

	pe_info("Sessionid: %d System role: %d limMlmState: %d: Auth response Received BSSID: "MAC_ADDRESS_STR" RSSI: %d",
		pe_session->peSessionId, GET_LIM_SYSTEM_ROLE(pe_session),
		pe_session->limMlmState, MAC_ADDR_ARRAY(mac_hdr->bssId),
		(uint) abs((int8_t) WMA_GET_RX_RSSI_NORMALIZED(rx_pkt_info)));

	body_ptr = WMA_GET_RX_MPDU_DATA(rx_pkt_info);

	/* Restore default failure timeout */
	if (QDF_P2P_CLIENT_MODE == pe_session->pePersona &&
			pe_session->defaultAuthFailureTimeout) {
		pe_debug("Restore default failure timeout");
		cfg_set_int(mac_ctx, WNI_CFG_AUTHENTICATE_FAILURE_TIMEOUT,
				pe_session->defaultAuthFailureTimeout);
	}
	/*
	 * Determine if WEP bit is set in the FC or received MAC header
	 * Note: WEP bit is set in FC of MAC header.
	 */
	if (mac_hdr->fc.wep) {
		/*
		 * If TKIP counter measures enabled then issue Deauth
		 * frame to station
		 */
		if (pe_session->bTkipCntrMeasActive &&
				LIM_IS_AP_ROLE(pe_session)) {
			pe_err("Tkip counter enabled, send deauth to: %pM",
				mac_hdr->sa);
			lim_send_deauth_mgmt_frame(mac_ctx,
					eSIR_MAC_MIC_FAILURE_REASON,
					mac_hdr->sa, pe_session, false);
			return;
		}
		/* Extract key ID from IV (most 2 bits of 4th byte of IV) */
		key_id = (*(body_ptr + 3)) >> 6;

		/*
		 * On STA in infrastructure BSS, Authentication frames received
		 * with WEP bit set in the FC must be rejected with challenge
		 * failure status code (wierd thing in the spec - this should've
		 * been rejected with unspecified failure/unexpected assertion
		 * of wep bit (this status code does not exist though) or
		 * Out-of-sequence-Authentication-Frame status code.
		 */
		if (LIM_IS_STA_ROLE(pe_session)) {
			auth_frame.authAlgoNumber = eSIR_SHARED_KEY;
			auth_frame.authTransactionSeqNumber =
				SIR_MAC_AUTH_FRAME_4;
			auth_frame.authStatusCode =
				eSIR_MAC_CHALLENGE_FAILURE_STATUS;
			/* Log error */
			pe_err("rx Auth frm with wep bit set role: %d %pM",
				GET_LIM_SYSTEM_ROLE(pe_session), mac_hdr->sa);
			lim_send_auth_mgmt_frame(mac_ctx, &auth_frame,
				mac_hdr->sa, LIM_NO_WEP_IN_FC,
				pe_session, false);
			return;
		}

		if (frame_len < LIM_ENCR_AUTH_BODY_LEN) {
			/* Log error */
			pe_err("Not enough size: %d to decry rx Auth frm",
				frame_len);
			lim_print_mac_addr(mac_ctx, mac_hdr->sa, LOGE);
			return;
		}
		if (LIM_IS_AP_ROLE(pe_session)) {
			val = pe_session->privacy;
		} else if (wlan_cfg_get_int(mac_ctx, WNI_CFG_PRIVACY_ENABLED,
					&val) != eSIR_SUCCESS) {
			/*
			 * Accept Authentication frame only if Privacy is
			 * implemented, if Could not get Privacy option
			 * from CFG then Log fatal error
			 */
			pe_warn("could not retrieve Privacy option");
		}
		cfg_privacy_opt_imp = (uint8_t) val;

		if (!cfg_privacy_opt_imp) {
			pe_err("received Authentication frame3 from peer that while privacy option is turned OFF "
				MAC_ADDRESS_STR,
				MAC_ADDR_ARRAY(mac_hdr->sa));
			/*
			 * Privacy option is not implemented.
			 * So reject Authentication frame received with
			 * WEP bit set by sending Authentication frame
			 * with 'challenge failure' status code. This is
			 * another strange thing in the spec. Status code
			 * should have been 'unsupported algorithm' status code.
			 */
			auth_frame.authAlgoNumber = eSIR_SHARED_KEY;
			auth_frame.authTransactionSeqNumber =
				SIR_MAC_AUTH_FRAME_4;
			auth_frame.authStatusCode =
				eSIR_MAC_CHALLENGE_FAILURE_STATUS;
			lim_send_auth_mgmt_frame(mac_ctx, &auth_frame,
				mac_hdr->sa, LIM_NO_WEP_IN_FC,
				pe_session, false);
			return;
		}

		/*
		 * Privacy option is implemented. Check if the received frame is
		 * Authentication frame3 and there is a context for requesting
		 * STA. If not, reject with unspecified failure status code
		 */
		auth_node = lim_search_pre_auth_list(mac_ctx, mac_hdr->sa);
		if (auth_node == NULL) {
			pe_err("rx Auth frame with no preauth ctx with WEP bit set "
				MAC_ADDRESS_STR,
				MAC_ADDR_ARRAY(mac_hdr->sa));
			/*
			 * No 'pre-auth' context exists for this STA
			 * that sent an Authentication frame with FC
			 * bit set. Send Auth frame4 with
			 * 'out of sequence' status code.
			 */
			auth_frame.authAlgoNumber = eSIR_SHARED_KEY;
			auth_frame.authTransactionSeqNumber =
				SIR_MAC_AUTH_FRAME_4;
			auth_frame.authStatusCode =
				eSIR_MAC_AUTH_FRAME_OUT_OF_SEQ_STATUS;
			lim_send_auth_mgmt_frame(mac_ctx, &auth_frame,
				mac_hdr->sa, LIM_NO_WEP_IN_FC,
				pe_session, false);
			return;
		}
		/* Change the auth-response timeout */
		lim_deactivate_and_change_per_sta_id_timer(mac_ctx,
				eLIM_AUTH_RSP_TIMER, auth_node->authNodeIdx);

		/* 'Pre-auth' status exists for STA */
		if ((auth_node->mlmState != eLIM_MLM_WT_AUTH_FRAME3_STATE) &&
			(auth_node->mlmState !=
				eLIM_MLM_AUTH_RSP_TIMEOUT_STATE)) {
			pe_err("received Authentication frame from peer that is in state %d "
				MAC_ADDRESS_STR,
				auth_node->mlmState,
				MAC_ADDR_ARRAY(mac_hdr->sa));
			/*
			 * Should not have received Authentication frame
			 * with WEP bit set in FC in other states.
			 * Reject by sending Authenticaton frame with
			 * out of sequence Auth frame status code.
			 */
			auth_frame.authAlgoNumber = eSIR_SHARED_KEY;
			auth_frame.authTransactionSeqNumber =
				SIR_MAC_AUTH_FRAME_4;
			auth_frame.authStatusCode =
				eSIR_MAC_AUTH_FRAME_OUT_OF_SEQ_STATUS;

			lim_send_auth_mgmt_frame(mac_ctx, &auth_frame,
				mac_hdr->sa, LIM_NO_WEP_IN_FC,
				pe_session, false);
			return;
		}

		val = SIR_MAC_KEY_LENGTH;

		if (LIM_IS_AP_ROLE(pe_session)) {
			tpSirKeys key_ptr;
			key_ptr = &pe_session->WEPKeyMaterial[key_id].key[0];
			qdf_mem_copy(defaultkey, key_ptr->key,
					key_ptr->keyLength);
			val = key_ptr->keyLength;
		} else if (wlan_cfg_get_str(mac_ctx,
				(uint16_t) (WNI_CFG_WEP_DEFAULT_KEY_1 + key_id),
				defaultkey, &val) != eSIR_SUCCESS) {
			pe_warn("could not retrieve Default key");

			/*
			 * Send Authentication frame
			 * with challenge failure status code
			 */
			auth_frame.authAlgoNumber = eSIR_SHARED_KEY;
			auth_frame.authTransactionSeqNumber =
				SIR_MAC_AUTH_FRAME_4;
			auth_frame.authStatusCode =
				eSIR_MAC_CHALLENGE_FAILURE_STATUS;
			lim_send_auth_mgmt_frame(mac_ctx, &auth_frame,
				mac_hdr->sa, LIM_NO_WEP_IN_FC,
				pe_session, false);
			return;
		}

		key_length = val;
		decrypt_result = lim_decrypt_auth_frame(mac_ctx, defaultkey,
					body_ptr, plainbody, key_length,
					(uint16_t) (frame_len -
							SIR_MAC_WEP_IV_LENGTH));
		if (decrypt_result == LIM_DECRYPT_ICV_FAIL) {
			pe_err("received Authentication frame from peer that failed decryption: "
				MAC_ADDRESS_STR,
				MAC_ADDR_ARRAY(mac_hdr->sa));
			/* ICV failure */
			lim_delete_pre_auth_node(mac_ctx, mac_hdr->sa);
			auth_frame.authAlgoNumber = eSIR_SHARED_KEY;
			auth_frame.authTransactionSeqNumber =
				SIR_MAC_AUTH_FRAME_4;
			auth_frame.authStatusCode =
				eSIR_MAC_CHALLENGE_FAILURE_STATUS;

			lim_send_auth_mgmt_frame(mac_ctx, &auth_frame,
				mac_hdr->sa, LIM_NO_WEP_IN_FC,
				pe_session, false);
			return;
		}
		if ((sir_convert_auth_frame2_struct(mac_ctx, plainbody,
				frame_len - 8, &rx_auth_frame) != eSIR_SUCCESS)
				|| (!is_auth_valid(mac_ctx, &rx_auth_frame,
							pe_session))) {
			pe_err("failed to convert Auth Frame to structure or Auth is not valid");
			return;
		}
	} else if ((sir_convert_auth_frame2_struct(mac_ctx, body_ptr,
				frame_len, &rx_auth_frame) != eSIR_SUCCESS)
				|| (!is_auth_valid(mac_ctx, &rx_auth_frame,
						pe_session))) {
			pe_err("failed to convert Auth Frame to structure or Auth is not valid");
			return;
	}

	rx_auth_frm_body = &rx_auth_frame;

	pe_debug("Received Auth frame with type: %d seqnum: %d status: %d %d",
		(uint32_t) rx_auth_frm_body->authAlgoNumber,
		(uint32_t) rx_auth_frm_body->authTransactionSeqNumber,
		(uint32_t) rx_auth_frm_body->authStatusCode,
		(uint32_t) mac_ctx->lim.gLimNumPreAuthContexts);

	/*
	 * IOT Workaround: with invalid WEP key, some APs reply
	 * AuthFrame 4 with invalid seqNumber. This AuthFrame
	 * will be dropped by driver, thus driver sends the
	 * generic status code instead of protocol status code.
	 * As a workaround, override AuthFrame 4's seqNumber.
	 */
	if ((pe_session->limMlmState ==
		eLIM_MLM_WT_AUTH_FRAME4_STATE) &&
		(rx_auth_frm_body->authTransactionSeqNumber !=
		SIR_MAC_AUTH_FRAME_1) &&
		(rx_auth_frm_body->authTransactionSeqNumber !=
		SIR_MAC_AUTH_FRAME_2) &&
		(rx_auth_frm_body->authTransactionSeqNumber !=
		SIR_MAC_AUTH_FRAME_3)) {
		pe_warn("Override AuthFrame 4's seqNumber to 4");
		rx_auth_frm_body->authTransactionSeqNumber =
			SIR_MAC_AUTH_FRAME_4;
	}


	switch (rx_auth_frm_body->authTransactionSeqNumber) {
	case SIR_MAC_AUTH_FRAME_1:
		lim_process_auth_frame_type1(mac_ctx,
			mac_hdr, rx_auth_frm_body, rx_pkt_info,
			curr_seq_num, &auth_frame, pe_session);
		break;
	case SIR_MAC_AUTH_FRAME_2:
		lim_process_auth_frame_type2(mac_ctx,
			mac_hdr, rx_auth_frm_body, &auth_frame, plainbody,
			body_ptr, frame_len, pe_session);
		break;
	case SIR_MAC_AUTH_FRAME_3:
		lim_process_auth_frame_type3(mac_ctx,
			mac_hdr, rx_auth_frm_body, &auth_frame, pe_session);
		break;
	case SIR_MAC_AUTH_FRAME_4:
		lim_process_auth_frame_type4(mac_ctx,
			mac_hdr, rx_auth_frm_body, pe_session);
		break;
	default:
		/* Invalid Authentication Frame received. Ignore it. */
		pe_warn("rx auth frm with invalid authseq no: %d from: %pM",
			rx_auth_frm_body->authTransactionSeqNumber,
			mac_hdr->sa);
		break;
	}
}

/*----------------------------------------------------------------------
 *
 * Pass the received Auth frame. This is possibly the pre-auth from the
 * neighbor AP, in the same mobility domain.
 * This will be used in case of 11r FT.
 *
 * !!!! This is going to be renoved for the next checkin. We will be creating
 * the session before sending out the Auth. Thus when auth response
 * is received we will have a session in progress. !!!!!
 ***----------------------------------------------------------------------
 */
tSirRetStatus lim_process_auth_frame_no_session(tpAniSirGlobal pMac, uint8_t *pBd,
						void *body)
{
	tpSirMacMgmtHdr pHdr;
	tpPESession psessionEntry = NULL;
	uint8_t *pBody;
	uint16_t frameLen;
	tSirMacAuthFrameBody rxAuthFrame;
	tSirMacAuthFrameBody *pRxAuthFrameBody = NULL;
	tSirRetStatus ret_status = eSIR_FAILURE;
	int i;

	pHdr = WMA_GET_RX_MAC_HEADER(pBd);
	pBody = WMA_GET_RX_MPDU_DATA(pBd);
	frameLen = WMA_GET_RX_PAYLOAD_LEN(pBd);

	pe_info("Auth Frame Received: BSSID " MAC_ADDRESS_STR " (RSSI %d)",
		MAC_ADDR_ARRAY(pHdr->bssId),
		(uint) abs((int8_t) WMA_GET_RX_RSSI_NORMALIZED(pBd)));

	/* Auth frame has come on a new BSS, however, we need to find the session
	 * from where the auth-req was sent to the new AP
	 */
	for (i = 0; i < pMac->lim.maxBssId; i++) {
		/* Find first free room in session table */
		if (pMac->lim.gpSession[i].valid == true &&
		    pMac->lim.gpSession[i].ftPEContext.ftPreAuthSession ==
		    true) {
			/* Found the session */
			psessionEntry = &pMac->lim.gpSession[i];
			pMac->lim.gpSession[i].ftPEContext.ftPreAuthSession =
				false;
		}
	}

	if (psessionEntry == NULL) {
		pe_debug("cannot find session id in FT pre-auth phase");
		return eSIR_FAILURE;
	}

	if (psessionEntry->ftPEContext.pFTPreAuthReq == NULL) {
		pe_err("Error: No FT");
		/* No FT in progress. */
		return eSIR_FAILURE;
	}

	if (frameLen == 0) {
		pe_err("Error: Frame len = 0");
		return eSIR_FAILURE;
	}
	lim_print_mac_addr(pMac, pHdr->bssId, LOGD);
	lim_print_mac_addr(pMac,
			   psessionEntry->ftPEContext.pFTPreAuthReq->preAuthbssId,
			   LOGD);
	pe_debug("seqControl: 0x%X",
		((pHdr->seqControl.seqNumHi << 8) |
		 (pHdr->seqControl.seqNumLo << 4) |
		 (pHdr->seqControl.fragNum)));

	/* Check that its the same bssId we have for preAuth */
	if (qdf_mem_cmp
		    (psessionEntry->ftPEContext.pFTPreAuthReq->preAuthbssId,
		    pHdr->bssId, sizeof(tSirMacAddr))) {
		pe_err("Error: Same bssid as preauth BSSID");
		/* In this case SME if indeed has triggered a */
		/* pre auth it will time out. */
		return eSIR_FAILURE;
	}

	if (true ==
	    psessionEntry->ftPEContext.pFTPreAuthReq->bPreAuthRspProcessed) {
		/*
		 * This is likely a duplicate for the same pre-auth request.
		 * PE/LIM already posted a response to SME. Hence, drop it.
		 * TBD:
		 * 1) How did we even receive multiple auth responses?
		 * 2) Do we need to delete pre-auth session? Suppose we
		 * previously received an auth resp with failure which
		 * would not have created the session and forwarded to SME.
		 * And, we subsequently received an auth resp with success
		 * which would have created the session. This will now be
		 * dropped without being forwarded to SME! However, it is
		 * very unlikely to receive auth responses from the same
		 * AP with different reason codes.
		 * NOTE: return eSIR_SUCCESS so that the packet is dropped
		 * as this was indeed a response from the BSSID we tried to
		 * pre-auth.
		 */
		pe_debug("Auth rsp already posted to SME"
			       " (session %p, FT session %p)", psessionEntry,
			       psessionEntry);
		return eSIR_SUCCESS;
	} else {
		pe_warn("Auth rsp not yet posted to SME"
			       " (session %p, FT session %p)", psessionEntry,
			       psessionEntry);
		psessionEntry->ftPEContext.pFTPreAuthReq->bPreAuthRspProcessed =
			true;
	}

	pe_debug("Pre-Auth response received from neighbor");
	pe_debug("Pre-Auth done state");
	/* Stopping timer now, that we have our unicast from the AP */
	/* of our choice. */
	lim_deactivate_and_change_timer(pMac, eLIM_FT_PREAUTH_RSP_TIMER);

	/* Save off the auth resp. */
	if ((sir_convert_auth_frame2_struct(pMac, pBody, frameLen, &rxAuthFrame) !=
	     eSIR_SUCCESS)) {
		pe_err("failed to convert Auth frame to struct");
		lim_handle_ft_pre_auth_rsp(pMac, eSIR_FAILURE, NULL, 0,
					   psessionEntry);
		return eSIR_FAILURE;
	}
	pRxAuthFrameBody = &rxAuthFrame;

	pe_debug("Received Auth frame with type: %d seqnum: %d status: %d %d",
		       (uint32_t) pRxAuthFrameBody->authAlgoNumber,
		       (uint32_t) pRxAuthFrameBody->authTransactionSeqNumber,
		       (uint32_t) pRxAuthFrameBody->authStatusCode,
		       (uint32_t) pMac->lim.gLimNumPreAuthContexts);
	switch (pRxAuthFrameBody->authTransactionSeqNumber) {
	case SIR_MAC_AUTH_FRAME_2:
		if (pRxAuthFrameBody->authStatusCode != eSIR_MAC_SUCCESS_STATUS) {
			pe_err("Auth status code received is %d",
				(uint32_t) pRxAuthFrameBody->authStatusCode);
			if (eSIR_MAC_MAX_ASSOC_STA_REACHED_STATUS ==
			    pRxAuthFrameBody->authStatusCode)
				ret_status = eSIR_LIM_MAX_STA_REACHED_ERROR;
		} else {
			ret_status = eSIR_SUCCESS;
		}
		break;

	default:
		pe_warn("Seq. no incorrect expected 2 received %d",
			(uint32_t) pRxAuthFrameBody->authTransactionSeqNumber);
		break;
	}

	/* Send the Auth response to SME */
	lim_handle_ft_pre_auth_rsp(pMac, ret_status, pBody, frameLen, psessionEntry);

	return ret_status;
}
