eapol/eapol_framework/eapol_common/core/eapol_core.cpp
changeset 0 c8830336c852
child 2 1c7bc153c08e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eapol/eapol_framework/eapol_common/core/eapol_core.cpp	Thu Dec 17 08:47:43 2009 +0200
@@ -0,0 +1,4183 @@
+/*
+* Copyright (c) 2001-2006 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the License "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:  EAP and WLAN authentication protocols.
+*
+*/
+
+
+// This is enumeration of EAPOL source code.
+#if defined(USE_EAP_MINIMUM_RELEASE_TRACES)
+	#undef EAP_FILE_NUMBER_ENUM
+	#define EAP_FILE_NUMBER_ENUM 48 
+	#undef EAP_FILE_NUMBER_DATE 
+	#define EAP_FILE_NUMBER_DATE 1127594498 
+#endif //#if defined(USE_EAP_MINIMUM_RELEASE_TRACES)
+
+
+
+#include "eap_am_memory.h"
+#include "eap_tools.h"
+#include "eapol_core.h"
+#include "eap_crypto_api.h"
+#include "eapol_key_header.h"
+#include "eapol_rc4_key_header.h"
+#include "eap_state_notification.h"
+#include "eap_automatic_variable.h"
+#include "eap_network_id_selector.h"
+#include "eap_config.h"
+#include "eap_buffer.h"
+#include "eapol_session_key.h"
+#include "eap_master_session_key.h"
+
+#define MPPE_KEY_LENGTH 32
+
+//--------------------------------------------------
+
+// 
+EAP_FUNC_EXPORT eapol_core_c::~eapol_core_c()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eapol_core_c::~eapol_core_c(): this = 0x%08x\n"),
+		this));
+
+	EAP_ASSERT(m_shutdown_was_called == true);
+
+	delete m_eap_core;
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+}
+
+//--------------------------------------------------
+
+#if defined(_WIN32) && !defined(__GNUC__)
+	#pragma warning( disable : 4355 ) // 'this' : used in base member initializer list
+#endif
+
+// 
+EAP_FUNC_EXPORT eapol_core_c::eapol_core_c(
+	abs_eap_am_tools_c * const tools,
+	abs_eapol_core_c * const partner,
+	const bool is_client_when_true)
+	: m_partner(partner)
+#if !defined(NO_EAP_SESSION_CORE)
+	  , m_eap_core(new eap_session_core_c(tools, this, is_client_when_true))
+#else
+	  , m_eap_core(new eap_core_c(tools, this, is_client_when_true, 0, false))
+#endif
+#if defined(USE_EAPOL_KEY_STATE)
+	  , m_eapol_key_state_map(tools, this)
+#endif //#if defined(USE_EAPOL_KEY_STATE)
+	  , m_am_tools(tools)
+	  , m_master_session_key(m_am_tools)
+	  , m_authentication_type(eapol_key_authentication_type_none)
+	  , m_eapol_header_offset(0u)
+	  , m_MTU(0u)
+	  , m_trailer_length(0u)
+	  , m_max_eapol_starts(0u)
+	  , m_eapol_start_interval(0u)
+	  , m_eapol_starts_sent(0u)
+	  , m_is_client(is_client_when_true)
+	  , m_is_valid(false)
+	  , m_shutdown_was_called(false)
+	  , m_block_state_notifications(false)
+#if defined(USE_EAPOL_KEY_STATE)
+	  , m_skip_start_4_way_handshake(false)
+#endif //#if defined(USE_EAPOL_KEY_STATE)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eapol_core_c::eapol_core_c(): %s, this = 0x%08x => 0x%08x, compiled %s %s.\n"),
+		(m_is_client == true) ? "client": "server",
+		this,
+		dynamic_cast<abs_eap_base_timer_c *>(this),
+		__DATE__,
+		__TIME__));
+
+	if (m_eap_core != 0
+		&& m_eap_core->get_is_valid() == true)
+	{
+		set_is_valid();
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eapol_core_c::packet_process(
+	const eap_am_network_id_c * const receive_network_id,
+	eap_general_header_base_c * const packet_data,
+	const u32_t packet_length)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	eap_status_e status = eap_status_process_general_error;
+
+	if (receive_network_id == 0
+		|| receive_network_id->get_is_valid_data() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	if (packet_data == 0
+		|| packet_length < eap_header_base_c::get_header_length())
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_illegal_packet_error);
+	}
+
+	eapol_header_wr_c eapol(
+		m_am_tools,
+		packet_data->get_header_buffer(packet_data->get_header_buffer_length()),
+		packet_data->get_header_buffer_length());
+
+	if (eapol.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_illegal_packet_error);
+	}
+
+	if (packet_length < eapol.get_data_length())
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_illegal_packet_error);
+	}
+
+	if (m_eap_core == 0)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	if (packet_length < eapol_header_rd_c::get_header_length() // First we need atleast header to check the data length.
+		|| packet_length < eapol_header_rd_c::get_header_length()+eapol.get_data_length())
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_illegal_packet_error);
+	}
+
+	EAP_TRACE_DEBUG(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("-> EAPOL: %s, version=0x%02x, packet_type=0x%02x=%s, data_length=0x%04x, packet_length 0x%04x\n"),
+		(m_is_client == true) ? "client": "server",
+		eapol.get_version(),
+		eapol.get_packet_type(),
+		eapol.get_type_string(),
+		eapol.get_data_length(),
+		packet_length));
+
+	status = eapol.check_header();
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	if (receive_network_id->get_type() == eapol_ethernet_type_preauthentication
+		&& eapol.get_packet_type() != eapol_packet_type_eap
+		&& eapol.get_packet_type() != eapol_packet_type_start)
+	{
+		// Only EAP-Packet and EAPOL-Start are valid for preauthentication.
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_wrong_eapol_type);
+	}
+
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	eapol_key_state_c *eapol_key_state = 0;
+
+	{
+		// Here we swap the addresses.
+		eap_am_network_id_c send_network_id(
+			m_am_tools,
+			receive_network_id->get_destination_id(),
+			receive_network_id->get_source_id(),
+			receive_network_id->get_type());
+		if (send_network_id.get_is_valid_data() == false)
+		{
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		eap_network_id_selector_c state_selector(
+			m_am_tools,
+			&send_network_id);
+
+		if (state_selector.get_is_valid() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		EAP_TRACE_DATA_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("packet_process(): EAPOL-Key session"),
+			state_selector.get_data(state_selector.get_data_length()),
+			state_selector.get_data_length()));
+
+
+		// Check do we have handler for this packet. This applies to EAP-packets too.
+		eapol_key_state = m_eapol_key_state_map.get_handler(&state_selector);
+
+		if (eapol_key_state == 0)
+		{
+			EAP_TRACE_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("ERROR: eapol_core_c::packet_process() failed, no eapol_key_state_c object for this packet. Drop packet.\n")));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+
+	if (eapol.get_packet_type() == eapol_packet_type_eap
+		&& eapol.get_data_length() >= eap_header_base_c::get_header_length())
+	{
+		if (m_authentication_type != eapol_key_authentication_type_RSNA_EAP
+			&& m_authentication_type != eapol_key_authentication_type_WPA_EAP
+			&& m_authentication_type != eapol_key_authentication_type_802_1X
+#if defined(EAP_USE_WPXM)
+			&& m_authentication_type != eapol_key_authentication_type_WPXM
+#endif //#if defined(EAP_USE_WPXM)
+			&& m_authentication_type != eapol_key_authentication_type_WFA_SC
+			)
+		{
+			EAP_TRACE_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("WARNING: eapol_core_c::packet_process(): Illegal authentication type 0x%08x\n"),
+				 m_authentication_type));
+			
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_wrong_eapol_type);
+		}
+
+
+#if defined(USE_EAPOL_KEY_STATE_OPTIMIZED_4_WAY_HANDSHAKE)
+
+		// Test first we are connected.
+		if (eapol_key_state->get_is_associated() == true)
+		{
+			status = m_eap_core->synchronous_create_eap_session(receive_network_id);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+
+#endif //#if !defined(USE_EAPOL_KEY_STATE_OPTIMIZED_4_WAY_HANDSHAKE)
+
+
+		eap_header_wr_c eap(
+			m_am_tools,
+			eapol.get_eap_header(),
+			eapol.get_data_length());
+
+		status = m_eap_core->packet_process(
+			receive_network_id,
+			&eap,
+			eapol.get_data_length());
+
+		EAP_GENERAL_HEADER_COPY_ERROR_PARAMETERS(packet_data, &eap);
+	}
+	else if (m_is_client == false
+		&& eapol.get_packet_type() == eapol_packet_type_logoff)
+	{
+		status = eap_status_ok;
+	}
+	else if (m_is_client == false
+		&& eapol.get_packet_type() == eapol_packet_type_start)
+	{
+		if (m_authentication_type != eapol_key_authentication_type_RSNA_EAP
+			&& m_authentication_type != eapol_key_authentication_type_WPA_EAP
+			&& m_authentication_type != eapol_key_authentication_type_802_1X
+			&& m_authentication_type != eapol_key_authentication_type_WFA_SC
+#if defined(EAP_USE_WPXM)
+			&& m_authentication_type != eapol_key_authentication_type_WPXM
+#endif //#if defined(EAP_USE_WPXM)
+			)
+		{
+			EAP_TRACE_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("WARNING: eapol_core_c::packet_process(): Illegal authentication type 0x%08x\n"),
+				 m_authentication_type));
+			
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_wrong_eapol_type);
+		}
+
+		// This call does not cause clean restart. On-going authentication is not terminated.
+		status = restart_authentication(
+			receive_network_id,
+			m_is_client,
+			false);
+	}
+#if defined(USE_EAPOL_KEY_STATE)
+	else if (eapol.get_packet_type() == eapol_packet_type_key)
+	{
+		if (m_authentication_type != eapol_key_authentication_type_RSNA_EAP
+			&& m_authentication_type != eapol_key_authentication_type_RSNA_PSK
+			&& m_authentication_type != eapol_key_authentication_type_WPA_EAP
+			&& m_authentication_type != eapol_key_authentication_type_WPA_PSK
+			&& m_authentication_type != eapol_key_authentication_type_802_1X
+#if defined(EAP_USE_WPXM)
+			&& m_authentication_type != eapol_key_authentication_type_WPXM
+#endif //#if defined(EAP_USE_WPXM)
+			)
+		{
+			EAP_TRACE_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("WARNING: eapol_core_c::packet_process(): Illegal authentication type 0x%08x\n"),
+				 m_authentication_type));
+			
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_wrong_eapol_type);
+		}
+
+		// Indicates EAP-Core of received EAPOL-Key message.
+		// This is indication of finished EAP-authentication.
+		// NOTE, we do not care of the status.
+		// Some EAP-types does not implement this function.
+		(void) eap_acknowledge(receive_network_id);
+
+		if (eapol_key_state != 0)
+		{
+			status = eapol_key_state->process_eapol_key_frame(
+				receive_network_id,
+				packet_data,
+				packet_length);
+		}
+		else
+		{
+			EAP_TRACE_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("ERROR: process_eapol_key_frame() failed, no eapol_key_state_c object\n")));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_handler_does_not_exists_error);
+		}
+	}
+#endif //#if defined(USE_EAPOL_KEY_STATE)
+#if ! defined(USE_EAPOL_KEY_STATE)
+	else if (
+		m_is_client == true
+		&& eapol.get_packet_type() == eapol_packet_type_key)
+	{
+		// Handle EAPOL-Key frame.
+		// Here is assumed the EAPOL-Key frame includes RC4 Key Descriptor.
+		/**
+		 * @{ Here we need to check the Descriptor Type field.
+		 * It may be RC4 Key Descriptor, RSNA Key Descriptor or other descriptor. }
+		 */
+		
+		eapol_RC4_key_header_c eapol_key_msg(
+			m_am_tools,
+			eapol.get_header_buffer(eapol.get_header_buffer_length()),
+			eapol.get_header_buffer_length());
+		if (eapol_key_msg.get_is_valid() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+		}
+		else if (eapol_key_msg.check_header() != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = handle_RC4_key_descriptor(
+			receive_network_id,
+			&eapol_key_msg,
+			packet_length);
+	}
+#endif //#if ! defined(USE_EAPOL_KEY_STATE)
+	else
+	{
+		EAP_TRACE_DEBUG(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("ERROR: %s, packet_type=0x%02x=%s not handled, data length 0x%04x.\n"),
+			(m_is_client == true) ? "client": "server",
+			eapol.get_packet_type(),
+			eapol.get_type_string(),
+			eapol.get_data_length()));
+		EAP_TRACE_DATA_DEBUG(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("     source"), receive_network_id->get_source(), receive_network_id->get_source_length()));
+		EAP_TRACE_DATA_DEBUG(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("destination"), receive_network_id->get_destination(), receive_network_id->get_destination_length()));
+
+		status = eap_status_wrong_eapol_type;
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eapol_core_c::packet_send(
+	const eap_am_network_id_c * const send_network_id,
+	eap_buf_chain_wr_c * const sent_packet,
+	const u32_t header_offset,
+	const u32_t data_length,
+	const u32_t buffer_length)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+	EAP_ASSERT(header_offset < sent_packet->get_data_length());
+	EAP_ASSERT(data_length <= sent_packet->get_data_length());
+	EAP_ASSERT(sent_packet->get_data_length() <= buffer_length);
+
+	if (header_offset < eap_header_wr_c::get_header_length())
+	{
+		EAP_TRACE_DEBUG(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("packet_send: packet buffer corrupted.\n")));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_general_error);
+	}
+
+	eapol_header_wr_c eapol(
+		m_am_tools,
+		sent_packet->get_data_offset(
+			header_offset-eap_header_wr_c::get_header_length(), data_length),
+		data_length);
+
+	if (eapol.get_is_valid() == false)
+	{
+		EAP_TRACE_DEBUG(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("packet_send: packet buffer corrupted.\n")));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+	}
+
+	eapol.set_version(eapol_protocol_version_1);
+	eapol.set_packet_type(eapol_packet_type_eap);
+	eapol.set_data_length(static_cast<u16_t>(data_length));
+
+	{
+		eap_network_id_selector_c state_selector(
+			m_am_tools,
+			send_network_id);
+		if (state_selector.get_is_valid() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		EAP_TRACE_DATA_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("packet_send(): EAPOL-Key session"),
+			state_selector.get_data(state_selector.get_data_length()),
+			state_selector.get_data_length()));
+
+		eapol_key_state_c *session = m_eapol_key_state_map.get_handler(&state_selector);
+
+		bool encryption_on(false);
+
+		if (session != 0
+			&& session->get_is_valid() == true)
+		{
+			encryption_on = session->get_is_encryption_on();
+		}
+
+		EAP_TRACE_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("Read EAPOL-Key session: encryption is %s\n"),
+			((encryption_on == true) ? "on" : "off")));
+		
+		sent_packet->set_encrypt(encryption_on);
+	}
+
+	EAP_TRACE_DEBUG(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("<- EAPOL: %s, version=0x%02x, packet_type=0x%02x=%s, data_length=0x%04x, packet_length 0x%04x\n"),
+		(m_is_client == true) ? "client": "server",
+		eapol.get_version(),
+		eapol.get_packet_type(),
+		eapol.get_type_string(),
+		eapol.get_data_length(),
+		data_length));
+
+	if (m_shutdown_was_called == true
+		&& m_is_client == true)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("WARNING: EAP_Core: %s, eapol_core_c::packet_send(): %s packet dropped quietly because shutdown was already called.\n"),
+			 (m_is_client == true) ? "client": "server",
+			 eapol.get_type_string()));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
+	}
+
+	eap_status_e status = m_partner->packet_send(
+		send_network_id, sent_packet, header_offset-eapol_header_wr_c::get_header_length(),
+		data_length+eapol_header_wr_c::get_header_length(), buffer_length);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT u32_t eapol_core_c::get_header_offset(
+	u32_t * const MTU,
+	u32_t * const trailer_length)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	const u32_t offset = m_partner->get_header_offset(MTU, trailer_length);
+	(*MTU) -= eapol_header_wr_c::get_header_length();
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return offset+eapol_header_wr_c::get_header_length();
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eapol_core_c::eap_acknowledge(
+	const eap_am_network_id_c * const receive_network_id)
+{
+	// Any Network Protocol packet is accepted as a success indication.
+	// This is described in RFC 2284 "PPP Extensible Authentication Protocol (EAP)".
+
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	if (m_eap_core == 0)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	eap_status_e status = m_eap_core->eap_acknowledge(receive_network_id);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eapol_core_c::load_module(
+	const eap_type_value_e type,
+	const eap_type_value_e tunneling_type,
+	abs_eap_base_type_c * const partner,
+	eap_base_type_c ** const eap_type,
+	const bool is_client_when_true,
+	const eap_am_network_id_c * const receive_network_id)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	eap_status_e status = m_partner->load_module(
+		type,
+		tunneling_type,
+		partner,
+		eap_type,
+		is_client_when_true,
+		receive_network_id);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eapol_core_c::unload_module(const eap_type_value_e type)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	const eap_status_e status = m_partner->unload_module(type);
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eapol_core_c::restart_authentication(
+	const eap_am_network_id_c * const receive_network_id,
+	const bool is_client_when_true,
+	const bool force_clean_restart,
+	const bool from_timer)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eapol_core_c::restart_authentication(): %s\n"),
+		(m_is_client == true) ? "client": "server"));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: eapol_core_c::restart_authentication()");
+
+	eap_status_e status = eap_status_process_general_error;
+
+	if (receive_network_id->get_type() != eapol_ethernet_type_pae
+		&& receive_network_id->get_type() != eapol_ethernet_type_preauthentication)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("WARNING: eapol_core_c::restart_authentication(): Illegal Ethernet type %d\n"),
+			receive_network_id->get_type()));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_ethernet_type_not_supported);
+	}
+
+	if (is_client_when_true == true)
+	{
+		// Client sends a EAPOL-Start message.
+		(void) m_partner->cancel_timer(
+			this,
+			EAPOL_CORE_TIMER_SEND_START_AGAIN_ID);
+
+#if !defined(USE_EAPOL_KEY_STATE_OPTIMIZED_4_WAY_HANDSHAKE)
+#if !defined(NO_EAP_SESSION_CORE)
+		// First we remove possible EAP session.
+		(void) m_eap_core->synchronous_remove_eap_session(receive_network_id);
+#endif
+#endif //#if defined(USE_EAPOL_KEY_STATE_OPTIMIZED_4_WAY_HANDSHAKE)
+
+		// Here we swap the addresses.
+		eap_am_network_id_c send_network_id(m_am_tools,
+			receive_network_id->get_destination_id(),
+			receive_network_id->get_source_id(),
+			receive_network_id->get_type());
+		if (send_network_id.get_is_valid_data() == false)
+		{
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		eap_buf_chain_wr_c start_packet(
+			eap_write_buffer, 
+			m_am_tools, 
+			EAP_MAX_LOCAL_PACKET_BUFFER_LENGTH);
+
+		if (start_packet.get_is_valid() == false)
+		{
+			EAP_TRACE_DEBUG(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("packet buffer corrupted.\n")));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		u32_t buffer_size = EAP_MAX_LOCAL_PACKET_BUFFER_LENGTH;
+		EAP_ASSERT_ALWAYS(m_MTU > m_trailer_length);
+		if (m_MTU-m_trailer_length < buffer_size)
+		{
+			buffer_size = m_MTU-m_trailer_length;
+		}
+
+		EAP_ASSERT_ALWAYS(buffer_size >= m_eapol_header_offset);
+		eapol_header_wr_c eapol(
+			m_am_tools,
+			start_packet.get_data_offset(
+				m_eapol_header_offset,
+				(buffer_size-m_eapol_header_offset)),
+			(buffer_size-m_eapol_header_offset));
+
+		if (eapol.get_is_valid() == false)
+		{
+			EAP_TRACE_DEBUG(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("packet_send: packet buffer corrupted.\n")));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+		}
+
+		eapol.set_version(eapol_protocol_version_1);
+		eapol.set_packet_type(eapol_packet_type_start);
+		eapol.set_data_length(0);
+
+		start_packet.set_data_length(
+			m_eapol_header_offset
+			+eapol_header_wr_c::get_header_length()
+			+eapol.get_data_length());
+
+		EAP_TRACE_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("<- EAPOL: %s, version=0x%02x, packet_type=0x%02x=%s, data_length=0x%04x, packet length 0x%04x\n"),
+			(m_is_client == true) ? "client": "server",
+			eapol.get_version(),
+			eapol.get_packet_type(),
+			eapol.get_type_string(),
+			eapol.get_data_length(),
+			eapol_header_wr_c::get_header_length()+eapol.get_data_length()));
+
+		status = m_partner->packet_send(
+			&send_network_id,
+			&start_packet,
+			m_eapol_header_offset,
+			eapol_header_wr_c::get_header_length()+eapol.get_data_length(),
+			buffer_size);
+
+		if (from_timer == false)
+		{
+			// Reset EAPOL-Start counter because this is not a resending.
+			m_eapol_starts_sent = 1;
+			// Send notification
+			eap_state_notification_c notification(
+				m_am_tools,
+				&send_network_id,
+				true, // m_is_client
+				eap_state_notification_generic,
+				eap_protocol_layer_eapol,
+				eap_type_none,
+				eapol_state_none,
+				eapol_state_start_sent,
+				0 /* EAP identifier not valid here */,
+				false);
+			state_notification(&notification);
+		}
+
+		// This copy is important because timer needs data that is allocated from heap.
+		eap_am_network_id_c * const copy_receive_network_id = receive_network_id->copy();
+		eap_automatic_variable_c<eap_am_network_id_c> automatic_copy_receive_network_id(
+			m_am_tools,
+			copy_receive_network_id);
+
+		if (copy_receive_network_id == 0
+			|| copy_receive_network_id->get_is_valid_data() == false)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		// Timer will hold copy_send_network_id data.
+		automatic_copy_receive_network_id.do_not_free_variable();
+
+		status = m_partner->set_timer(
+			this,
+			EAPOL_CORE_TIMER_SEND_START_AGAIN_ID, 
+			copy_receive_network_id,
+			m_eapol_start_interval);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		EAP_TRACE_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("TIMER: %s: EAPOL_CORE_TIMER_SEND_START_AGAIN_ID set %d ms.\n"),
+			 (m_is_client == true ? "client": "server"),
+			 m_eapol_start_interval));
+		
+	}
+#if defined(USE_EAP_CORE_SERVER)
+	else 
+	{
+		// Server.
+
+		if (m_eap_core == 0)
+		{
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		if (force_clean_restart == true)
+		{
+			// It is bad idea to terminate on-going authentication
+			// when EAPOL-Start is received.
+			// Because of that synchronous_remove_eap_session() is called
+			// only when force_clean_restart is true.
+#if !defined(NO_EAP_SESSION_CORE)
+			bool previous_block = m_block_state_notifications;
+			m_block_state_notifications = true;
+			(void) m_eap_core->synchronous_remove_eap_session(receive_network_id);
+			m_block_state_notifications = previous_block;
+#endif
+		}
+
+		if (m_authentication_type == eapol_key_authentication_type_RSNA_EAP
+			|| m_authentication_type == eapol_key_authentication_type_WPA_EAP
+			|| m_authentication_type == eapol_key_authentication_type_802_1X
+#if defined(EAP_USE_WPXM)
+			|| m_authentication_type == eapol_key_authentication_type_WPXM
+#endif //#if defined(EAP_USE_WPXM)
+			|| m_authentication_type == eapol_key_authentication_type_WFA_SC
+			)
+		{
+			status = m_eap_core->send_eap_identity_request(receive_network_id);
+		}
+		else
+		{
+			status = eap_status_ok;
+		}
+	}
+
+#else
+
+	EAP_UNREFERENCED_PARAMETER(force_clean_restart); // Only server version uses this.
+
+#endif //#if defined(USE_EAP_CORE_SERVER)
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eapol_core_c::check_pmksa_cache(
+	eap_array_c<eap_am_network_id_c> * const bssid_sta_receive_network_ids,
+	const eapol_key_authentication_type_e selected_eapol_key_authentication_type,
+	const eapol_RSNA_key_header_c::eapol_RSNA_cipher_e pairwise_key_cipher_suite,
+	const eapol_RSNA_key_header_c::eapol_RSNA_cipher_e group_key_cipher_suite)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);	
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("eapol_core_c::check_pmksa_cache(): %s\n"),
+		(m_is_client == true) ? "client": "server"));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: eapol_core_c::check_pmksa_cache()");
+
+	eap_status_e status = eap_status_ok;
+
+	for (u32_t ind = 0ul; ind < bssid_sta_receive_network_ids->get_object_count();)
+	{
+		eap_am_network_id_c * const receive_network_id = bssid_sta_receive_network_ids->get_object(ind);
+		if (receive_network_id == 0)
+		{
+			bssid_sta_receive_network_ids->reset();
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		// Here we swap the addresses.
+		eap_am_network_id_c send_network_id(
+			m_am_tools,
+			receive_network_id->get_destination_id(),
+			receive_network_id->get_source_id(),
+			receive_network_id->get_type());
+		if (send_network_id.get_is_valid_data() == false)
+		{
+			bssid_sta_receive_network_ids->reset();
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		eap_network_id_selector_c state_selector(
+			m_am_tools,
+			&send_network_id);
+		if (state_selector.get_is_valid() == false)
+		{
+			bssid_sta_receive_network_ids->reset();
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		EAP_TRACE_DATA_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("check_pmksa_cache(): checks PMKSA EAPOL-Key session"),
+			 state_selector.get_data(state_selector.get_data_length()),
+			 state_selector.get_data_length()));
+		
+		eapol_key_state_c *session = m_eapol_key_state_map.get_handler(&state_selector);
+		
+		if (session == 0
+			|| selected_eapol_key_authentication_type == eapol_key_authentication_type_RSNA_PSK
+			|| selected_eapol_key_authentication_type == eapol_key_authentication_type_WPA_PSK
+			|| session->check_pmksa_cache(
+				selected_eapol_key_authentication_type,
+				pairwise_key_cipher_suite,
+				group_key_cipher_suite) != eap_status_ok)
+		{
+			// No cached PMKID for this address and security suite.
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("No cached PMKID for this address"),
+				 state_selector.get_data(state_selector.get_data_length()),
+				 state_selector.get_data_length()));
+
+			status = bssid_sta_receive_network_ids->remove_object(ind);
+			if (status != eap_status_ok)
+			{
+				bssid_sta_receive_network_ids->reset();
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			// Note here we do not increase index because we removed the current object.
+		}
+		else
+		{
+			// Check the next index.
+			++ind;
+		}
+	} // for()
+
+	if (bssid_sta_receive_network_ids->get_object_count() > 0ul)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);	
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	else
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);	
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_not_found);
+	}
+}
+
+//--------------------------------------------------
+
+/**
+ * This function removes PMKSA from cache.
+ * @param receive_network_id carries the MAC addresses.
+ * MAC address of Authenticator should be in source address.
+ * MAC address of Supplicant should be in destination address.
+ */
+EAP_FUNC_EXPORT eap_status_e eapol_core_c::remove_pmksa_from_cache(
+	const eap_am_network_id_c * const receive_network_id
+	)
+{
+	eap_status_e status = eap_status_process_general_error;
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("%s: eapol_core_c::remove_pmksa_from_cache().\n"),
+		 (m_is_client == true) ? "client": "server"));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: eapol_core_c::remove_pmksa_from_cache()");
+
+	if (receive_network_id == 0)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	// Here we swap the addresses.
+	eap_am_network_id_c send_network_id(
+		m_am_tools,
+		receive_network_id->get_destination_id(),
+		receive_network_id->get_source_id(),
+		receive_network_id->get_type());
+	if (send_network_id.get_is_valid_data() == false)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	status = remove_eapol_key_state(
+		&send_network_id);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("WARNING: eapol_core_c::disassociation(): ")
+			 EAPL("remove_eapol_key_state(), eap_status_e %d\n"),
+			status));
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eapol_core_c::start_preauthentication(
+	const eap_am_network_id_c * const receive_network_id,
+	const eapol_key_authentication_type_e authentication_type)
+{
+	eap_status_e status = eap_status_process_general_error;
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("%s: eapol_core_c::start_preauthentication().\n"),
+		 (m_is_client == true) ? "client": "server"));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: eapol_core_c::start_preauthentication()");
+
+#if defined(USE_EAPOL_KEY_STATE)
+
+	if (receive_network_id->get_type() != eapol_ethernet_type_preauthentication)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("WARNING: eapol_core_c::start_preauthentication(): Illegal Ethernet type 0x%04x\n"),
+			receive_network_id->get_type()));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_ethernet_type_not_supported);
+	}
+
+
+	if (m_authentication_type != authentication_type)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("WARNING: eapol_core_c::start_preauthentication(): Illegal authentication type 0x%08x, ")
+			 EAPL("it should be 0x%08x\n"),
+			 authentication_type,
+			 m_authentication_type));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_wrong_authentication_type);
+	}
+
+
+	// Here we swap the addresses.
+	eap_am_network_id_c send_network_id(
+		m_am_tools,
+		receive_network_id->get_destination_id(),
+		receive_network_id->get_source_id(),
+		receive_network_id->get_type());
+	if (send_network_id.get_is_valid_data() == false)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	eap_network_id_selector_c state_selector(
+		m_am_tools,
+		&send_network_id);
+
+	if (state_selector.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("start_preauthentication(): EAPOL-Key session"),
+		state_selector.get_data(state_selector.get_data_length()),
+		state_selector.get_data_length()));
+
+
+	eapol_key_state_c *eapol_key_state = m_eapol_key_state_map.get_handler(&state_selector);
+
+	if (eapol_key_state != 0)
+	{
+		// Reuse the session.
+		eapol_key_state->unset_marked_removed();
+
+		status = eapol_key_state->reset();
+		if (status != eap_status_ok)
+		{
+			// We cannot reuse the session.
+			EAP_TRACE_ERROR(
+				m_am_tools, 
+				TRACE_FLAGS_ERROR, 
+				(EAPL("start_preauthentication(): eapol_key_state NOT reused.\n")));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+
+	if (eapol_key_state == 0)
+	{
+		eapol_key_state = new eapol_key_state_c(
+			m_am_tools,
+			this,
+			m_partner,
+			m_is_client,
+			receive_network_id,
+			authentication_type);
+		if (eapol_key_state == 0
+			|| eapol_key_state->get_is_valid() == false)
+		{
+			if (eapol_key_state != 0)
+			{
+				eapol_key_state->shutdown();
+			}
+			else
+			{
+				EAP_TRACE_DEBUG(
+					m_am_tools, 
+					TRACE_FLAGS_DEFAULT, 
+					(EAPL("WARNING: eapol_core_c::start_preauthentication(): Cannot run eapol_key_state->shutdown() 0x%08x\n"),
+					eapol_key_state));
+			}
+			delete eapol_key_state;
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		status = eapol_key_state->initialize_preauthentication(
+			receive_network_id,
+			authentication_type);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = m_eapol_key_state_map.add_handler(&state_selector, eapol_key_state);
+		if (status != eap_status_ok)
+		{
+			eapol_key_state->shutdown();
+			delete eapol_key_state;
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+	else
+	{
+		status = eapol_key_state->initialize_preauthentication(
+			receive_network_id,
+			authentication_type);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+
+	status = eapol_key_state->configure();
+	if (status != eap_status_ok)
+	{
+		status = remove_eapol_key_state(
+			&send_network_id);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("WARNING: eapol_core_c::start_preauthentication(): remove_eapol_key_state(), eap_status_e %d\n"),
+				status));
+		}
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+
+	// This call does cause clean restart. On-going authentication is terminated.
+	status = restart_authentication(
+		receive_network_id,
+		m_is_client,
+		true,
+		false);
+
+#endif //#if defined(USE_EAPOL_KEY_STATE)
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+eap_status_e eapol_core_c::copy_eapol_key_state(
+	const eap_am_network_id_c * const old_receive_network_id, ///< source includes remote address, destination includes local address.
+	const eap_am_network_id_c * const new_receive_network_id ///< source includes remote address, destination includes local address.
+	)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);	
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("%s: eapol_core_c::copy_eapol_key_state()\n"),
+		(m_is_client == true) ? "client": "server"));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: eapol_core_c::copy_eapol_key_state()");
+
+	eap_status_e status(eap_status_process_general_error);
+
+	if (old_receive_network_id == 0
+		|| old_receive_network_id->get_is_valid_data() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	if (new_receive_network_id == 0
+		|| new_receive_network_id->get_is_valid_data() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+#if defined(USE_EAPOL_KEY_STATE)
+
+	// Here we swap the addresses.
+	eap_am_network_id_c old_send_network_id(
+		m_am_tools,
+		old_receive_network_id->get_destination_id(),
+		old_receive_network_id->get_source_id(),
+		old_receive_network_id->get_type());
+	if (old_send_network_id.get_is_valid_data() == false)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	eap_network_id_selector_c old_state_selector(
+		m_am_tools,
+		&old_send_network_id);
+
+	if (old_state_selector.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("copy_eapol_key_state(): old EAPOL-Key session"),
+		old_state_selector.get_data(),
+		old_state_selector.get_data_length()));
+
+	eapol_key_state_c *old_eapol_key_state = m_eapol_key_state_map.get_handler(&old_state_selector);
+
+	if (old_eapol_key_state != 0)
+	{
+		{
+			// Here we remove the old state from map. Note the old state is not deleted.
+			// Later we add the modified old map to new location in the map.
+			status = m_eapol_key_state_map.remove_handler(&old_state_selector, false);
+			if (status != eap_status_ok)
+			{
+				old_eapol_key_state->shutdown();
+				delete old_eapol_key_state;
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			// Set up the old state and add it to m_eapol_key_state_map.
+
+			// Here we swap the addresses.
+			eap_am_network_id_c new_send_network_id(
+				m_am_tools,
+				new_receive_network_id->get_destination_id(),
+				new_receive_network_id->get_source_id(),
+				new_receive_network_id->get_type());
+			if (new_send_network_id.get_is_valid_data() == false)
+			{
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+			}
+
+#if defined(EAP_USE_WPXM)
+			status = old_eapol_key_state->set_WPXM_parameters(new_receive_network_id);
+			if (status != eap_status_ok)
+			{
+				old_eapol_key_state->shutdown();
+				delete old_eapol_key_state;
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+#endif //#if defined(EAP_USE_WPXM)
+
+			eap_network_id_selector_c new_state_selector(
+				m_am_tools,
+				&new_send_network_id);
+
+			if (new_state_selector.get_is_valid() == false)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+			}
+
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("copy_eapol_key_state(): new EAPOL-Key session"),
+				new_state_selector.get_data(),
+				new_state_selector.get_data_length()));
+
+			status = m_eapol_key_state_map.add_handler(&new_state_selector, old_eapol_key_state);
+			if (status != eap_status_ok)
+			{
+				old_eapol_key_state->shutdown();
+				delete old_eapol_key_state;
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+	}
+
+#endif //#if defined(USE_EAPOL_KEY_STATE)
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);	
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+eap_status_e eapol_core_c::generate_new_pmksa(
+	eapol_key_state_c * * const new_eapol_key_state,
+	const eap_am_network_id_c * const old_receive_network_id, ///< source includes remote address, destination includes local address.
+	const eap_am_network_id_c * const new_receive_network_id ///< source includes remote address, destination includes local address.
+	)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);	
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("%s: eapol_core_c::generate_new_pmksa()\n"),
+		(m_is_client == true) ? "client": "server"));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: eapol_core_c::generate_new_pmksa()");
+
+	eap_status_e status(eap_status_process_general_error);
+
+	if (new_eapol_key_state == 0)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	if (old_receive_network_id == 0
+		|| old_receive_network_id->get_is_valid_data() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	if (new_receive_network_id == 0
+		|| new_receive_network_id->get_is_valid_data() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+#if defined(USE_EAPOL_KEY_STATE)
+
+	// Here we swap the addresses.
+	eap_am_network_id_c old_send_network_id(
+		m_am_tools,
+		old_receive_network_id->get_destination_id(),
+		old_receive_network_id->get_source_id(),
+		old_receive_network_id->get_type());
+	if (old_send_network_id.get_is_valid_data() == false)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	eap_network_id_selector_c old_state_selector(
+		m_am_tools,
+		&old_send_network_id);
+
+	if (old_state_selector.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("generate_new_pmksa(): old EAPOL-Key session"),
+		old_state_selector.get_data(),
+		old_state_selector.get_data_length()));
+
+	eapol_key_state_c *old_eapol_key_state = m_eapol_key_state_map.get_handler(&old_state_selector);
+
+	if (old_eapol_key_state != 0)
+	{
+		*new_eapol_key_state = old_eapol_key_state->copy(new_receive_network_id);
+
+		if (*new_eapol_key_state == 0
+			|| (*new_eapol_key_state)->get_is_valid() == false)
+		{
+			if (*new_eapol_key_state != 0)
+			{
+				(*new_eapol_key_state)->shutdown();
+				delete *new_eapol_key_state;
+				*new_eapol_key_state = 0;
+			}
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		if (*new_eapol_key_state != 0)
+		{
+			// Here we swap the addresses.
+			eap_am_network_id_c new_send_network_id(
+				m_am_tools,
+				new_receive_network_id->get_destination_id(),
+				new_receive_network_id->get_source_id(),
+				new_receive_network_id->get_type());
+			if (new_send_network_id.get_is_valid_data() == false)
+			{
+				(*new_eapol_key_state)->shutdown();
+				delete *new_eapol_key_state;
+				*new_eapol_key_state = 0;
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+			}
+
+			eap_network_id_selector_c new_state_selector(
+				m_am_tools,
+				&new_send_network_id);
+
+			if (new_state_selector.get_is_valid() == false)
+			{
+				(*new_eapol_key_state)->shutdown();
+				delete *new_eapol_key_state;
+				*new_eapol_key_state = 0;
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+			}
+
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("generate_new_pmksa(): new EAPOL-Key session"),
+				new_state_selector.get_data(),
+				new_state_selector.get_data_length()));
+
+			status = m_eapol_key_state_map.add_handler(&new_state_selector, *new_eapol_key_state);
+			if (status != eap_status_ok)
+			{
+				(*new_eapol_key_state)->shutdown();
+				delete *new_eapol_key_state;
+				*new_eapol_key_state = 0;
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+	}
+	else
+	{
+		status = eap_status_not_found;
+	}
+
+#endif //#if defined(USE_EAPOL_KEY_STATE)
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);	
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+eap_status_e eapol_core_c::read_reassociation_parameters(
+	const eap_am_network_id_c * const old_receive_network_id, ///< source includes remote address, destination includes local address.
+	const eap_am_network_id_c * const new_receive_network_id, ///< source includes remote address, destination includes local address.
+	const eapol_key_authentication_type_e authentication_type,
+	eap_variable_data_c * const PMKID,
+	const eap_variable_data_c * const received_WPA_ie,
+	const eap_variable_data_c * const sent_WPA_ie)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);	
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("%s: eapol_core_c::read_reassociation_parameters()\n"),
+		(m_is_client == true) ? "client": "server"));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: eapol_core_c::read_reassociation_parameters()");
+
+	eap_status_e status(eap_status_process_general_error);
+
+#if defined(USE_EAPOL_KEY_STATE)
+
+	// No need to check authentication type anymore. It can be changed in reassociation.
+
+#if defined(EAP_USE_WPXM)
+	if (authentication_type == eapol_key_authentication_type_WPXM)
+	{
+		status = copy_eapol_key_state(old_receive_network_id, new_receive_network_id);
+		if (status != eap_status_ok)
+		{
+			// We cannot copy the session.
+			EAP_TRACE_ERROR(
+				m_am_tools, 
+				TRACE_FLAGS_ERROR, 
+				(EAPL("eapol_core_c::read_reassociation_parameters(): cannot copy eapol_key_state.\n")));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+#else
+	EAP_UNREFERENCED_PARAMETER(old_receive_network_id);
+#endif //#if defined(EAP_USE_WPXM)
+
+	// Here we swap the addresses.
+	eap_am_network_id_c new_send_network_id(
+		m_am_tools,
+		new_receive_network_id->get_destination_id(),
+		new_receive_network_id->get_source_id(),
+		new_receive_network_id->get_type());
+	if (new_send_network_id.get_is_valid_data() == false)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	eap_network_id_selector_c state_selector(
+		m_am_tools,
+		&new_send_network_id);
+
+	if (state_selector.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("read_reassociation_parameters(): EAPOL-Key session"),
+		state_selector.get_data(state_selector.get_data_length()),
+		state_selector.get_data_length()));
+
+	eapol_key_state_c *eapol_key_state = m_eapol_key_state_map.get_handler(&state_selector);
+
+	if (eapol_key_state == 0
+		&& authentication_type == eapol_key_authentication_type_RSNA_EAP)
+	{
+		// Creates a new PMKSA based on old PMKSA and the new MAC address of new access point.
+		status = generate_new_pmksa(&eapol_key_state, old_receive_network_id, new_receive_network_id);
+		if (status != eap_status_ok)
+		{
+			// We cannot generate a new PMKSA.
+			EAP_TRACE_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("eapol_core_c::read_reassociation_parameters(): new eapol_key_state NOT generated.\n")));
+			status = eap_status_not_found;
+		}
+	}
+
+	if (eapol_key_state != 0)
+	{
+		status = eapol_key_state->reset_cached_pmksa();
+		if (status != eap_status_ok)
+		{
+			// We cannot reuse the session.
+			EAP_TRACE_ERROR(
+				m_am_tools, 
+				TRACE_FLAGS_ERROR, 
+				(EAPL("eapol_core_c::read_reassociation_parameters(): eapol_key_state NOT reused.\n")));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		// We have state for this connection.
+		status = eapol_key_state->read_reassociation_parameters(
+			new_receive_network_id, ///< source includes remote address, destination includes local address.
+			authentication_type,
+			PMKID,
+			received_WPA_ie,
+			sent_WPA_ie);
+		if (status != eap_status_ok)
+		{
+			// ERROR, Cannot reassociate.
+
+			EAP_TRACE_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("ERROR: eapol_core_c::read_reassociation_parameters(): Cannot reassociate.\n")));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);	
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+	else
+	{
+		status = eap_status_not_found;
+	}
+
+#endif //#if defined(USE_EAPOL_KEY_STATE)
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);	
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eapol_core_c::start_reassociation(
+	const eap_am_network_id_c * const receive_network_id,
+	const eapol_key_authentication_type_e authentication_type,
+	const eap_variable_data_c * const /*PMKID*/)
+{
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("%s: eapol_core_c::start_reassociation()\n"),
+		(m_is_client == true) ? "client": "server"));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: eapol_core_c::start_reassociation()");
+
+	// Here we swap the addresses.
+	eap_am_network_id_c send_network_id(
+		m_am_tools,
+		receive_network_id->get_destination_id(),
+		receive_network_id->get_source_id(),
+		receive_network_id->get_type());
+	if (send_network_id.get_is_valid_data() == false)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	eap_network_id_selector_c state_selector(
+		m_am_tools,
+		&send_network_id);
+
+	if (state_selector.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("start_reassociation(): EAPOL-Key session"),
+		state_selector.get_data(state_selector.get_data_length()),
+		state_selector.get_data_length()));
+
+	eap_status_e status(eap_status_process_general_error);
+
+	eapol_key_state_c *eapol_key_state = m_eapol_key_state_map.get_handler(&state_selector);
+
+	if (eapol_key_state != 0)
+	{
+		status = eapol_key_state->reset_cached_pmksa();
+		if (status != eap_status_ok)
+		{
+			// We cannot reuse the session.
+			EAP_TRACE_ERROR(
+				m_am_tools, 
+				TRACE_FLAGS_ERROR, 
+				(EAPL("eapol_core_c::start_reassociation(): eapol_key_state NOT reused.\n")));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+#if defined(USE_EAPOL_KEY_STATE)
+		if (m_skip_start_4_way_handshake == true
+			&& (authentication_type == eapol_key_authentication_type_RSNA_EAP
+				|| authentication_type == eapol_key_authentication_type_RSNA_PSK
+				|| authentication_type == eapol_key_authentication_type_WPA_EAP
+				|| authentication_type == eapol_key_authentication_type_WPA_PSK))
+		{
+			// This is test to skip 4-Way Handshake start.
+			EAP_TRACE_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("WARNING: eapol_core_c::start_reassociation(): ")
+				 EAPL("skips start_4_way_handshake()\n")));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+		}
+		else
+#endif //#if defined(USE_EAPOL_KEY_STATE)
+		{
+#if !defined(NO_EAPOL_KEY_STATE_SERVER)
+			status = eapol_key_state->start_4_way_handshake(
+				receive_network_id);
+#endif //#if !defined(NO_EAPOL_KEY_STATE_SERVER)
+		}
+
+#if !defined(NO_EAPOL_KEY_STATE_SERVER)
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("ERROR: eapol_core_c::start_reassociation(): ")
+				 EAPL("start_4_way_handshake() failed, eap_status_e %d\n"),
+				 status));
+		}
+		else
+		{
+			EAP_TRACE_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("eapol_core_c::start_reassociation(): ")
+				 EAPL("start_4_way_handshake(), eap_status_e %d\n"),
+				 status));
+		}
+#endif //#if !defined(NO_EAPOL_KEY_STATE_SERVER)
+
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+eap_status_e eapol_core_c::complete_reassociation(
+	const eapol_wlan_authentication_state_e reassociation_result,
+	const eap_am_network_id_c * const receive_network_id,
+	const eapol_key_authentication_type_e authentication_type,
+	const eap_variable_data_c * const received_WPA_IE,
+	const eap_variable_data_c * const sent_WPA_IE,
+	const eapol_RSNA_key_header_c::eapol_RSNA_cipher_e pairwise_key_cipher_suite,
+	const eapol_RSNA_key_header_c::eapol_RSNA_cipher_e group_key_cipher_suite
+	)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("%s: eapol_core_c::complete_reassociation()\n"),
+		(m_is_client == true) ? "client": "server"));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: eapol_core_c::complete_reassociation()");
+
+#if defined(USE_EAPOL_KEY_STATE)
+
+	// Here we swap the addresses.
+	eap_am_network_id_c send_network_id(
+		m_am_tools,
+		receive_network_id->get_destination_id(),
+		receive_network_id->get_source_id(),
+		receive_network_id->get_type());
+	if (send_network_id.get_is_valid_data() == false)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+
+	// No need to check previous authentication type. It can be changed in reassociation.
+	m_authentication_type = authentication_type;
+
+
+	eap_network_id_selector_c state_selector(
+		m_am_tools,
+		&send_network_id);
+
+	if (state_selector.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("complete_reassociation(): EAPOL-Key session"),
+		state_selector.get_data(state_selector.get_data_length()),
+		state_selector.get_data_length()));
+
+	eapol_key_state_c *eapol_key_state = m_eapol_key_state_map.get_handler(&state_selector);
+
+	if (eapol_key_state != 0)
+	{
+		// We have state for this connection.
+		eap_status_e status = eapol_key_state->complete_reassociation(
+			reassociation_result,
+			receive_network_id,
+			authentication_type,
+			received_WPA_IE,
+			sent_WPA_IE,
+			pairwise_key_cipher_suite,
+			group_key_cipher_suite
+			);
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);	
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	else
+#endif //#if defined(USE_EAPOL_KEY_STATE)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);	
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_authentication_failure);
+	}
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eapol_core_c::send_logoff(
+	const eap_am_network_id_c * const receive_network_id)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("%s: eapol_core_c::send_logoff()\n"),
+		(m_is_client == true) ? "client": "server"));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: eapol_core_c::send_logoff()");
+
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	eap_status_e status(eap_status_not_supported);
+
+	if (m_eap_core == 0)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	if (receive_network_id == 0)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	// Here we swap the addresses.
+	eap_am_network_id_c send_network_id(m_am_tools,
+		receive_network_id->get_destination_id(),
+		receive_network_id->get_source_id(),
+		receive_network_id->get_type());
+	if (send_network_id.get_is_valid_data() == false)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	eap_buf_chain_wr_c logoff_packet(
+		eap_write_buffer, 
+		m_am_tools, 
+		EAP_MAX_LOCAL_PACKET_BUFFER_LENGTH);
+
+	if (logoff_packet.get_is_valid() == false)
+	{
+		EAP_TRACE_DEBUG(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("packet buffer corrupted.\n")));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	u32_t data_length = EAP_MAX_LOCAL_PACKET_BUFFER_LENGTH;
+
+	if (data_length > 0)
+	{
+		data_length = 0;
+	}
+
+	eapol_header_wr_c eapol(
+		m_am_tools,
+		logoff_packet.get_data_offset(
+			m_eapol_header_offset, m_eapol_header_offset+data_length),
+		m_eapol_header_offset+data_length);
+
+	if (eapol.get_is_valid() == false)
+	{
+		EAP_TRACE_DEBUG(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("packet_send: packet buffer corrupted.\n")));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+	}
+
+	eapol.set_version(eapol_protocol_version_1);
+	eapol.set_packet_type(eapol_packet_type_logoff);
+	eapol.set_data_length(static_cast<u16_t>(data_length));
+
+	logoff_packet.set_data_length(m_eapol_header_offset+eapol_header_wr_c::get_header_length()+data_length);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("<- EAPOL: %s, version=0x%02x, packet_type=0x%02x=%s, data_length=0x%04x, packet length 0x%04x\n"),
+		(m_is_client == true) ? "client": "server",
+		eapol.get_version(),
+		eapol.get_packet_type(),
+		eapol.get_type_string(),
+		eapol.get_data_length(),
+		eapol.get_header_length()+eapol.get_data_length()));
+
+	status = m_partner->packet_send(
+		&send_network_id, &logoff_packet, m_eapol_header_offset,
+		eapol.get_header_length()+eapol.get_data_length(), EAP_MAX_LOCAL_PACKET_BUFFER_LENGTH);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eapol_core_c::packet_data_crypto_keys(
+	const eap_am_network_id_c * const send_network_id,
+	const eap_master_session_key_c * const master_session_key
+	)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("%s: eapol_core_c::packet_data_crypto_keys()\n"),
+		(m_is_client == true) ? "client": "server"));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: eapol_core_c::packet_data_crypto_keys()");
+
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	eap_status_e status = eap_status_process_general_error;
+
+	if (master_session_key->get_is_valid() == true)
+	{
+		EAP_TRACE_DATA_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("master session key"),
+			 master_session_key->get_data(master_session_key->get_data_length()),
+			 master_session_key->get_data_length()));
+	}
+
+#if defined(USE_EAPOL_KEY_STATE)
+
+	eap_network_id_selector_c state_selector(
+		m_am_tools,
+		send_network_id);
+
+	if (state_selector.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("packet_data_crypto_keys(): EAPOL-Key session"),
+		state_selector.get_data(state_selector.get_data_length()),
+		state_selector.get_data_length()));
+
+	eapol_key_state_c *eapol_key_state = m_eapol_key_state_map.get_handler(&state_selector);
+
+	if (eapol_key_state != 0)
+	{
+		status = eapol_key_state->set_pairwise_PMK(
+			master_session_key,
+			send_network_id);
+	}
+	else
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("ERROR: packet_data_crypto_keys() failed, no eapol_key_state_c object\n")));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_handler_does_not_exists_error);
+	}
+
+#else
+
+	// Store the session key so it can be used when EAPOL-Key is received.
+	m_master_session_key.reset();
+
+	status = m_master_session_key.set_copy_of_buffer(master_session_key);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+#endif //#if defined(USE_EAPOL_KEY_STATE)
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eapol_core_c::packet_data_session_key(
+	const eap_am_network_id_c * const send_network_id,
+	const eapol_session_key_c * const key)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("%s: eapol_core_c::packet_data_session_key()\n"),
+		(m_is_client == true) ? "client": "server"));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: eapol_core_c::packet_data_session_key()");
+
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	eap_status_e status = eap_status_process_general_error;
+
+	if (key->get_is_valid() == true)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("eapol session key: type 0x%02x, index 0x%02x, tx %d\n"),
+			 key->get_key_type(),
+			 key->get_key_index(),
+			 key->get_key_tx_bit()));
+
+		EAP_TRACE_DATA_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("eapol session key"),
+			 key->get_key()->get_data(key->get_key()->get_data_length()),
+			 key->get_key()->get_data_length()));
+	}
+
+	// Forward the keys to lower layers
+	status = m_partner->packet_data_session_key(
+			send_network_id,
+			key);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+#if !defined(USE_EAPOL_KEY_STATE)
+
+//
+eap_status_e eapol_core_c::handle_RC4_key_descriptor(
+	const eap_am_network_id_c * const receive_network_id,
+	eapol_RC4_key_header_c* const packet,
+	const u32_t packet_length)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("%s: eapol_core_c::handle_RC4_key_descriptor()\n"),
+		(m_is_client == true) ? "client": "server"));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: eapol_core_c::handle_RC4_key_descriptor()");
+
+	eap_status_e status = eap_status_process_general_error;
+
+	// Check the packet length
+	if (static_cast<u32_t>(packet->get_header_length()) != packet_length
+		&& static_cast<u32_t>(packet->get_header_length() + packet->get_key_length()) != packet_length)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("ERROR: Illegal EAPOL-Key frame length, packet->get_header_length() %d, packet_length %d\n"),
+			packet->get_header_length(),
+			packet_length));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_illegal_packet_error);
+	}
+
+	// Get MS-MPPE-Recv-Key and MS-MPPE-Send-Key
+	// Recv-Key is the first 32 bytes of master session key and Send-Key is the next 32 bytes.
+	eap_variable_data_c mppe_recv_key(m_am_tools);
+	eap_variable_data_c mppe_send_key(m_am_tools);
+	if (m_master_session_key.get_data_length() == 16ul)
+	{
+		status = mppe_recv_key.set_buffer(
+			m_master_session_key.get_data(m_master_session_key.get_data_length()),
+			m_master_session_key.get_data_length(),
+			false,
+			false);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = mppe_send_key.set_buffer(
+			m_master_session_key.get_data(m_master_session_key.get_data_length()),
+			m_master_session_key.get_data_length(),
+			false,
+			false);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+	else
+	{
+		status = mppe_recv_key.set_buffer(
+			m_master_session_key.get_data(MPPE_KEY_LENGTH),
+			MPPE_KEY_LENGTH,
+			false,
+			false);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = mppe_send_key.set_buffer(
+			m_master_session_key.get_data_offset(MPPE_KEY_LENGTH, MPPE_KEY_LENGTH),
+			MPPE_KEY_LENGTH,
+			false,
+			false);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	if (mppe_recv_key.get_is_valid() == false
+		|| mppe_send_key.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	// Verify the the MD5 signature in Eapol-Key
+	crypto_md5_c md5(m_am_tools);
+	crypto_hmac_c hmac_md5(m_am_tools, &md5, false);
+	if (hmac_md5.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	// MPPE-Send-Key is used as the signature key.
+	if (hmac_md5.hmac_set_key(&mppe_send_key) != eap_status_ok)
+	{
+		EAP_TRACE_DEBUG(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("ERROR: hmac_md5_init failed\n")));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	// Save the signature from the packet
+	eap_variable_data_c signature(m_am_tools);
+	status = signature.set_copy_of_buffer(packet->get_key_signature(), EAPOL_RC4_KEY_SIGNATURE_LENGTH);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// Replace the signature with zeros.
+	packet->zero_key_signature(m_am_tools);
+
+	// Send the data to HMAC-MD5 module
+	if (hmac_md5.hmac_update(packet->get_header_buffer(packet_length), packet_length) != eap_status_ok)
+	{
+		EAP_TRACE_DEBUG(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("ERROR: hmac_md5_update failed\n")));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);		
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_general_error);
+	}
+
+	// Get the calculated signature
+	u8_t tmp_signature[EAPOL_RC4_KEY_SIGNATURE_LENGTH];
+	u32_t length = EAPOL_RC4_KEY_SIGNATURE_LENGTH;
+	if (hmac_md5.hmac_final(tmp_signature, &length) != eap_status_ok)
+	{
+		EAP_TRACE_DEBUG(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("ERROR: hmac_md5_final failed\n")));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_general_error);
+	}
+	
+	// Compare the calculated and original signature
+	if (m_am_tools->memcmp(
+		tmp_signature,
+		signature.get_data(
+			EAPOL_RC4_KEY_SIGNATURE_LENGTH),
+			EAPOL_RC4_KEY_SIGNATURE_LENGTH) != 0)
+	{
+		// Signatures did not match. Something's wrong.
+		EAP_TRACE_DEBUG(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("ERROR: EAPOL-Key HMAC-MD5 check failed.\n")));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+	}
+	EAP_TRACE_DEBUG(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("EAPOL-Key HMAC-MD5 check passed.\n")));
+
+	eap_variable_data_c key_out(m_am_tools);
+	// Decrypt the RC4 encrypted key
+	if (packet->get_key() == 0)
+	{
+		// EAPOL-Key does not contain the key. This means that we should use
+		// the first bytes from MS-MPPE-Recv-Key as the key. There is a slight 
+		// confusion in draft-congdon-radius-8021x-23.txt regarding this but this is how
+		// it works.
+		if (packet->get_key_length() > 0)
+		{
+			status = key_out.set_copy_of_buffer(mppe_recv_key.get_data(packet->get_key_length()), packet->get_key_length());	
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		} 
+		else
+		{
+			// Key message with no key length?
+			// Just ignore the message.
+			EAP_TRACE_DEBUG(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("Got empty WEP unicast key message.\n")));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+		}
+	} 
+	else
+	{
+		// Set-up RC4 key. Key is the IV and the MS-MPPE-Recv-Key truncated together.
+		eap_variable_data_c rc4_key(m_am_tools);
+		status = rc4_key.set_copy_of_buffer(packet->get_key_IV(), EAPOL_RC4_KEY_IV_LENGTH);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		rc4_key.add_data(&mppe_recv_key);
+
+		// Set-up RC4 module
+		crypto_rc4_c rc4(m_am_tools);
+		// Set the key for RC4
+		if (rc4.set_key(&rc4_key) != eap_status_ok)
+		{
+			EAP_TRACE_DEBUG(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("ERROR: rc4_set_key failed\n")));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_process_general_error);
+		}
+
+		// Decrypt the key to key_out
+		key_out.set_buffer_length(packet->get_key_length());
+		if (rc4.decrypt_data(packet->get_key(), key_out.get_data(packet->get_key_length()), packet->get_key_length()) != eap_status_ok)
+		{
+			EAP_TRACE_DEBUG(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("ERROR: rc4 failed\n")));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_process_general_error);
+		}		
+		key_out.set_data_length(packet->get_key_length());
+	}
+
+	// Find out the key type. At the moment only WEP keys are supported.
+	eapol_key_type_e key_type;
+	switch (packet->get_key_flag())
+	{
+	case eapol_RC4_key_flag_broadcast:
+		EAP_TRACE_DEBUG(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("Got WEP broadcast key\n")));
+		key_type = eapol_key_type_broadcast;
+		break;
+	case eapol_RC4_key_flag_unicast:
+		EAP_TRACE_DEBUG(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("Got WEP unicast key\n")));
+		key_type = eapol_key_type_unicast;
+		break;
+	default:
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+	}
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("Key"),
+		 key_out.get_data(key_out.get_data_length()),
+		 key_out.get_data_length()));
+
+	// Here we swap the addresses.
+	eap_am_network_id_c send_network_id(m_am_tools,
+		receive_network_id->get_destination_id(),
+		receive_network_id->get_source_id(),
+		receive_network_id->get_type());
+	if (send_network_id.get_is_valid_data() == false)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	eapol_session_key_c wep_key(
+		m_am_tools,
+		&key_out,
+		key_type,
+		packet->get_key_index(),
+		true);
+
+	// Forward the keys to lower layers
+	status = m_partner->packet_data_session_key(
+		&send_network_id,
+		&wep_key);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+#endif //#if !defined(USE_EAPOL_KEY_STATE)
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eapol_core_c::configure()
+{
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("%s: eapol_core_c::configure()\n"),
+		(m_is_client == true) ? "client": "server"));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: eapol_core_c::configure()");
+
+	m_eapol_header_offset = m_partner->get_header_offset(
+		&m_MTU, &m_trailer_length);
+	
+	eap_status_e status = eap_status_process_general_error;
+
+	{
+		eap_variable_data_c max_eapol_starts(m_am_tools);
+
+		status = read_configure(
+			cf_str_EAPOL_CORE_starts_max_count.get_field(),
+			&max_eapol_starts);
+		if (status != eap_status_ok 
+			|| max_eapol_starts.get_is_valid_data() == false
+			|| max_eapol_starts.get_data_length() < sizeof(u32_t))
+		{
+			// Probably not found from db. Use the default value.
+			m_max_eapol_starts = EAPOL_CORE_MAX_EAPOL_START_SENDINGS;						
+		} 
+		else 
+		{
+			m_max_eapol_starts = *reinterpret_cast<u32_t *>(
+				max_eapol_starts.get_data(sizeof(u32_t)));
+		}
+	}
+
+	{
+		eap_variable_data_c eapol_start_interval(m_am_tools);
+
+		status = read_configure(
+			cf_str_EAPOL_CORE_send_start_interval.get_field(),
+			&eapol_start_interval);
+		if (status != eap_status_ok 
+			|| eapol_start_interval.get_is_valid_data() == false
+			|| eapol_start_interval.get_data_length() < sizeof(u32_t))
+		{
+			// Probably not found from db. Use the default value.
+			m_eapol_start_interval = EAPOL_CORE_TIMER_SEND_START_AGAIN_TIMEOUT;						
+		} 
+		else 
+		{
+			m_eapol_start_interval = *reinterpret_cast<u32_t *>(
+				eapol_start_interval.get_data(sizeof(u32_t)));
+		}
+	}
+
+#if defined(USE_EAP_CORE_SERVER)
+	if (m_is_client == false)
+	{
+		eap_variable_data_c data(m_am_tools);
+
+		eap_status_e status = read_configure(
+			cf_str_EAPOL_CORE_skip_start_4_way_handshake.get_field(),
+			&data);
+		if (status == eap_status_ok
+			&& data.get_data_length() == sizeof(u32_t)
+			&& data.get_data(data.get_data_length()) != 0)
+		{
+			u32_t *flag = reinterpret_cast<u32_t *>(data.get_data(data.get_data_length()));
+
+			if (flag != 0)
+			{
+				if ((*flag) != 0ul)
+				{
+					m_skip_start_4_way_handshake = true;
+				}
+				else
+				{
+					m_skip_start_4_way_handshake = false;
+				}
+			}
+		}
+	}
+#endif //#if defined(USE_EAP_CORE_SERVER)
+
+	return EAP_STATUS_RETURN(m_am_tools, m_eap_core->configure());
+}
+
+//--------------------------------------------------
+
+#if defined(USE_EAPOL_KEY_STATE)
+
+//
+EAP_FUNC_EXPORT eap_status_e eapol_core_c::shutdown_operation(
+	eapol_key_state_c * const handler,
+	abs_eap_am_tools_c * const m_am_tools)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_UNREFERENCED_PARAMETER(m_am_tools);
+
+	eap_status_e status = handler->shutdown();
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+#endif //#if defined(USE_EAPOL_KEY_STATE)
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eapol_core_c::shutdown()
+{
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("%s: eapol_core_c::shutdown(), m_shutdown_was_called=%d\n"),
+		(m_is_client == true) ? "client": "server",
+		m_shutdown_was_called));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: eapol_core_c::shutdown()");
+
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	if (m_shutdown_was_called == true)
+	{
+		// Shutdown function was called already.
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+	}
+	m_shutdown_was_called = true;
+
+	eap_status_e status;
+#if defined(USE_EAPOL_KEY_STATE)
+	status = m_eapol_key_state_map.for_each(shutdown_operation, true);
+#endif //#if defined(USE_EAPOL_KEY_STATE)
+
+	if (m_eap_core != 0)
+	{
+		status = m_eap_core->shutdown();
+	}
+
+	// This will cancel all timers of this object.
+	m_partner->cancel_timer(this, EAPOL_CORE_TIMER_SEND_START_AGAIN_ID);
+#if defined(USE_EAPOL_KEY_STATE)
+	m_partner->cancel_timer(this, EAPOL_REMOVE_EAPOL_KEY_HANDSHAKE_ID);
+#endif //#if defined(USE_EAPOL_KEY_STATE)
+	
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eapol_core_c::read_configure(
+	const eap_configuration_field_c * const field,
+	eap_variable_data_c * const data)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	const eap_status_e status = m_partner->read_configure(field, data);
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e eapol_core_c::write_configure(
+	const eap_configuration_field_c * const field,
+	eap_variable_data_c * const data)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	const eap_status_e status = m_partner->write_configure(field, data);
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT void eapol_core_c::set_is_valid()
+{
+	m_is_valid = true;
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT bool eapol_core_c::get_is_valid()
+{
+	return m_is_valid;
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eapol_core_c::timer_expired(
+	const u32_t id, void *data)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("TIMER: %s: [0x%08x]->eapol_core_c::timer_expired(id 0x%02x, data 0x%08x).\n"),
+		 (m_is_client == true) ? "client": "server",
+		 this,
+		 id,
+		 data));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: eapol_core_c::timer_expired()");
+
+	if (id == EAPOL_CORE_TIMER_SEND_START_AGAIN_ID)
+	{
+		const eap_am_network_id_c * const receive_network_id
+			= reinterpret_cast<const eap_am_network_id_c *>(data);
+		if (m_eapol_starts_sent < m_max_eapol_starts)
+		{
+			EAP_TRACE_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("TIMER: %s: EAPOL_CORE_TIMER_SEND_START_AGAIN_ID expired, re-start authentication.\n"),
+				 (m_is_client == true ? "client": "server"),
+				 m_eapol_start_interval));
+
+			m_eapol_starts_sent++;			
+			restart_authentication(receive_network_id , m_is_client, true, true);
+		}
+		else 
+		{
+			EAP_TRACE_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("ERROR: TIMER: %s: EAPOL_CORE_TIMER_SEND_START_AGAIN_ID expired, too many re-starts, notifies lower layers.\n"),
+				 (m_is_client == true ? "client": "server"),
+				 m_eapol_start_interval));
+
+			// Here we swap the addresses.
+			eap_am_network_id_c send_network_id(m_am_tools,
+				receive_network_id->get_destination_id(),
+				receive_network_id->get_source_id(),
+				receive_network_id->get_type());
+			if (send_network_id.get_is_valid_data() == false)
+			{
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+			}
+
+			// EAPOL-Start has been sent enough times. Send notification to lower layers.
+			// After this EAPOL stays quiet and waits for incoming
+			// packets or authentication restarting.
+			eap_state_notification_c notification(
+				m_am_tools,
+				&send_network_id,
+				true, // m_is_client
+				eap_state_notification_generic,
+				eap_protocol_layer_eapol,
+				eap_type_none,
+				eapol_state_start_sent,
+				eapol_state_no_start_response,
+				0 /* EAP identifier not valid here */,
+				false);
+
+			notification.set_authentication_error(eap_status_authentication_failure);
+
+			state_notification(&notification);
+		}
+	}
+#if defined(USE_EAPOL_KEY_STATE)
+	else if (id == EAPOL_REMOVE_EAPOL_KEY_HANDSHAKE_ID)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("TIMER: EAPOL_REMOVE_EAPOL_KEY_HANDSHAKE_ID elapsed, %s.\n"),
+			 (m_is_client == true) ? "client": "server"));
+
+		const eap_am_network_id_c * const send_network_id 
+			= reinterpret_cast<const eap_am_network_id_c *>(data);
+		if (send_network_id->get_is_valid_data() == false)
+		{
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		(void) remove_eapol_key_state(send_network_id);
+	}
+#endif //#if defined(USE_EAPOL_KEY_STATE)
+
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eapol_core_c::timer_delete_data(
+	const u32_t id, void *data)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("TIMER: %s: [0x%08x]->eapol_core_c::timer_delete_data(id 0x%02x, data 0x%08x).\n"),
+		(m_is_client == true) ? "client": "server",
+		this, id, data));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: eapol_core_c::timer_delete_data()");
+
+	if (id == EAPOL_CORE_TIMER_SEND_START_AGAIN_ID)
+	{
+		const eap_am_network_id_c * const send_network_id
+			= reinterpret_cast<const eap_am_network_id_c *>(data);
+		delete send_network_id;
+	}
+#if defined(USE_EAPOL_KEY_STATE)
+	else if (id == EAPOL_REMOVE_EAPOL_KEY_HANDSHAKE_ID)
+
+	{
+		const eap_am_network_id_c * const send_network_id 
+			= reinterpret_cast<const eap_am_network_id_c *>(data);
+		delete send_network_id;
+	}
+#endif //#if defined(USE_EAPOL_KEY_STATE)
+
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+//--------------------------------------------------
+
+#if defined(USE_EAPOL_KEY_STATE)
+
+//
+EAP_FUNC_EXPORT eap_status_e eapol_core_c::init_eapol_key_pmksa_caching_timeout(
+	const eap_am_network_id_c * const send_network_id)
+{
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("%s: eapol_core_c::init_eapol_key_pmksa_caching_timeout().\n"),
+		 (m_is_client == true) ? "client": "server"));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: eapol_core_c::init_eapol_key_pmksa_caching_timeout()");
+
+	// Initialize PMK caching timeout of EAPOL Key state.
+	eap_network_id_selector_c state_selector(
+		m_am_tools,
+		send_network_id);
+
+	if (state_selector.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("init_eapol_key_pmksa_caching_timeout(): EAPOL-Key session"),
+		state_selector.get_data(state_selector.get_data_length()),
+		state_selector.get_data_length()));
+
+	eapol_key_state_c *eapol_key_state = m_eapol_key_state_map.get_handler(&state_selector);
+
+	if (eapol_key_state == 0)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("session not found.\n"),
+			 (m_is_client == true) ? "client": "server"));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+	}
+
+	eap_status_e status = eapol_key_state->init_pmksa_caching_timeout();
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("WARNING: eapol_core_c::init_eapol_key_pmksa_caching_timeout(): ")
+			 EAPL("eapol_key_state->init_pmksa_caching_timeout(), eap_status_e %d\n"),
+			 status));
+	}
+
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+#endif //#if defined(USE_EAPOL_KEY_STATE)
+
+//--------------------------------------------------
+
+#if defined(USE_EAPOL_KEY_STATE)
+
+//
+EAP_FUNC_EXPORT eap_status_e eapol_core_c::indicate_eapol_key_state_started_eap_authentication(
+	const eap_am_network_id_c * const send_network_id)
+{
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("%s: eapol_core_c::indicate_eapol_key_state_started_eap_authentication().\n"),
+		 (m_is_client == true) ? "client": "server"));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: eapol_core_c::indicate_eapol_key_state_started_eap_authentication()");
+
+	// Remove possible EAPOL Key state.
+	eap_network_id_selector_c state_selector(
+		m_am_tools,
+		send_network_id);
+
+	if (state_selector.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("indicate_eapol_key_state_started_eap_authentication(): EAPOL-Key session"),
+		state_selector.get_data(state_selector.get_data_length()),
+		state_selector.get_data_length()));
+
+	eapol_key_state_c *eapol_key_state = m_eapol_key_state_map.get_handler(&state_selector);
+
+	eap_status_e status(eap_status_handler_does_not_exists_error);
+
+	if (eapol_key_state != 0)
+	{
+		status = eapol_key_state->started_eap_authentication();
+	}
+	else
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("WARNING: eapol_core_c::indicate_eapol_key_state_started_eap_authentication(): Cannot run eapol_key_state->started_eap_authentication() 0x%08x\n"),
+			eapol_key_state));
+	}
+
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+#endif //#if defined(USE_EAPOL_KEY_STATE)
+
+//--------------------------------------------------
+
+#if defined(USE_EAPOL_KEY_STATE)
+
+//
+EAP_FUNC_EXPORT eap_status_e eapol_core_c::remove_eapol_key_state(
+	const eap_am_network_id_c * const send_network_id)
+{
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("%s: eapol_core_c::remove_eapol_key_state().\n"),
+		 (m_is_client == true) ? "client": "server"));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: eapol_core_c::remove_eapol_key_state()");
+
+	// Remove possible EAPOL Key state.
+	eap_network_id_selector_c state_selector(
+		m_am_tools,
+		send_network_id);
+
+	if (state_selector.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("remove_eapol_key_state(): EAPOL-Key session"),
+		state_selector.get_data(state_selector.get_data_length()),
+		state_selector.get_data_length()));
+
+	eapol_key_state_c *eapol_key_state = m_eapol_key_state_map.get_handler(&state_selector);
+
+	if (eapol_key_state != 0)
+	{
+		if (eapol_key_state->get_marked_removed() == false)
+		{
+			// Do not remove object in use.
+			EAP_TRACE_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("WARNING: eapol_core_c::remove_eapol_key_state(): Cannot removed used object 0x%08x\n"),
+				eapol_key_state));
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+		}
+
+		eapol_key_state->shutdown();
+	}
+	else
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("WARNING: eapol_core_c::remove_eapol_key_state(): Cannot run eapol_key_state->shutdown() 0x%08x\n"),
+			eapol_key_state));
+	}
+
+	eap_status_e status = m_eapol_key_state_map.remove_handler(&state_selector, true);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("WARNING: eapol_core_c::remove_eapol_key_state(): ")
+			 EAPL("eapol_key_state->remove_handler(), eap_status_e %d\n"),
+			 status));
+	}
+
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+#endif //#if defined(USE_EAPOL_KEY_STATE)
+
+//--------------------------------------------------
+
+#if defined(USE_EAPOL_KEY_STATE)
+
+//
+eap_status_e eapol_core_c::asynchronous_init_remove_eapol_key_state(
+	const eap_am_network_id_c * const send_network_id)
+{
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("%s: eapol_core_c::asynchronous_init_remove_eapol_key_state().\n"),
+		 (m_is_client == true) ? "client": "server"));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: eapol_core_c::asynchronous_init_remove_eapol_key_state()");
+
+	// NOTE: we cannot delete the eapol_key_state_c object directly, because we will
+	// return from here to removed object.
+
+	eap_status_e status = eap_status_process_general_error;
+
+	eap_network_id_selector_c state_selector(
+		m_am_tools,
+		send_network_id);
+
+	if (state_selector.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("asynchronous_init_remove_eapol_key_state(): EAPOL-Key session"),
+		state_selector.get_data(state_selector.get_data_length()),
+		state_selector.get_data_length()));
+
+	eapol_key_state_c *session = m_eapol_key_state_map.get_handler(&state_selector);
+
+	if (session != 0)
+	{
+		session->set_marked_removed();
+
+		// So we initiate a timer to remove session identified by state_selector.
+		eap_am_network_id_c * const copy_send_network_id = send_network_id->copy();
+		if (copy_send_network_id == 0
+			|| copy_send_network_id->get_is_valid_data() == false)
+		{
+			delete copy_send_network_id;
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		status = m_partner->set_timer(
+			this,
+			EAPOL_REMOVE_EAPOL_KEY_HANDSHAKE_ID, 
+			copy_send_network_id,
+			EAPOL_REMOVE_EAPOL_KEY_HANDSHAKE_TIMEOUT);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("eapol_core_c::asynchronous_init_remove_eapol_key_state()")
+			 EAPL(": %s: EAPOL_REMOVE_EAPOL_KEY_HANDSHAKE_ID timer set %d ms.\n"),
+			 (m_is_client == true) ? "client": "server",
+			 EAPOL_REMOVE_EAPOL_KEY_HANDSHAKE_TIMEOUT));
+	}
+	else
+	{
+		// Not found, cannot remove.
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("ERROR: eapol_core_c::asynchronous_init_remove_eapol_key_state()")
+			 EAPL(": %s: failed session not found.\n"),
+			 (m_is_client == true) ? "client": "server"));
+
+		status = eap_status_ok;
+	}
+
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+#endif //#if defined(USE_EAPOL_KEY_STATE)
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void eapol_core_c::state_notification(
+	const abs_eap_state_notification_c * const state)
+{
+	eap_status_e status = eap_status_process_general_error;
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("%s: eapol_core_c::state_notification(): m_block_state_notifications=%d.\n"),
+		 (m_is_client == true) ? "client": "server",
+		 m_block_state_notifications));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: eapol_core_c::state_notification()");
+
+	if (m_block_state_notifications == true)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("WARNING: eapol_core_c::state_notification(): Blocks state notification")
+			 EAPL("Protocol layer %d, Protocol %d, State transition ")
+			 EAPL("from %d=%s to %d=%s, client %d\n"),
+			state->get_protocol_layer(), 
+			state->get_protocol(), 
+			state->get_previous_state(), 
+			state->get_previous_state_string(), 
+			state->get_current_state(), 
+			state->get_current_state_string(),
+			state->get_is_client()));
+
+		return;
+	}
+
+	if (state->get_protocol_layer() == eap_protocol_layer_eap)
+	{
+		if (state->get_current_state() == eap_state_identity_request_received
+			|| state->get_current_state() == eap_state_eap_response_sent)
+		{
+			if (state->get_current_state() == eap_state_identity_request_received)
+			{
+				(void) m_partner->cancel_timer(
+					this,
+					EAPOL_CORE_TIMER_SEND_START_AGAIN_ID);
+			}
+
+			if (state->get_current_state() == eap_state_identity_request_received
+				|| state->get_current_state() == eap_state_eap_response_sent)
+			{
+#if defined(USE_EAPOL_KEY_STATE)
+				// Indicate EAPOL Key state the started EAP-authentication.
+				status = indicate_eapol_key_state_started_eap_authentication(
+					state->get_send_network_id());
+				if (status != eap_status_ok)
+				{
+					EAP_TRACE_DEBUG(
+						m_am_tools, 
+						TRACE_FLAGS_DEFAULT, 
+						(EAPL("WARNING: eapol_core_c::state_notification(): ")
+						 EAPL("indicate_eapol_key_state_started_eap_authentication(), eap_status_e %d\n"),
+						status));
+				}
+#endif //#if defined(USE_EAPOL_KEY_STATE)
+			}
+
+		}
+		else if (state->get_current_state() == eap_state_authentication_terminated_unsuccessfully)
+		{
+			EAP_TRACE_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("ERROR: eapol_core_c::state_notification(): %s: EAP-authentication FAILED\n"),
+				 (state->get_is_client() == true ? "client": "server")));
+
+#if defined(USE_EAPOL_KEY_STATE)
+			// Remove possible EAPOL Key state.
+			status = remove_eapol_key_state(
+				state->get_send_network_id());
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_DEBUG(
+					m_am_tools, 
+					TRACE_FLAGS_DEFAULT, 
+					(EAPL("WARNING: eapol_core_c::state_notification(): ")
+					 EAPL("remove_eapol_key_state(), eap_status_e %d\n"),
+					status));
+			}
+#endif //#if defined(USE_EAPOL_KEY_STATE)
+
+		}
+		else if (state->get_current_state() == eap_state_authentication_finished_successfully)
+		{
+			EAP_TRACE_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("eapol_core_c::state_notification(): %s: EAP authentication SUCCESS\n"),
+				(state->get_is_client() == true ? "client": "server")));
+
+#if defined(USE_EAPOL_KEY_STATE)
+			eap_network_id_selector_c state_selector(
+				m_am_tools,
+				state->get_send_network_id());
+
+			if (state_selector.get_is_valid() == false)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return;
+			}
+
+			EAP_TRACE_DATA_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("state_notification(): EAPOL-Key session"),
+				 state_selector.get_data(state_selector.get_data_length()),
+				 state_selector.get_data_length()));
+
+			eapol_key_state_c *eapol_key_state
+				= m_eapol_key_state_map.get_handler(&state_selector);
+
+#if !defined(NO_EAPOL_KEY_STATE_SERVER)
+			if (m_is_client == false)
+			{
+				// server
+				if (eapol_key_state != 0)
+				{
+					// Here we swap the addresses.
+					eap_am_network_id_c receive_network_id(
+						m_am_tools,
+						state->get_send_network_id()->get_destination_id(),
+						state->get_send_network_id()->get_source_id(),
+						state->get_send_network_id()->get_type());
+					if (receive_network_id.get_is_valid_data() == false)
+					{
+						EAP_TRACE_DEBUG(
+							m_am_tools, 
+							TRACE_FLAGS_DEFAULT, 
+							(EAPL("ERROR: eapol_core_c::state_notification(): ")
+							 EAPL("start_4_way_handshake() failed, allocation error\n"),
+							status));
+						return;
+					}
+
+					if (m_skip_start_4_way_handshake == true
+						&& (m_authentication_type == eapol_key_authentication_type_RSNA_EAP
+							|| m_authentication_type == eapol_key_authentication_type_RSNA_PSK
+							|| m_authentication_type == eapol_key_authentication_type_WPA_EAP
+							|| m_authentication_type == eapol_key_authentication_type_WPA_PSK
+							|| m_authentication_type == eapol_key_authentication_type_WPXM))
+					{
+						// This is test to skip 4-Way Handshake start.
+						EAP_TRACE_DEBUG(
+							m_am_tools, 
+							TRACE_FLAGS_DEFAULT, 
+							(EAPL("WARNING: eapol_core_c::state_notification(): ")
+							 EAPL("skips start_4_way_handshake()\n")));
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return;
+					}
+					else
+					{
+						status = eapol_key_state->start_4_way_handshake(
+							&receive_network_id);
+					}
+
+					if (status != eap_status_ok)
+					{
+						EAP_TRACE_DEBUG(
+							m_am_tools, 
+							TRACE_FLAGS_DEFAULT, 
+							(EAPL("ERROR: eapol_core_c::state_notification(): ")
+							 EAPL("start_4_way_handshake() failed, eap_status_e %d\n"),
+							status));
+					}
+					else
+					{
+						EAP_TRACE_DEBUG(
+							m_am_tools, 
+							TRACE_FLAGS_DEFAULT, 
+							(EAPL("eapol_core_c::state_notification(): ")
+							 EAPL("start_4_way_handshake(), eap_status_e %d\n"),
+							status));
+					}
+				}
+				else
+				{
+					EAP_TRACE_DEBUG(
+						m_am_tools, 
+						TRACE_FLAGS_DEFAULT, 
+						(EAPL("ERROR: eapol_core_c::state_notification(): ")
+						 EAPL("start_4_way_handshake() failed, no eapol_key_state_c object\n")));
+				}
+			}
+			else
+#endif //#if !defined(NO_EAPOL_KEY_STATE_SERVER)
+			{
+				if (eapol_key_state != 0)
+				{
+					eap_status_e status = eapol_key_state->allow_4_way_handshake();
+					if (status != eap_status_ok)
+					{
+						EAP_TRACE_DEBUG(
+							m_am_tools, 
+							TRACE_FLAGS_DEFAULT, 
+							(EAPL("ERROR: eapol_core_c::state_notification(): ")
+							 EAPL("allow_4_way_handshake() failed, eap_status_e %d\n"),
+							status));
+					}
+					else
+					{
+						EAP_TRACE_DEBUG(
+							m_am_tools, 
+							TRACE_FLAGS_DEFAULT, 
+							(EAPL("eapol_core_c::state_notification(): ")
+							 EAPL("allow_4_way_handshake(), eap_status_e %d\n"),
+							status));
+					}
+				}
+				else
+				{
+					EAP_TRACE_DEBUG(
+						m_am_tools, 
+						TRACE_FLAGS_DEFAULT, 
+						(EAPL("ERROR: eapol_core_c::state_notification(): ")
+						 EAPL("allow_4_way_handshake() failed, no eapol_key_state_c object\n")));
+				}
+			}
+#endif //#if defined(USE_EAPOL_KEY_STATE)
+		}
+		else
+		{
+			EAP_TRACE_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("eapol_core_c::state_notification(): Unknown notification: ")
+				 EAPL("Protocol layer %d, Protocol %d, State transition ")
+				 EAPL("from %d=%s to %d=%s, client %d\n"),
+				state->get_protocol_layer(), 
+				state->get_protocol(), 
+				state->get_previous_state(), 
+				state->get_previous_state_string(), 
+				state->get_current_state(), 
+				state->get_current_state_string(),
+				state->get_is_client()));
+		}
+	}
+#if defined(USE_EAPOL_KEY_STATE)
+	else if (state->get_protocol_layer() == eap_protocol_layer_eapol_key)
+	{
+		// This nofifation is from eapol_key_state_c object.
+		if (state->get_current_state()
+			== eapol_key_state_802_11i_authentication_terminated_unsuccessfull)
+		{
+			if (state->get_protocol() == eapol_key_handshake_type_4_way_handshake)
+			{
+				// 4-Way Handshake failed.
+				EAP_TRACE_DEBUG(
+					m_am_tools,
+					TRACE_FLAGS_DEFAULT,
+					(EAPL("ERROR: eapol_core_c::state_notification(): ")
+					 EAPL("%s: 4-Way Handshake FAILED\n"),
+					 (state->get_is_client() == true ? "client": "server")));
+			}
+			else if (state->get_protocol() == eapol_key_handshake_type_group_key_handshake)
+			{
+				// Group Key Handshake failed.
+				EAP_TRACE_DEBUG(
+					m_am_tools,
+					TRACE_FLAGS_DEFAULT,
+					(EAPL("ERROR: eapol_core_c::state_notification(): ")
+					 EAPL("%s: Group Key Handshake FAILED\n"),
+					 (state->get_is_client() == true ? "client": "server")));
+			}
+		}
+		else if (state->get_current_state()
+				 == eapol_key_state_802_11i_authentication_finished_successfull)
+		{
+			if (state->get_protocol() == eapol_key_handshake_type_4_way_handshake)
+			{
+				// 4-Way Handshake succesfull.
+				EAP_TRACE_DEBUG(
+					m_am_tools,
+					TRACE_FLAGS_DEFAULT,
+					(EAPL("eapol_core_c::state_notification(): %s: 4-Way Handshake SUCCESS.\n"),
+					 (state->get_is_client() == true ? "client": "server")));
+			}
+			else if (state->get_protocol() == eapol_key_handshake_type_group_key_handshake)
+			{
+				// Group Key Handshake succesfull.
+				EAP_TRACE_DEBUG(
+					m_am_tools,
+					TRACE_FLAGS_DEFAULT,
+					(EAPL("eapol_core_c::state_notification(): %s: Group Key Handshake SUCCESS.\n"),
+					 (state->get_is_client() == true ? "client": "server")));
+			}
+			else if (state->get_protocol() == eapol_key_handshake_type_802_11i_handshake)
+			{
+				// Full 802.11i authentication succesfull.
+				EAP_TRACE_DEBUG(
+					m_am_tools,
+					TRACE_FLAGS_DEFAULT,
+					(EAPL("eapol_core_c::state_notification(): %s: ")
+					 EAPL("Full 802.11i authentication SUCCESS.\n"),
+					 (state->get_is_client() == true ? "client": "server")));
+			}
+			else
+			{
+				EAP_TRACE_DEBUG(
+					m_am_tools,
+					TRACE_FLAGS_DEFAULT,
+					(EAPL("eapol_core_c::state_notification(): Unknown notification: ")
+					 EAPL("Protocol layer %d, Protocol %d, State transition ")
+					 EAPL("from %d=%s to %d=%s, client %d\n"),
+					state->get_protocol_layer(), 
+					state->get_protocol(), 
+					state->get_previous_state(), 
+					state->get_previous_state_string(), 
+					state->get_current_state(), 
+					state->get_current_state_string(),
+					state->get_is_client()));
+			}
+		}
+#if defined(EAP_USE_WPXM)
+		else if (state->get_current_state()
+				 == eapol_key_state_wpxm_reassociation_finished_successfull)
+		{
+			if (state->get_protocol() == eapol_key_handshake_type_WPXM_reassociation)
+			{
+				// WPXM reassociation succesfull.
+				EAP_TRACE_DEBUG(
+					m_am_tools,
+					TRACE_FLAGS_DEFAULT,
+					(EAPL("eapol_core_c::state_notification(): %s: ")
+					 EAPL("WPXM reassociation SUCCESS.\n"),
+					 (state->get_is_client() == true ? "client": "server")));
+			}
+			else
+			{
+				EAP_TRACE_DEBUG(
+					m_am_tools,
+					TRACE_FLAGS_DEFAULT,
+					(EAPL("eapol_core_c::state_notification(): Unknown notification: ")
+					 EAPL("Protocol layer %d, Protocol %d, State transition ")
+					 EAPL("from %d=%s to %d=%s, client %d\n"),
+					state->get_protocol_layer(), 
+					state->get_protocol(), 
+					state->get_previous_state(), 
+					state->get_previous_state_string(), 
+					state->get_current_state(), 
+					state->get_current_state_string(),
+					state->get_is_client()));
+			}
+		}
+#endif //#if defined(EAP_USE_WPXM)
+		else if (state->get_current_state()
+				 == eapol_key_state_group_key_handshake_successfull)
+		{
+			if (state->get_protocol() == eapol_key_handshake_type_group_key_handshake)
+			{
+				// Group Key Handshake succesfull.
+				EAP_TRACE_DEBUG(
+					m_am_tools,
+					TRACE_FLAGS_DEFAULT,
+					(EAPL("eapol_core_c::state_notification(): %s: Group Key Handshake SUCCESS.\n"),
+					 (state->get_is_client() == true ? "client": "server")));
+			}
+			else
+			{
+				EAP_TRACE_DEBUG(
+					m_am_tools,
+					TRACE_FLAGS_DEFAULT,
+					(EAPL("eapol_core_c::state_notification(): Unknown notification: ")
+					 EAPL("Protocol layer %d, Protocol %d, State transition ")
+					 EAPL("from %d=%s to %d=%s, client %d\n"),
+					state->get_protocol_layer(), 
+					state->get_protocol(), 
+					state->get_previous_state(), 
+					state->get_previous_state_string(), 
+					state->get_current_state(), 
+					state->get_current_state_string(),
+					state->get_is_client()));
+			}
+		}
+		else
+		{
+			EAP_TRACE_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("eapol_core_c::state_notification(): Unknown notification: ")
+				 EAPL("Protocol layer %d, Protocol %d, State transition ")
+				 EAPL("from %d=%s to %d=%s, client %d\n"),
+				state->get_protocol_layer(), 
+				state->get_protocol(), 
+				state->get_previous_state(), 
+				state->get_previous_state_string(), 
+				state->get_current_state(), 
+				state->get_current_state_string(),
+				state->get_is_client()));
+		}
+	}
+#endif //#if defined(USE_EAPOL_KEY_STATE)
+
+
+	m_partner->state_notification(state);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eapol_core_c::asynchronous_init_remove_eap_session(
+	const eap_am_network_id_c * const /* send_network_id */)
+{
+	// eapol_core_c object does not support asynchronous_init_remove_eap_session().
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_not_supported);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eapol_core_c::asynchronous_start_authentication(
+	const eap_am_network_id_c * const /* receive_network_id */,
+	const bool /* is_client_when_true */)
+{
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_not_supported);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eapol_core_c::set_timer(
+	abs_eap_base_timer_c * const p_initializer, 
+	const u32_t p_id, 
+	void * const p_data,
+	const u32_t p_time_ms)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	const eap_status_e status = m_partner->set_timer(
+		p_initializer, 
+		p_id, 
+		p_data,
+		p_time_ms);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eapol_core_c::cancel_timer(
+	abs_eap_base_timer_c * const p_initializer, 
+	const u32_t p_id)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	const eap_status_e status = m_partner->cancel_timer(
+		p_initializer, 
+		p_id);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eapol_core_c::cancel_all_timers()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	const eap_status_e status = m_partner->cancel_all_timers();
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e eapol_core_c::check_is_valid_eap_type(
+	const eap_type_value_e eap_type)
+{
+	eap_status_e status = m_partner->check_is_valid_eap_type(eap_type);
+
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e eapol_core_c::get_eap_type_list(
+	eap_array_c<eap_type_value_e> * const eap_type_list)
+{
+	eap_status_e status = m_partner->get_eap_type_list(eap_type_list);
+
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+#if defined(USE_EAPOL_KEY_STATE)
+
+//
+EAP_FUNC_EXPORT eap_status_e eapol_core_c::cancel_authentication_session(
+	eapol_key_state_c * const handler,
+	abs_eap_am_tools_c * const m_am_tools)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eapol_core_c::cancel_authentication_session(): this = 0x%08x => 0x%08x.\n"),
+		handler,
+		dynamic_cast<abs_eap_base_timer_c *>(handler)));
+
+	EAP_UNREFERENCED_PARAMETER(m_am_tools);
+
+	eap_status_e status = handler->cancel_authentication_session();
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+#endif //#if defined(USE_EAPOL_KEY_STATE)
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e eapol_core_c::cancel_all_authentication_sessions()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eapol_core_c::cancel_all_authentication_sessions(): this = 0x%08x => 0x%08x.\n"),
+		this,
+		dynamic_cast<abs_eap_base_timer_c *>(this)));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: eapol_core_c::cancel_all_authentication_sessions()");
+
+	eap_status_e status(eap_status_ok);
+
+
+	bool previous_block = m_block_state_notifications;
+	m_block_state_notifications = true;
+
+#if !defined(NO_EAP_SESSION_CORE)
+	if (m_eap_core != 0)
+	{
+		status = m_eap_core->synchronous_cancel_all_eap_sessions();
+	}
+#endif //#if !defined(NO_EAP_SESSION_CORE)
+
+
+#if defined(USE_EAPOL_KEY_STATE)
+	status = m_eapol_key_state_map.for_each(cancel_authentication_session, true);
+#endif //#if defined(USE_EAPOL_KEY_STATE)
+
+	m_block_state_notifications = previous_block;
+
+
+	// This will cancel all timers of this object.
+	m_partner->cancel_timer(this, EAPOL_CORE_TIMER_SEND_START_AGAIN_ID);
+#if defined(USE_EAPOL_KEY_STATE)
+	m_partner->cancel_timer(this, EAPOL_REMOVE_EAPOL_KEY_HANDSHAKE_ID);
+#endif //#if defined(USE_EAPOL_KEY_STATE)
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+#if defined(USE_EAPOL_KEY_STATE)
+
+EAP_FUNC_EXPORT eap_status_e eapol_core_c::get_and_increment_global_key_counter(
+	eap_variable_data_c * const key_counter)
+{
+	key_counter->reset();
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_not_supported);
+}
+
+#endif //#if defined(USE_EAPOL_KEY_STATE)
+
+//--------------------------------------------------
+
+#if defined(USE_EAPOL_KEY_STATE) && defined(USE_EAPOL_KEY_STATE_OPTIMIZED_4_WAY_HANDSHAKE)
+/**
+ * Function creates a state for later use. This is for optimazing 4-Way Handshake.
+ * @param receive_network_id carries the MAC addresses.
+ * MAC address of Authenticator should be in source address. MAC address of 
+ * Supplicant should be in destination address.
+ * @param authentication_type is the selected authentication type.
+ */
+EAP_FUNC_EXPORT eap_status_e eapol_core_c::create_state(
+	const eap_am_network_id_c * const receive_network_id,
+	const eapol_key_authentication_type_e authentication_type
+	)
+{
+	eap_status_e status = eap_status_process_general_error;
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("%s: eapol_core_c::create_state().\n"),
+		 (m_is_client == true) ? "client": "server"));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: eapol_core_c::create_state()");
+
+	if (receive_network_id->get_type() != eapol_ethernet_type_pae)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("WARNING: eapol_core_c::create_state(): Illegal Ethernet type %d\n"),
+			receive_network_id->get_type()));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_ethernet_type_not_supported);
+	}
+
+	// Here we swap the addresses.
+	eap_am_network_id_c send_network_id(
+		m_am_tools,
+		receive_network_id->get_destination_id(),
+		receive_network_id->get_source_id(),
+		receive_network_id->get_type());
+	if (send_network_id.get_is_valid_data() == false)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	eap_network_id_selector_c state_selector(
+		m_am_tools,
+		&send_network_id);
+
+	if (state_selector.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	m_authentication_type = authentication_type;
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("create_state(): EAPOL-Key session"),
+		state_selector.get_data(state_selector.get_data_length()),
+		state_selector.get_data_length()));
+
+
+	eapol_key_state_c *eapol_key_state = m_eapol_key_state_map.get_handler(&state_selector);
+
+	if (eapol_key_state != 0)
+	{
+		// Reuse the session.
+		eapol_key_state->unset_marked_removed();
+
+		if (m_is_client == false)
+		{
+			// In test version do not reset server.
+		}
+		else
+		{
+			status = eapol_key_state->reset();
+			if (status != eap_status_ok)
+			{
+				// We cannot reuse the session.
+				EAP_TRACE_ERROR(
+					m_am_tools,
+					TRACE_FLAGS_ERROR,
+					(EAPL("eapol_core_c::create_state(): eapol_key_state NOT reused.\n")));
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+	}
+
+
+	if (eapol_key_state == 0)
+	{
+		eapol_key_state = new eapol_key_state_c(
+			m_am_tools,
+			this,
+			m_partner,
+			m_is_client,
+			receive_network_id,
+			authentication_type);
+		if (eapol_key_state == 0
+			|| eapol_key_state->get_is_valid() == false)
+		{
+			if (eapol_key_state != 0)
+			{
+				eapol_key_state->shutdown();
+			}
+			else
+			{
+				EAP_TRACE_DEBUG(
+					m_am_tools, 
+					TRACE_FLAGS_DEFAULT, 
+					(EAPL("WARNING: eapol_core_c::create_state(): Cannot run eapol_key_state->shutdown() 0x%08x\n"),
+					eapol_key_state));
+			}
+			delete eapol_key_state;
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		status = eapol_key_state->initialize(
+			receive_network_id,
+			authentication_type);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = m_eapol_key_state_map.add_handler(&state_selector, eapol_key_state);
+		if (status != eap_status_ok)
+		{
+			if (eapol_key_state != 0)
+			{
+				eapol_key_state->shutdown();
+			}
+			else
+			{
+				EAP_TRACE_DEBUG(
+					m_am_tools, 
+					TRACE_FLAGS_DEFAULT, 
+					(EAPL("WARNING: eapol_core_c::create_state(): Cannot run eapol_key_state->shutdown() 0x%08x\n"),
+					eapol_key_state));
+			}
+			delete eapol_key_state;
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+	else
+	{
+		status = eapol_key_state->initialize(
+			receive_network_id,
+			authentication_type);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	status = eapol_key_state->configure();
+	if (status != eap_status_ok)
+	{
+		status = remove_eapol_key_state(
+			&send_network_id);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("WARNING: eapol_core_c::create_state(): ")
+				 EAPL("remove_eapol_key_state(), eap_status_e %d\n"),
+				status));
+		}
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+#endif //#if defined(USE_EAPOL_KEY_STATE) && defined(USE_EAPOL_KEY_STATE_OPTIMIZED_4_WAY_HANDSHAKE)
+
+//--------------------------------------------------
+
+#if defined(USE_EAPOL_KEY_STATE)
+/**
+ * @param receive_network_id carries the MAC addresses.
+ * MAC address of Authenticator should be in source address. MAC address of 
+ * Supplicant should be in destination address.
+ * @param authenticator_RSNA_IE is RSN IE of authenticator. Authenticator 
+ * sends this in Beacon or Probe message.
+ * @param supplicant_RSNA_IE is RSN IE of supplicant. Supplicant sends 
+ * this in (re)association request message.
+ * @param eapol_pairwise_cipher is the selected pairwise cipher.
+ * @param eapol_group_cipher is the selected group cipher.
+ */
+EAP_FUNC_EXPORT eap_status_e eapol_core_c::association(
+	const eap_am_network_id_c * const receive_network_id,
+	const eapol_key_authentication_type_e authentication_type,
+	const eap_variable_data_c * const authenticator_RSNA_IE,
+	const eap_variable_data_c * const supplicant_RSNA_IE,
+	const eapol_RSNA_key_header_c::eapol_RSNA_cipher_e eapol_pairwise_cipher,
+	const eapol_RSNA_key_header_c::eapol_RSNA_cipher_e eapol_group_cipher,
+	const eap_variable_data_c * const pre_shared_key_PSK
+	)
+{
+	eap_status_e status = eap_status_process_general_error;
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("%s: eapol_core_c::association().\n"),
+		 (m_is_client == true) ? "client": "server"));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: eapol_core_c::association()");
+
+	if (receive_network_id->get_type() != eapol_ethernet_type_pae)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("WARNING: eapol_core_c::association(): Illegal Ethernet type %d\n"),
+			receive_network_id->get_type()));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_ethernet_type_not_supported);
+	}
+
+	// Here we swap the addresses.
+	eap_am_network_id_c send_network_id(
+		m_am_tools,
+		receive_network_id->get_destination_id(),
+		receive_network_id->get_source_id(),
+		receive_network_id->get_type());
+	if (send_network_id.get_is_valid_data() == false)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	eap_network_id_selector_c state_selector(
+		m_am_tools,
+		&send_network_id);
+
+	if (state_selector.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	m_authentication_type = authentication_type;
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("association(): EAPOL-Key session"),
+		state_selector.get_data(state_selector.get_data_length()),
+		state_selector.get_data_length()));
+
+
+	eapol_key_state_c *eapol_key_state = m_eapol_key_state_map.get_handler(&state_selector);
+
+#if !defined(USE_EAPOL_KEY_STATE_OPTIMIZED_4_WAY_HANDSHAKE)
+	if (eapol_key_state != 0)
+	{
+		// Reuse the session.
+		eapol_key_state->unset_marked_removed();
+
+		if (m_is_client == false)
+		{
+			// In test version do not reset server.
+		}
+		else
+		{
+			status = eapol_key_state->reset();
+			if (status != eap_status_ok)
+			{
+				// We cannot reuse the session.
+				EAP_TRACE_ERROR(
+					m_am_tools,
+					TRACE_FLAGS_ERROR,
+					(EAPL("eapol_core_c::association(): eapol_key_state NOT reused.\n")));
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+	}
+#endif //#if !defined(USE_EAPOL_KEY_STATE_OPTIMIZED_4_WAY_HANDSHAKE)
+
+
+	if (eapol_key_state == 0)
+	{
+
+#if !defined(USE_EAPOL_KEY_STATE_OPTIMIZED_4_WAY_HANDSHAKE)
+
+		eapol_key_state = new eapol_key_state_c(
+			m_am_tools,
+			this,
+			m_partner,
+			m_is_client,
+			receive_network_id,
+			authentication_type,
+			authenticator_RSNA_IE,
+			supplicant_RSNA_IE,
+			eapol_pairwise_cipher,
+			eapol_group_cipher,
+			pre_shared_key_PSK);
+		if (eapol_key_state == 0
+			|| eapol_key_state->get_is_valid() == false)
+		{
+			if (eapol_key_state != 0)
+			{
+				eapol_key_state->shutdown();
+			}
+			else
+			{
+				EAP_TRACE_DEBUG(
+					m_am_tools, 
+					TRACE_FLAGS_DEFAULT, 
+					(EAPL("WARNING: eapol_core_c::association(): Cannot run eapol_key_state->shutdown() 0x%08x\n"),
+					eapol_key_state));
+			}
+			delete eapol_key_state;
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+		}
+
+		status = eapol_key_state->initialize(
+			receive_network_id,
+			authentication_type,
+			authenticator_RSNA_IE,
+			supplicant_RSNA_IE,
+			eapol_pairwise_cipher,
+			eapol_group_cipher,
+			pre_shared_key_PSK);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = m_eapol_key_state_map.add_handler(&state_selector, eapol_key_state);
+		if (status != eap_status_ok)
+		{
+			if (eapol_key_state != 0)
+			{
+				eapol_key_state->shutdown();
+			}
+			else
+			{
+				EAP_TRACE_DEBUG(
+					m_am_tools, 
+					TRACE_FLAGS_DEFAULT, 
+					(EAPL("WARNING: eapol_core_c::association(): Cannot run eapol_key_state->shutdown() 0x%08x\n"),
+					eapol_key_state));
+			}
+			delete eapol_key_state;
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+#else
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_handler_does_not_exists_error);
+
+#endif //#if !defined(USE_EAPOL_KEY_STATE_OPTIMIZED_4_WAY_HANDSHAKE)
+
+	}
+	else
+	{
+		status = eapol_key_state->initialize(
+			receive_network_id,
+			authentication_type,
+			authenticator_RSNA_IE,
+			supplicant_RSNA_IE,
+			eapol_pairwise_cipher,
+			eapol_group_cipher,
+			pre_shared_key_PSK);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+
+#if !defined(USE_EAPOL_KEY_STATE_OPTIMIZED_4_WAY_HANDSHAKE)
+	status = eapol_key_state->configure();
+	if (status != eap_status_ok)
+	{
+		status = remove_eapol_key_state(
+			&send_network_id);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("WARNING: eapol_core_c::association(): ")
+				 EAPL("remove_eapol_key_state(), eap_status_e %d\n"),
+				status));
+		}
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+#endif //#if !defined(USE_EAPOL_KEY_STATE_OPTIMIZED_4_WAY_HANDSHAKE)
+
+
+	if (authentication_type == eapol_key_authentication_type_RSNA_PSK
+		|| authentication_type == eapol_key_authentication_type_WPA_PSK)
+	{
+
+#if !defined(NO_EAPOL_KEY_STATE_SERVER)
+		if (m_is_client == false)
+		{
+#if defined(USE_EAPOL_KEY_STATE)
+			if (m_skip_start_4_way_handshake == true)
+			{
+				// This is test to skip 4-Way Handshake start.
+				EAP_TRACE_DEBUG(
+					m_am_tools, 
+					TRACE_FLAGS_DEFAULT, 
+					(EAPL("WARNING: eapol_core_c::association(): ")
+					 EAPL("skips start_4_way_handshake()\n")));
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+			}
+			else
+#endif //#if defined(USE_EAPOL_KEY_STATE)
+			{
+				status = eapol_key_state->start_4_way_handshake(
+					receive_network_id);
+			}
+
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+		else
+#endif //#if !defined(NO_EAPOL_KEY_STATE_SERVER)
+		{
+			eapol_key_state->allow_4_way_handshake();
+		}
+	}
+#if defined(USE_EAPOL_KEY_STATE_OPTIMIZED_4_WAY_HANDSHAKE)
+	else if (authentication_type == eapol_key_authentication_type_RSNA_EAP
+		|| authentication_type == eapol_key_authentication_type_WPA_EAP
+		|| authentication_type == eapol_key_authentication_type_802_1X
+		|| authentication_type == eapol_key_authentication_type_WPXM
+		|| authentication_type == eapol_key_authentication_type_WFA_SC)
+	{
+		// Creates a EAP-session.
+		status = m_eap_core->synchronous_create_eap_session(receive_network_id);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+#endif //#if defined(USE_EAPOL_KEY_STATE_OPTIMIZED_4_WAY_HANDSHAKE)
+
+
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+#endif //#if defined(USE_EAPOL_KEY_STATE)
+
+//--------------------------------------------------
+
+#if defined(USE_EAPOL_KEY_STATE)
+/**
+ * @param receive_network_id carries the MAC addresses.
+ * MAC address of Authenticator should be in source address.
+ * MAC address of Supplicant should be in destination address.
+ */
+EAP_FUNC_EXPORT eap_status_e eapol_core_c::disassociation(
+	const eap_am_network_id_c * const receive_network_id
+	)
+{
+	eap_status_e status = eap_status_process_general_error;
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("%s: eapol_core_c::disassociation().\n"),
+		 (m_is_client == true) ? "client": "server"));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: eapol_core_c::disassociation()");
+
+	if (receive_network_id == 0)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+	}
+
+	// Here we swap the addresses.
+	eap_am_network_id_c send_network_id(
+		m_am_tools,
+		receive_network_id->get_destination_id(),
+		receive_network_id->get_source_id(),
+		receive_network_id->get_type());
+	if (send_network_id.get_is_valid_data() == false)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+
+	(void) m_partner->cancel_timer(this, EAPOL_CORE_TIMER_SEND_START_AGAIN_ID);
+
+#if !defined(NO_EAP_SESSION_CORE)
+	// First we remove possible EAP session.
+	(void) m_eap_core->synchronous_remove_eap_session(receive_network_id);
+#endif
+
+	status = init_eapol_key_pmksa_caching_timeout(
+		&send_network_id);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("WARNING: eapol_core_c::disassociation(): ")
+			 EAPL("init_eapol_key_pmksa_caching_timeout(), eap_status_e %d\n"),
+			status));
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+#endif //#if defined(USE_EAPOL_KEY_STATE)
+
+//--------------------------------------------------
+
+/// @see abs_eap_core_c::add_rogue_ap().
+EAP_FUNC_EXPORT eap_status_e eapol_core_c::add_rogue_ap(
+	eap_array_c<eap_rogue_ap_entry_c> & rogue_ap_list)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("%s: eapol_core_c::add_rogue_ap().\n"),
+		 (m_is_client == true) ? "client": "server"));
+
+	const eap_status_e status = m_partner->add_rogue_ap(rogue_ap_list);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e eapol_core_c::tkip_mic_failure(
+	const eap_am_network_id_c * const receive_network_id,
+	const bool fatal_failure_when_true,
+	const eapol_RSNA_key_header_c::eapol_tkip_mic_failure_type_e tkip_mic_failure_type)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("%s: eapol_core_c::tkip_mic_failure().\n"),
+		 (m_is_client == true) ? "client": "server"));
+
+	eap_status_e status = eap_status_process_general_error;
+
+#if defined(USE_EAPOL_KEY_STATE)
+
+	// Here we swap the addresses.
+	eap_am_network_id_c send_network_id(
+		m_am_tools,
+		receive_network_id->get_destination_id(),
+		receive_network_id->get_source_id(),
+		receive_network_id->get_type());
+	if (send_network_id.get_is_valid_data() == false)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	eap_network_id_selector_c state_selector(
+		m_am_tools,
+		&send_network_id);
+
+	if (state_selector.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("tkip_mic_failure(): EAPOL-Key session"),
+		state_selector.get_data(state_selector.get_data_length()),
+		state_selector.get_data_length()));
+
+
+	eapol_key_state_c *eapol_key_state = m_eapol_key_state_map.get_handler(&state_selector);
+
+	if (eapol_key_state != 0)
+	{
+		status = eapol_key_state->tkip_mic_failure(
+			fatal_failure_when_true,
+			tkip_mic_failure_type);
+	}
+	else
+	{
+		status = eap_status_handler_does_not_exists_error;
+	}
+
+#else
+
+	status = eap_status_not_supported;
+
+#endif //#if defined(USE_EAPOL_KEY_STATE)
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eapol_core_c::set_session_timeout(
+	const u32_t /* session_timeout_ms */)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_not_supported);
+}
+
+//--------------------------------------------------
+
+
+
+// End.