eapol/eapol_framework/eapol_common/core/eap_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/eap_core.cpp	Thu Dec 17 08:47:43 2009 +0200
@@ -0,0 +1,5542 @@
+/*
+* 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 44 
+	#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 "eap_core.h"
+#include "eap_core_nak_info.h"
+#include "eap_state_notification.h"
+#include "eap_network_id_selector.h"
+#include "eap_buffer.h"
+#include "eap_header_string.h"
+#include "eap_automatic_variable.h"
+
+
+/**
+ * This is the character that separates routing realms.
+ */
+const u8_t EAP_NAI_ROUTING_REALM_SEPARATOR[] = "!";
+
+/**
+ * This is the at character of NAI.
+ */
+const u8_t EAP_NAI_AT_CHARACTER[] = "@";
+
+
+//--------------------------------------------------
+
+// 
+EAP_FUNC_EXPORT eap_core_c::~eap_core_c()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eap_core_c::~eap_core_c(): %s, %s, this = 0x%08x => 0x%08x.\n"),
+		(m_is_client == true) ? "client": "server",
+		(m_is_tunneled_eap == true) ? "tunneled": "outer most",
+		this,
+		dynamic_cast<abs_eap_base_timer_c *>(this)));
+
+	EAP_ASSERT(m_shutdown_was_called == true);
+
+	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 eap_core_c::eap_core_c(
+	abs_eap_am_tools_c * const tools,
+	abs_eap_core_c * const partner,
+	const bool is_client_when_true,
+	const eap_am_network_id_c * const receive_network_id,
+	const bool is_tunneled_eap)
+	: m_partner(partner)
+	, m_am_tools(tools)
+	, m_type_map(tools, this)
+	, m_current_eap_type(eap_type_none)
+	, m_default_eap_type(eap_type_none)
+	, m_eap_identity(tools)
+	, m_eap_header_offset(0u)
+	, m_MTU(0u)
+	, m_trailer_length(0u)
+	, m_receive_network_id(tools)
+	, m_retransmission(0)
+	, m_retransmission_time(EAP_CORE_RETRANSMISSION_TIME)
+	, m_retransmission_counter(EAP_CORE_RETRANSMISSION_COUNTER)
+	, m_session_timeout(EAP_CORE_SESSION_TIMEOUT)
+	, m_eap_core_failure_received_timeout(EAP_CORE_FAILURE_RECEIVED_TIMEOUT)
+	, m_remove_session_timeout(EAP_CORE_REMOVE_SESSION_TIMEOUT)
+#if defined(USE_EAP_CORE_WAIT_REQUEST_TYPE_TIMER)
+	, m_wait_eap_request_type_timeout(EAP_CORE_WAIT_EAP_REQUEST_TYPE_TIMEOUT)
+	, m_wait_eap_request_type_timeout_set(false)
+#endif //#if defined(USE_EAP_CORE_WAIT_REQUEST_TYPE_TIMER)
+	, m_eap_identity_request_identifier_client(0)
+	, m_is_client(is_client_when_true)
+	, m_is_client_role(is_client_when_true)
+	, m_is_valid(false)
+	, m_client_restart_authentication_initiated(false)
+	, m_marked_removed(false)
+	, m_eap_identity_response_accepted(false)
+	, m_shutdown_was_called(false)
+	, m_eap_type_response_sent(false)
+	, m_is_tunneled_eap(is_tunneled_eap)
+#if defined(USE_EAP_CORE_SERVER)
+	, m_process_eap_nak_immediately(EAP_CORE_PROCESS_EAP_NAK_IMMEDIATELY)
+	, m_nak_process_timer_active(false)
+	, m_eap_identity_request_send(false)
+	, m_eap_identity_response_received(false)
+	, m_eap_failure_sent(false)
+	, m_send_eap_success_after_notification(false)
+#if defined(USE_EAP_CORE_WAIT_REQUEST_TYPE_TIMER)
+	, m_skip_eap_request_identity(false)
+#endif //#if defined(USE_EAP_CORE_WAIT_REQUEST_TYPE_TIMER)
+#endif //#if defined(USE_EAP_CORE_SERVER)
+	, m_use_eap_expanded_type(false)
+	, m_ignore_eap_failure(false)
+	, m_ignore_notifications(false)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eap_core_c::eap_core_c(): %s, %s, this = 0x%08x => 0x%08x, compiled %s %s.\n"),
+		(m_is_client == true) ? "client": "server",
+		(m_is_tunneled_eap == true) ? "tunneled": "outer most",
+		this,
+		dynamic_cast<abs_eap_base_timer_c *>(this),
+		__DATE__,
+		__TIME__));
+
+	eap_status_e status = m_receive_network_id.set_copy_of_network_id(receive_network_id);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return;
+	}
+
+	set_is_valid();
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT abs_eap_core_c * eap_core_c::get_partner()
+{
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	return m_partner;
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT void eap_core_c::set_partner(abs_eap_core_c * const partner)
+{
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	m_partner = partner;
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT void eap_core_c::set_is_valid()
+{
+	m_is_valid = true;
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT bool eap_core_c::get_is_valid()
+{
+	return m_is_valid;
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT void eap_core_c::object_increase_reference_count()
+{
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT u32_t eap_core_c::object_decrease_reference_count()
+{
+	return 0u;
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT bool eap_core_c::get_marked_removed()
+{
+	return m_marked_removed;
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT void eap_core_c::set_marked_removed()
+{
+	m_marked_removed = true;
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT void eap_core_c::unset_marked_removed()
+{
+	m_marked_removed = false;
+}
+
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT void eap_core_c::ignore_notifications()
+{
+	m_ignore_notifications = true;
+}
+
+//--------------------------------------------------
+
+//
+eap_status_e eap_core_c::initialize_asynchronous_init_remove_eap_session(
+	const u32_t remove_session_timeout)
+{
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eap_core_c::initialize_asynchronous_init_remove_eap_session(): %s.\n"),
+		 (m_is_client == true) ? "client": "server"));
+
+	eap_status_e status = eap_status_process_general_error;
+
+
+	if (m_is_client_role == false)
+	{
+		// Server stops re-transmissions.
+		// Client can re-transmit until session is removed.
+		cancel_retransmission();
+	}
+
+	cancel_eap_failure_timeout();
+
+	cancel_session_timeout();
+
+#if defined(USE_EAP_CORE_WAIT_REQUEST_TYPE_TIMER)
+	if (m_is_tunneled_eap == false
+		&& m_is_client_role == true)
+	{
+		cancel_wait_eap_request_type_timeout();
+	}
+#endif //#if defined(USE_EAP_CORE_WAIT_REQUEST_TYPE_TIMER)
+
+	set_marked_removed();
+
+
+	if (remove_session_timeout == 0ul)
+	{
+		status = asynchronous_init_remove_eap_session();
+	}
+	else
+	{
+		EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+		cancel_asynchronous_init_remove_eap_session();
+
+		status = m_partner->set_timer(
+			this,
+			EAP_CORE_REMOVE_SESSION_TIMEOUT_ID,
+			0,
+			remove_session_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("TIMER: %s: %s, EAP_CORE_REMOVE_SESSION_TIMEOUT_ID set %d ms, this = 0x%08x.\n"),
+			 (m_is_client == true) ? "client": "server",
+			 (m_is_tunneled_eap == true) ? "tunneled": "outer most",
+			 remove_session_timeout,
+			 this));
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+eap_status_e eap_core_c::cancel_asynchronous_init_remove_eap_session()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	if (m_partner != 0)
+	{
+		eap_status_e status = m_partner->cancel_timer(
+			this,
+			EAP_CORE_REMOVE_SESSION_TIMEOUT_ID);
+
+		EAP_UNREFERENCED_PARAMETER(status); // in release
+		
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("TIMER: %s: %s, EAP_CORE_REMOVE_SESSION_TIMEOUT_ID cancelled status %d, this = 0x%08x.\n"),
+			 (m_is_client == true ? "client": "server"),
+			 (m_is_tunneled_eap == true) ? "tunneled": "outer most",
+			 status,
+			 this));
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+//--------------------------------------------------
+
+//
+eap_status_e eap_core_c::asynchronous_init_remove_eap_session()
+{
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eap_core_c::asynchronous_init_remove_eap_session(): %s.\n"),
+		 (m_is_client == true) ? "client": "server"));
+
+		eap_am_network_id_c send_network_id(
+			m_am_tools,
+			m_receive_network_id.get_destination_id(),
+			m_receive_network_id.get_source_id(),
+			m_receive_network_id.get_type());
+
+		eap_status_e status = m_partner->asynchronous_init_remove_eap_session(
+			&send_network_id);
+
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+eap_status_e eap_core_c::init_end_of_session(
+	const abs_eap_state_notification_c * const state)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eap_core_c::init_end_of_session(): %s.\n"),
+		 (m_is_client == true) ? "client": "server"));
+
+	eap_status_e status(eap_status_process_general_error);
+
+	// Normally we will remove session after authentication ends.
+	// Remove session only if the stack is not already being deleted
+	if (m_shutdown_was_called == false)
+	{
+		#if defined(USE_EAPOL_KEY_STATE) && defined(USE_EAP_CORE_RESTART_AUTHENTICATION)
+			#error ERROR: USE_EAPOL_KEY_STATE and USE_EAP_CORE_RESTART_AUTHENTICATION cannot be used same time.
+		#endif //#if defined(USE_EAPOL_KEY_STATE) && defined(USE_EAP_CORE_RESTART_AUTHENTICATION)
+
+		#if defined(USE_EAP_CORE_SIMULATOR_VERSION) && defined(USE_EAP_CORE_RESTART_AUTHENTICATION)
+
+			// Simulator reuses current session.
+			status = restart_authentication(
+				state->get_send_network_id(),
+				m_is_client);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+		#elif defined(USE_EAP_CORE_SIMULATOR_VERSION) && defined(USE_EAPOL_KEY_STATE)
+
+			EAP_TRACE_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("eap_core_c::state_notification(): %s, %s, Ignored notification: ")
+				 EAPL("Protocol layer %d, EAP type 0x%02x, State transition from ")
+				 EAPL("%d=%s to %d=%s, client %d.\n"),
+				 (m_is_client == true) ? "client": "server",
+				 (m_is_tunneled_eap == true) ? "tunneled": "outer most",
+				 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_EAP_CORE_SIMULATOR_VERSION)
+
+		status = initialize_asynchronous_init_remove_eap_session(m_remove_session_timeout);
+		if (status != eap_status_ok)
+		{
+			EAP_UNREFERENCED_PARAMETER(state);
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+	else
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("eap_core_c::state_notification(): %s, %s, Ignored notification: ")
+			 EAPL("Protocol layer %d, EAP type 0x%02x, State transition from ")
+			 EAPL("%d=%s to %d=%s, client %d when shutdown was called.\n"),
+			 (m_is_client == true) ? "client": "server",
+			 (m_is_tunneled_eap == true) ? "tunneled": "outer most",
+			 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()));
+
+		status = eap_status_ok;
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void eap_core_c::state_notification(
+	const abs_eap_state_notification_c * const state)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	eap_status_string_c status_string;
+	eap_header_string_c eap_string;
+	EAP_UNREFERENCED_PARAMETER(status_string); // in release
+	EAP_UNREFERENCED_PARAMETER(eap_string); // in release
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eap_core_c::state_notification(), %s, %s, protocol_layer %d=%s, protocol %d=%s, EAP-type 0x%08x=%s.\n"),
+		(m_is_client == true) ? "client": "server",
+		(m_is_tunneled_eap == true) ? "tunneled": "outer most",
+		state->get_protocol_layer(),
+		state->get_protocol_layer_string(),
+		state->get_protocol(),
+		state->get_protocol_string(),
+		convert_eap_type_to_u32_t(state->get_eap_type()),
+		eap_string.get_eap_type_string(state->get_eap_type())));
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eap_core_c::state_notification(), %s, %s, current_state %d=%s, error %d=%s.\n"),
+		(m_is_client == true) ? "client": "server",
+		(m_is_tunneled_eap == true) ? "tunneled": "outer most",
+		state->get_current_state(),
+		state->get_current_state_string(),
+		state->get_authentication_error(),
+		status_string.get_status_string(state->get_authentication_error())));
+
+	if (m_ignore_notifications == true
+		|| m_partner == 0)
+	{
+		return;
+	}
+
+	// Check if the notification is from the current active type
+	if (state->get_protocol_layer() == eap_protocol_layer_general)
+	{
+		 // These notications are allowed always.
+	}
+	else if (state->get_protocol_layer() == eap_protocol_layer_eap
+		&& state->get_eap_type() != m_current_eap_type)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("eap_core_c::state_notification(): %s, %s, Ignored notification: ")
+			 EAPL("Protocol layer %d, non-active EAP type 0x%02x, current EAP type 0x%08x, State transition from ")
+			 EAPL("%d=%s to %d=%s, client %d\n"),
+			 (m_is_client == true) ? "client": "server",
+			 (m_is_tunneled_eap == true) ? "tunneled": "outer most",
+			state->get_protocol_layer(),
+			state->get_protocol(),
+			convert_eap_type_to_u32_t(m_current_eap_type),
+			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_response_received)
+		{
+			m_eap_identity_response_accepted = true;
+
+			EAP_TRACE_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("EAP-Response/Identity received: %s, %s\n"),
+				 (m_is_client == true) ? "client": "server",
+				 (m_is_tunneled_eap == true) ? "tunneled": "outer most"
+				 ));
+		}
+		else if (state->get_current_state() == eap_state_identity_request_received)
+		{
+			EAP_TRACE_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("EAP-Request/Identity received: %s, %s\n"),
+				 (m_is_client == true) ? "client": "server",
+				 (m_is_tunneled_eap == true) ? "tunneled": "outer most"
+				 ));
+		}
+		else if (state->get_current_state() == eap_state_authentication_terminated_unsuccessfully)
+		{
+			EAP_TRACE_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("ERROR: eap_core_c::state_notification(): %s, %s: EAP-authentication terminated unsuccessfully.\n"),
+				 (m_is_client == true) ? "client": "server",
+				 (m_is_tunneled_eap == true) ? "tunneled": "outer most"
+				 ));
+
+#if defined(USE_EAP_CORE_SERVER)
+			if (m_is_client == false
+				&& (m_eap_type_response_sent == true
+					|| m_eap_identity_response_received == true)
+				&& m_send_eap_success_after_notification == false)
+			{
+				/**
+				 * 2003-10-01 draft-ietf-eap-rfc2284bis-06.txt chapter 2.
+				 * Extensible Authentication Protocol (EAP):
+				 * The authenticator MUST NOT send a Success or Failure packet when retransmitting
+				 * or when it fails to get a response from the peer.
+				 * In the case eap_state_authentication_terminated_unsuccessfully we will need a flag
+				 * that indicates whether there was a response from client or not.
+				 * If there was a response server must send EAP-Failure.
+				 * If there was NO response from client server
+				 * does NOT send EAP-Failure.
+				 */
+				send_eap_failure(
+					state->get_send_network_id(),
+					state->get_eap_identifier());
+			}
+#endif //#if defined(USE_EAP_CORE_SERVER)
+
+			(void) init_end_of_session(state);
+		}
+		else if (state->get_current_state() == eap_state_authentication_finished_successfully)
+		{
+			EAP_TRACE_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("eap_core_c::state_notification(): %s, %s: EAP-authentication finished successfully.\n"),
+				 (m_is_client == true) ? "client": "server",
+				 (m_is_tunneled_eap == true) ? "tunneled": "outer most"
+				 ));
+
+
+#if defined(USE_EAP_CORE_SERVER)
+			if (m_is_client == false
+				&& m_eap_type_response_sent == true
+				&& m_send_eap_success_after_notification == false)
+			{
+				// Here we test whether the EAP-type allow send EAP-Success.
+				// For example PEAP v1 does not allow send EAP-Success.
+				if (state->get_allow_send_eap_success() == true)
+				{
+					send_eap_success(
+						state->get_send_network_id(),
+						state->get_eap_identifier());
+				}
+
+				// Now we can send a new EAP-Request/Identity.
+				m_eap_identity_request_send = false;
+			}
+#endif //#if defined(USE_EAP_CORE_SERVER)
+
+			(void) init_end_of_session(state);
+		}
+		else if (m_is_client == true
+			&& state->get_current_state() == eap_state_use_eap_failure_in_termination)
+		{
+			// Client should accept EAP-Failure quietly.
+			m_ignore_eap_failure = true;
+
+			(void) init_end_of_session(state);
+		}
+#if defined(USE_EAP_CORE_SERVER)
+		else if (m_is_client == false
+			&& state->get_current_state() == eap_state_use_eap_failure_in_termination)
+		{
+			send_eap_failure(
+				state->get_send_network_id(),
+				state->get_eap_identifier());
+
+			(void) init_end_of_session(state);
+		}
+		else if (m_current_eap_type == eap_type_peap
+			&& state->get_current_state() == eap_state_authentication_wait_tppd_peapv1_empty_acknowledge)
+		{
+			send_eap_success(
+				state->get_send_network_id(),
+				state->get_eap_identifier());
+
+			return;
+		}
+#endif //#if defined(USE_EAP_CORE_SERVER)
+	}
+
+
+	m_partner->state_notification(state);
+
+
+#if defined(USE_EAP_CORE_SERVER)
+	// EAP-Success is send after the success notification is forwarded to lower layer.
+	// This allows to combine success notification and sent EAP-success packet.
+	if (state->get_current_state() == eap_state_authentication_finished_successfully)
+	{
+		if (m_is_client == false
+			&& m_eap_type_response_sent == true
+			&& m_send_eap_success_after_notification == true)
+		{
+			// Here we test whether the EAP-type allow send EAP-Success.
+			// For example PEAP v1 does not allow send EAP-Success.
+			if (state->get_allow_send_eap_success() == true)
+			{
+				send_eap_success(
+					state->get_send_network_id(),
+					state->get_eap_identifier());
+			}
+			
+			// Now we can send a new EAP-Request/Identity.
+			m_eap_identity_request_send = false;
+		}
+	}
+	else if (state->get_current_state() == eap_state_authentication_terminated_unsuccessfully)
+	{
+		if (m_is_client == false
+			&& (m_eap_type_response_sent == true
+				|| m_eap_identity_response_received == true)
+			&& m_send_eap_success_after_notification == true)
+		{
+			/**
+			 * 2003-10-01 draft-ietf-eap-rfc2284bis-06.txt chapter 2.
+			 * Extensible Authentication Protocol (EAP):
+			 * The authenticator MUST NOT send a Success or Failure packet when retransmitting
+			 * or when it fails to get a response from the peer.
+			 * In the case eap_state_authentication_terminated_unsuccessfully we will need a flag
+			 * that indicates whether there was a response from client or not.
+			 * If there was a response server must send EAP-Failure.
+			 * If there was NO response from client server
+			 * does NOT send EAP-Failure.
+			 */
+			send_eap_failure(
+				state->get_send_network_id(),
+				state->get_eap_identifier());
+		}
+	}
+#endif //#if defined(USE_EAP_CORE_SERVER)
+
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_base_type_c * eap_core_c::load_type(
+	const eap_type_value_e type,
+	const eap_type_value_e tunneling_type,
+	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_base_type_c *handler = 0;
+
+	eap_status_e status = m_partner->load_module(
+		type,
+		tunneling_type,
+		this,
+		&handler,
+		m_is_client,
+		receive_network_id);
+	if (status != eap_status_ok)
+	{
+		if (handler != 0)
+		{
+			handler->shutdown();
+			delete handler;
+		}
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return 0;
+	}
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return handler;
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e eap_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 eap_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);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eap_core_c::initialize_session_timeout(const u32_t session_timeout_ms)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	cancel_session_timeout();
+
+	eap_status_e status = m_partner->set_timer(
+		this,
+		EAP_CORE_SESSION_TIMEOUT_ID,
+		0,
+		session_timeout_ms);
+
+	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: %s, EAP_CORE_SESSION_TIMEOUT_ID set %d ms, this = 0x%08x.\n"),
+		 (m_is_client == true) ? "client": "server",
+		 (m_is_tunneled_eap == true) ? "tunneled": "outer most",
+		 session_timeout_ms,
+		 this));
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e eap_core_c::cancel_session_timeout()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	eap_status_e status = m_partner->cancel_timer(
+		this,
+		EAP_CORE_SESSION_TIMEOUT_ID);
+	
+	EAP_UNREFERENCED_PARAMETER(status); // in release
+	
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("TIMER: %s: %s, EAP_CORE_SESSION_TIMEOUT_ID cancelled status %d, this = 0x%08x.\n"),
+		 (m_is_client == true ? "client": "server"),
+		 (m_is_tunneled_eap == true) ? "tunneled": "outer most",
+		 status,
+		 this));
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT void eap_core_c::trace_eap_packet(
+	eap_const_string prefix,
+	const eap_header_wr_c * const eap_header)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	// Get rid of warnings in release version
+	EAP_UNREFERENCED_PARAMETER(eap_header);
+	EAP_UNREFERENCED_PARAMETER(prefix);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT|TRACE_TEST_VECTORS, 
+		(EAPL("%s EAP_Core: %s, %s, code=0x%02x=%s, identifier=0x%02x, length=0x%04x, type=0x%08x=%s, packet length 0x%04x\n"),
+		prefix,
+		(m_is_client == true) ? "client": "server",
+		(m_is_tunneled_eap == true) ? "tunneled": "outer most",
+		eap_header->get_code(),
+		eap_header->get_code_string(),
+		eap_header->get_identifier(),
+		eap_header->get_length(),
+		convert_eap_type_to_u32_t(eap_header->get_type()),
+		eap_header->get_type_string(),
+		eap_header->get_length()));
+}
+
+
+//--------------------------------------------------
+
+#if defined(USE_EAP_CORE_SERVER)
+
+EAP_FUNC_EXPORT eap_status_e eap_core_c::restart_with_new_type(
+	const eap_type_value_e used_eap_type,
+	const eap_am_network_id_c * const receive_network_id,
+	const u8_t eap_identifier)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eap_core_c::restart_with_new_type(): %s\n"),
+		(m_is_client == true) ? "client": "server"));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: eap_core_c::restart_with_new_type()");
+
+	// Here we need to re-create the received EAP-Response/Identity message.
+
+	if (m_eap_identity.get_is_valid_data() == false)
+	{
+		// No saved EAP-Identity. Cannot continue.
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_eap_identity);
+	}
+
+	eap_status_e status = eap_status_process_general_error;
+
+	eap_buf_chain_wr_c response_packet(
+		eap_write_buffer, 
+		m_am_tools, 
+		EAP_CORE_PACKET_BUFFER_LENGTH);
+
+	if (response_packet.get_is_valid() == false)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("packet buffer corrupted: %s, %s\n"),
+			 (m_is_client == true) ? "client": "server",
+			 (m_is_tunneled_eap == true) ? "tunneled": "outer most"
+			 ));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	status = create_eap_identity_response(
+		&response_packet,
+		&m_eap_identity,
+		eap_identifier);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+
+	eap_header_wr_c eap(
+		m_am_tools,
+		response_packet.get_data_offset(m_eap_header_offset, response_packet.get_data_length()),
+		response_packet.get_data_length());
+	if (eap.get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+	}
+
+	// Bacause we start with a new EAP-type EAP-Response/Identity is prosessed again.
+	m_eap_identity_response_accepted = false;
+
+	m_ignore_eap_failure = false;
+
+	status = packet_process_type(
+		used_eap_type,
+		receive_network_id,
+		&eap,
+		eap.get_length());
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+#endif //#if defined(USE_EAP_CORE_SERVER)
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e eap_core_c::client_proposes_eap_types(
+	const eap_am_network_id_c * const receive_network_id,
+	const u8_t eap_identifier)
+{
+	eap_array_c<eap_type_value_e> eap_type_list(m_am_tools);
+
+	/**
+	 * @{ 2005-04-19 complete Expanded Nak Type to client_proposes_eap_types(). }
+	 */
+	eap_status_e status = get_eap_type_list(&eap_type_list);
+	if (status != eap_status_ok)
+	{
+		eap_type_list.reset();
+
+		eap_type_value_e * default_eap_type = new eap_type_value_e(m_default_eap_type);
+		if (default_eap_type == 0)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = eap_type_list.add_object(default_eap_type, true);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	status = send_eap_nak_response(
+		receive_network_id,
+		eap_identifier,
+		&eap_type_list);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e eap_core_c::packet_process_type(
+	const eap_type_value_e used_eap_type,
+	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_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eap_core_c::packet_process_type(): %s\n"),
+		(m_is_client == true) ? "client": "server"));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: eap_core_c::packet_process_type()");
+
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	if (packet_data == 0)
+	{
+		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 < 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_too_short_message);
+	}
+
+	eap_header_wr_c eap(
+		m_am_tools,
+		packet_data->get_header_buffer(packet_data->get_header_buffer_length()),
+		packet_data->get_header_buffer_length());
+
+	if (eap.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 < eap.get_length())
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_too_short_message);
+	}
+
+	if (eap.get_code() == eap_code_none)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_eap_code);
+	}
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_TEST_VECTORS, 
+		(EAPL("--------------------------------------------------------\n")));
+
+	eap_status_e status = eap.check_header();
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+
+
+	status = eap_status_process_general_error;
+
+	if (m_eap_type_response_sent == false
+		&& m_current_eap_type != used_eap_type)
+	{
+		status = check_is_valid_eap_type(used_eap_type);
+		if (status != eap_status_ok)
+		{
+			if (m_is_client_role == true)
+			{
+				// Client does not accept proposed EAP type.
+				// We must send EAP-Response/Nak message with list of our own preferred EAP-Types.
+				status = client_proposes_eap_types(
+					receive_network_id,
+					eap.get_identifier());
+
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			} 
+			else 
+			{
+				// Not acceptable EAP-type in the server.
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+
+		// Now the current EAP-type is used_eap_type.
+
+		// First remove current EAP-type.
+		eap_variable_data_c selector(m_am_tools);
+		status = selector.set_copy_of_buffer(&m_current_eap_type, sizeof(m_current_eap_type));
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+		eap_base_type_c *handler = m_type_map.get_handler(&selector);
+
+		// Change the current EAP-type here because shutdown could cause state notifications from old EAP-type.
+		m_current_eap_type = used_eap_type;
+
+		if (handler != 0)
+		{
+			status = handler->shutdown();
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = m_type_map.remove_handler(&selector, true);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+	}
+
+	// Here we query the desired EAP-type.
+	eap_variable_data_c selector(m_am_tools);
+	u64_t selector_eap_type = convert_eap_type_to_u64_t(used_eap_type);
+	status = selector.set_buffer(&selector_eap_type, sizeof(selector_eap_type), false, false);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	eap_base_type_c *handler = m_type_map.get_handler(&selector);
+	
+	// Check if we already have this type loaded.
+	if (handler == 0)
+	{
+		// No. Load it.
+		if (m_eap_type_response_sent == false
+			&& eap.get_type() != eap_type_identity
+			&& (eap.get_code() == eap_code_request
+				|| eap.get_code() == eap_code_response))
+		{
+			// Here we need a check that proposed EAP type is valid for us.
+			status = check_is_valid_eap_type(used_eap_type);
+			if (status != eap_status_ok)
+			{
+				if (m_is_client_role == true)
+				{
+					// Client does not accept proposed EAP type.
+					// We must send EAP-Response/Nak message with list of our own preferred EAP-Types.
+					status = client_proposes_eap_types(
+						receive_network_id,
+						eap.get_identifier());
+
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, status);
+				} 
+				else 
+				{
+					// Not acceptable EAP-type in the server.
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, status);
+				}
+			}
+			// Change the current eap type.
+			m_current_eap_type = used_eap_type;
+		}
+		else if (eap.get_code() == eap_code_success
+				|| eap.get_code() == eap_code_failure)
+		{
+			// EAP-Success or EAP-Failure is not allowed at this state.
+			// This packet is dropped quietly.
+			EAP_TRACE_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("WARNING: eap_core_c::packet_process_type(): %s, %s, drops %s quietly.\n"),
+				 (m_is_client_role == true) ? "client": "server",
+				 (m_is_tunneled_eap == true) ? "tunneled": "outer most",
+				 eap.get_code_string()
+				 ));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+		}
+		else if (m_eap_type_response_sent == false
+			&& eap.get_type() == eap_type_identity
+			&& (eap.get_code() == eap_code_request
+				|| eap.get_code() == eap_code_response))
+		{
+			// EAP-Request/Identity is allowed at this state.
+			EAP_ASSERT(used_eap_type != eap_type_none);
+		}
+		else
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_eap_code);
+		}
+
+		// A new EAP-type is needed. The load_type() allocates new object.
+		handler = load_type(
+			used_eap_type,
+			eap_type_none,
+			receive_network_id);
+		if (handler != 0
+			&& handler->get_is_valid() == true)
+		{
+			status = handler->configure();
+			if (status != eap_status_ok)
+			{
+				handler->shutdown();
+				delete handler;
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = m_type_map.add_handler(&selector, handler);
+			if (status != eap_status_ok)
+			{
+				handler->shutdown();
+				delete handler;
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+		else
+		{
+			if (handler != 0)
+			{
+				// Handler not constructed successfully.
+				handler->shutdown();
+				delete handler;
+				status = eap_status_allocation_error;
+			}
+			else
+			{
+				status = eap_status_type_does_not_exists_error;
+			}
+
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	} 
+
+#if defined(USE_EAP_CORE_SERVER)
+	// We now have handler. Process packet
+	if (m_nak_process_timer_active == true)
+	{
+		m_partner->cancel_timer(
+			this,
+			EAP_CORE_DELAYED_EAP_NAK_PROCESS_ID);
+
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("TIMER: %s: %s, EAP_CORE_DELAYED_EAP_NAK_PROCESS_ID cancelled, this = 0x%08x.\n"),
+			 (m_is_client_role == true ? "client": "server"),
+			 (m_is_tunneled_eap == true) ? "tunneled": "outer most",
+			 this));
+
+		m_nak_process_timer_active = false;
+	}
+#endif //#if defined(USE_EAP_CORE_SERVER)
+
+
+	if (m_is_client_role == true
+		&& eap.get_code() == eap_code_request
+		&& (eap.get_type() == eap_type_identity
+			|| eap.get_type() == eap_type_notification))
+	{
+		// Client handles this packet, which is EAP-Request/Identity or EAP-Request/Notification.
+
+		if (eap.get_type() == eap_type_identity)
+		{
+			status = handle_eap_identity_request(
+				used_eap_type,
+				eap.get_identifier(),
+				receive_network_id);
+		}
+		else if (eap.get_type() == eap_type_notification)
+		{
+			// 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());
+
+			/**
+			 * @{ 2003-10-02 draft-ietf-eap-rfc2284bis-06.txt chapter 5.2 Notification:
+			 * The peer SHOULD display this message to the user or log it if it
+			 * cannot be displayed.  The Notification Type is intended to provide
+			 * an acknowledged notification of some imperative nature, but it is
+			 * not an error indication, and therefore does not change the state
+			 * of the peer.  Examples include a password with an expiration time
+			 * that is about to expire, an OTP sequence integer which is nearing
+			 * 0, an authentication failure warning, etc.  In most circumstances,
+			 * Notification should not be required.
+			 * }
+			 */
+			status = send_eap_notification_response(
+				&send_network_id,
+				eap.get_identifier());
+		}
+	}
+#if defined(USE_EAP_CORE_SERVER)
+	else if (m_is_client_role == false
+		&& eap.get_code() == eap_code_response
+		&& eap.get_type() == eap_type_identity)
+	{
+		// Server handles this EAP-Response/Identity packet.
+
+		if (m_eap_identity_response_accepted == false)
+		{
+			m_eap_identity_response_received = true;
+
+			status = handle_eap_identity_response(
+				handler,
+				used_eap_type,
+				receive_network_id,
+				&eap,
+				packet_length);
+
+			EAP_GENERAL_HEADER_COPY_ERROR_PARAMETERS(packet_data, &eap);
+		}
+		else
+		{
+			// Do not accept multiple EAP-Response/Identity messages.
+			EAP_TRACE_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("WARNING: EAP_Core: %s,%s, packet dropped quietly. m_eap_type_response_sent %d, ")
+				 EAPL("EAP-Type 0x%08x, m_current_eap_type 0x%08x\n"),
+				 (m_is_client_role == true) ? "client": "server",
+				 (m_is_tunneled_eap == true) ? "tunneled": "outer most",
+				 m_eap_type_response_sent,
+				 convert_eap_type_to_u32_t(eap.get_type()),
+				 convert_eap_type_to_u32_t(m_current_eap_type)));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
+		}
+	}
+#endif //#if defined(USE_EAP_CORE_SERVER)
+	else if ((eap.get_code() == eap_code_request
+				|| eap.get_code() == eap_code_response)
+			&& eap.get_type() == used_eap_type
+		|| eap.get_code() == eap_code_success
+		|| eap.get_code() == eap_code_failure)
+	{
+		// Client and server handles this packet.
+		// Packet is EAP-Request, EAP-Response, EAP-Success or EAP-Failure.
+		// EAP-Request and EAP-Response must be of the used EAP-type.
+
+		if (m_is_client_role == false)
+		{
+			// Server received EAP-Response from client.
+			// Now server could send EAP-Failure or EAP-success to client.
+			// See draft-ietf-eap-rfc2284bis-06.txt chapter 2. Extensible Authentication Protocol (EAP).
+			if (m_eap_type_response_sent == false)
+			{
+				// 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());
+
+				// Send state change notification
+				eap_state_notification_c notification(
+					m_am_tools,
+					&send_network_id,
+					m_is_client,
+					eap_state_notification_eap,
+					eap_protocol_layer_eap,
+					m_current_eap_type,
+					eap_state_none,
+					eap_state_eap_response_sent,
+					eap.get_identifier(),
+					false);
+				state_notification(&notification);
+			}
+			m_eap_type_response_sent = true;
+		}
+#if defined(USE_EAP_CORE_WAIT_REQUEST_TYPE_TIMER)
+		else
+		{
+			if (eap.get_code() == eap_code_request
+				&& m_is_tunneled_eap == false
+				&& m_is_client_role == true)
+			{
+				// We got the response. Now we let the session timer handle rest of timeout cases.
+				cancel_wait_eap_request_type_timeout();
+			}
+		}
+#endif //#if defined(USE_EAP_CORE_WAIT_REQUEST_TYPE_TIMER)
+
+		if (m_ignore_eap_failure == false
+			&& eap.get_code() == eap_code_failure)
+		{
+			// Set timer to delay EAP-Failure handling.
+			// If no other packet is received session will be
+			// terminated after timeout.
+			set_eap_failure_timeout();
+		}
+		else
+		{
+			cancel_eap_failure_timeout();
+		}
+
+		status = handler->packet_process(
+			receive_network_id,
+			&eap,
+			packet_length);
+
+		EAP_GENERAL_HEADER_COPY_ERROR_PARAMETERS(packet_data, &eap);
+
+		if (status == eap_status_success)
+		{
+			// NOTE state_notification() will send EAP-Success message.
+		}
+	}
+	else
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("ERROR: %s, %s, Illegal EAP-Code %d=0x%02x=%s, EAP-Type 0x%08x=%s\n"),
+			 (m_is_client_role == true) ? "client": "server",
+			 (m_is_tunneled_eap == true) ? "tunneled": "outer most",
+			 eap.get_code(),
+			 eap.get_code(),
+			 eap.get_code_string(),
+			 convert_eap_type_to_u32_t(eap.get_type()),
+			 eap.get_type_string()));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_eap_code);
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+/**
+ * @{ 2003-10-01 draft-ietf-eap-rfc2284bis-06.txt chapter 2.1 Support for sequences:
+ * An EAP conversation MAY utilize a sequence of methods.  A common
+ * example of this is an Identity request followed by a single EAP
+ * authentication method such as an MD5-Challenge.  However the peer and
+ * authenticator MUST utilize only one authentication method (Type 4 or
+ * greater) within an EAP conversation, after which the authenticator
+ * MUST send a Success or Failure packet.
+ * Once a peer has sent a Response of the same Type as the initial
+ * Request, an authenticator MUST NOT send a Request of a different Type
+ * prior to completion of the final round of a given method (with the
+ * exception of a Notification-Request) and MUST NOT send a Request for
+ * an additional method of any Type after completion of the initial
+ * authentication method; a peer receiving such Requests MUST treat them
+ * as invalid, and silently discard them. As a result, Identity Requery
+ * is not supported.
+ * A peer MUST NOT send a Nak (legacy or expanded) in reply to a
+ * Request, after an initial non-Nak Response has been sent.  Since
+ * spoofed EAP Request packets may be sent by an attacker, an
+ * authenticator receiving an unexpected Nak SHOULD discard it and log
+ * the event.
+ * Multiple authentication methods within an EAP conversation are not
+ * supported due to their vulnerability to man-in-the-middle attacks
+ * (see Section 7.4) and incompatibility with existing implementations.
+ * }
+ */
+EAP_FUNC_EXPORT eap_status_e eap_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_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eap_core_c::packet_process(): %s\n"),
+		(m_is_client == true) ? "client": "server"));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: eap_core_c::packet_process()");
+
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	if (packet_data == 0
+		|| packet_data->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 (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_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_too_short_message);
+	}
+
+	eap_header_wr_c eap(
+		m_am_tools,
+		packet_data->get_header_buffer(packet_data->get_header_buffer_length()),
+		packet_data->get_header_buffer_length());
+
+	if (eap.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 < eap.get_length())
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_too_short_message);
+	}
+
+	if (eap.get_code() == eap_code_none)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_eap_code);
+	}
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_TEST_VECTORS, 
+		(EAPL("--------------------------------------------------------\n")));
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools, 
+		EAP_TRACE_FLAGS_MESSAGE_DATA|TRACE_TEST_VECTORS, 
+		(EAPL("EAP-packet"),
+		 eap.get_header_buffer(packet_length),
+		 packet_length));
+
+	trace_eap_packet("->", &eap);
+
+	eap_status_e status = eap.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 defined (_DEBUG)
+	if (m_retransmission != 0)
+	{
+		eap_header_string_c eap_string;
+
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("EAP_Core: eap_core_c::packet_process(): %s, retransmission counter %d, retrans EAP-type %s, retrans EAP-Id %d, current EAP-type %s, current EAP-Id %d, session 0x%08x.\n"),
+			 (m_is_client_role == true) ? "client": "server",
+			 m_retransmission->get_retransmission_counter(),
+			 eap_string.get_eap_type_string(m_retransmission->get_eap_type()),
+			 m_retransmission->get_eap_identifier(),
+			 eap_string.get_eap_type_string(eap.get_type()),
+			 eap.get_identifier(),
+			 this));
+	}
+	else
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("EAP_Core: eap_core_c::packet_process(): %s, retransmission NULL, session 0x%08x.\n"),
+			 (m_is_client_role == true) ? "client": "server",
+			 this));
+	}
+#endif //#if defined (_DEBUG)
+
+
+	if (m_is_client_role == true
+		&& m_retransmission != 0
+		&& m_retransmission->get_eap_type() == eap.get_type()
+		&& m_retransmission->get_eap_identifier() == eap.get_identifier())
+	{
+		if (m_retransmission->get_retransmission_counter() > 0)
+		{
+			EAP_TRACE_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("EAP_Core: eap_core_c::packet_process(): %s, retransmits previous packet, counter %d, session 0x%08x.\n"),
+				 (m_is_client_role == true) ? "client": "server",
+				 m_retransmission->get_retransmission_counter(),
+				 this));
+
+			// We have the previous send EAP-packet stored.
+			// It does match to the current query.
+			// We could send the previous EAP-packet again.
+			status = resend_packet(
+				m_retransmission->get_send_network_id(),
+				m_retransmission->get_sent_packet(),
+				m_retransmission->get_header_offset(),
+				m_retransmission->get_data_length(),
+				m_retransmission->get_buffer_size(),
+				m_retransmission->get_retransmission_counter()
+				);
+
+			m_retransmission->get_next_retransmission_counter(); // This decrements the counter.
+		}
+		else
+		{
+			EAP_TRACE_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("EAP_Core: eap_core_c::packet_process(): %s, Does not retransmit previous packet, counter %d, session 0x%08x.\n"),
+				 (m_is_client_role == true) ? "client": "server",
+				 m_retransmission->get_retransmission_counter(),
+				 this));
+
+			status = eap_status_unexpected_message;
+		}
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+#if defined(USE_EAP_CORE_SERVER)
+	else if (m_is_client_role == false
+		&& m_retransmission != 0
+		&& m_retransmission->get_eap_type() == eap.get_type()
+		&& m_retransmission->get_eap_identifier() > eap.get_identifier())
+	{
+		// Here we assume the EAP-Identifier increases. This is for testing purposes.
+		// This packet is old response, drop it.
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("EAP_Core: eap_core_c::packet_process(): %s, Does not process old packet, EAP-Identifier of last received response %d, EAP-Identifier of the packet %d, session 0x%08x.\n"),
+			 (m_is_client_role == true) ? "client": "server",
+			 m_retransmission->get_eap_identifier(),
+			 eap.get_identifier(),
+			 this));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_unexpected_message);
+	}
+#endif //#if defined(USE_EAP_CORE_SERVER)
+	else if (get_marked_removed() == true
+			 && eap.get_code() != eap_code_success
+			 && eap.get_code() != eap_code_failure)
+	{
+		// NOTE, this delayed reset of session is used bacause in some cases cannot be responsed
+		// 4-Way Handshake message fast enough.
+
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("eap_core_c::packet_process(): %s, resets session, session 0x%08x.\n"),
+			 (m_is_client_role == true) ? "client": "server",
+			this));
+
+		unset_marked_removed();
+
+		reset();
+	}
+
+
+	eap_type_value_e used_eap_type = eap_type_none;
+
+	if (eap.get_code() == eap_code_request
+		&& m_is_client_role == true)
+	{
+		// Send ID using default EAP type, this is our best quess of other peer's EAP type.
+		// Other peer will sent the real EAP type later and we can NAK it then
+		// and send our own EAP type.
+		if (m_current_eap_type == eap_type_none)
+		{
+			// In Symbian implementation the default type is the highest priority EAP type. 
+			// At the moment it is always used to reply to the identity request.
+			m_current_eap_type = m_default_eap_type;
+			used_eap_type = m_current_eap_type;
+		}
+
+		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+		if (eap.get_length() > eap_header_base_c::get_header_length())
+		{
+			if (m_eap_type_response_sent == true
+				&& eap.get_type() != m_current_eap_type)
+			{
+				if (eap.get_type() == eap_type_tlv_extensions)
+				{
+					// Send EAP-Response/Nak to show this is not supported.
+
+					eap_array_c<eap_type_value_e> eap_type_list(m_am_tools);
+
+					status = send_eap_nak_response(
+						receive_network_id,
+						eap.get_identifier(),
+						&eap_type_list);
+
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, status);
+				}
+				else
+				{
+					/*
+					 * 2003-10-01 draft-ietf-eap-rfc2284bis-06.txt chapter 2.1 Support for sequences:
+					 * An EAP conversation MAY utilize a sequence of methods.  A common
+					 * example of this is an Identity request followed by a single EAP
+					 * authentication method such as an MD5-Challenge.  However the peer and
+					 * authenticator MUST utilize only one authentication method (Type 4 or
+					 * greater) within an EAP conversation, after which the authenticator
+					 * MUST send a Success or Failure packet.
+					 * Once a peer has sent a Response of the same Type as the initial
+					 * Request, an authenticator MUST NOT send a Request of a different Type
+					 * prior to completion of the final round of a given method (with the
+					 * exception of a Notification-Request) and MUST NOT send a Request for
+					 * an additional method of any Type after completion of the initial
+					 * authentication method; a peer receiving such Requests MUST treat them
+					 * as invalid, and silently discard them. As a result, Identity Requery
+					 * is not supported.
+					 */
+					EAP_TRACE_DEBUG(
+						m_am_tools, 
+						TRACE_FLAGS_DEFAULT, 
+						(EAPL("WARNING: EAP_Core: %s, %s, packet dropped quietly. m_eap_type_response_sent %d, ")
+						 EAPL("EAP-Type 0x%08x, m_current_eap_type 0x%08x\n"),
+						 (m_is_client_role == true) ? "client": "server",
+						 (m_is_tunneled_eap == true) ? "tunneled": "outer most",
+						 m_eap_type_response_sent,
+						 convert_eap_type_to_u32_t(eap.get_type()),
+						 convert_eap_type_to_u32_t(m_current_eap_type)));
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
+				}
+			}
+			else if (eap.get_type() == eap_type_identity)
+			{
+				// Should we respond here with another type?
+				/**
+				 * @{ Check if this is a re-transmission or a new request.
+				 * If this is re-transmission respond using the same type as previously. 
+				 * Otherwise assume that peer did not like our previous identity and 
+				 * try another configured type.
+				 * At the moment just try the last type that was used.
+				 * }
+				 */
+				used_eap_type = m_current_eap_type;
+			}
+			else if (eap.get_type() == eap_type_notification)
+			{
+				// Here we are again on thin ice.
+				// Best ques is the las used EAP type.
+				used_eap_type = m_current_eap_type;
+			}
+			else
+			{
+				// Here we know what the server really wants
+				// use this EAP-Type.
+				used_eap_type = eap.get_type();
+			}
+		}
+		else
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_eap_code);
+		}
+	}
+#if defined(USE_EAP_CORE_SERVER)
+	else if (eap.get_code() == eap_code_response
+		&& m_is_client_role == false)
+	{
+		// Received ID using some EAP type, this may be other than our current type.
+
+		if (m_current_eap_type == eap_type_none)
+		{
+			m_current_eap_type = m_default_eap_type;
+			used_eap_type = m_default_eap_type;
+		}
+		else if (eap.get_length() > eap_header_base_c::get_header_length())
+		{
+			if (eap.get_type() == eap_type_identity)
+			{
+				used_eap_type = m_default_eap_type;
+			}
+			else
+			{
+				used_eap_type = eap.get_type();
+
+				if (used_eap_type == eap_type_nak
+					|| eap_expanded_type_nak.get_type() == used_eap_type
+					)
+				{
+					// Server received EAP-Response from client.
+					// Now server could send EAP-Failure or EAP-success to client.
+					// See draft-ietf-eap-rfc2284bis-06.txt chapter 2. Extensible Authentication Protocol (EAP).
+
+					/**
+					 * @{ 2003-10-02 draft-ietf-eap-rfc2284bis-06.txt chapter 5.3.1 Legacy Nak:
+					 * The legacy Nak Type is valid only in Response messages.  It is
+					 * sent in reply to a Request where the desired authentication Type
+					 * is unacceptable.  Authentication Types are numbered 4 and above.
+					 * The Response contains one or more authentication Types desired by
+					 * the Peer.  Type zero (0) is used to indicate that the sender has
+					 * no viable alternatives, and therefore the authenticator SHOULD NOT
+					 * send another Request after receiving a Nak Response containing a
+					 * zero value.
+					 * Since the legacy Nak Type is valid only in Responses and has very
+					 * limited functionality, it MUST NOT be used as a general purpose
+					 * error indication, such as for communication of error messages, or
+					 * negotiation of parameters specific to a particular EAP method.
+					 * }
+					 */
+
+					/**
+					 * @{ 2003-10-02 draft-ietf-eap-rfc2284bis-06.txt chapter 5.3.2 Expanded Nak:
+					 * The Expanded Nak Type is valid only in Response messages.  It MUST
+					 * be sent only in reply to a Request of Type 254 (Expanded Type)
+					 * where the authentication Type is unacceptable.  The Expanded Nak
+					 * Type uses the Expanded Type format itself, and the Response
+					 * contains one or more authentication Types desired by the peer, all
+					 * in Expanded Type format.  Type zero (0) is used to indicate that
+					 * the sender has no viable alternatives.  The general format of the
+					 * Expanded Type is described in Section 5.7.
+					 * Since the Expanded Nak Type is valid only in Responses and has
+					 * very limited functionality, it MUST NOT be used as a general
+					 * purpose error indication, such as for communication of error
+					 * messages, or negotiation of parameters specific to a particular
+					 * EAP method.
+					 * }
+					 */
+
+					// Only server should receive this packet.
+					if (eap.get_type_data_length() >= sizeof(u8_t))
+					{
+						// EAP-Response/Nak includes list of new proposal for EAP type.
+						// Now we need to know does the proposed EAP type need separate identity.
+						// In that case we must restart the authentication.
+						// If the same identity is valid, we could continue.
+
+						u32_t proposal_length = eap.get_type_data_length();
+						for (u32_t ind = 0ul; ind < proposal_length; ind++)
+						{
+							/**
+							 * @{ 2005-04-19 complete Expanded Nak Type to packet_process(). }
+							 */
+							status = eap_expanded_type_c::read_type(
+								m_am_tools,
+								ind,
+								eap.get_type_data(eap.get_type_data_length()),
+								eap.get_type_data_length(),
+								&used_eap_type);
+							if (status != eap_status_ok)
+							{
+								EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+								return EAP_STATUS_RETURN(m_am_tools, status);
+							}
+
+							// Here we need a check that proposed EAP type is valid for us.
+							status = check_is_valid_eap_type(used_eap_type);
+							if (status == eap_status_ok)
+							{
+								// Let's use this EAP-type.
+								break;
+							}
+							else
+							{
+								used_eap_type = eap_type_none;
+							}
+						} // for()
+
+						if (used_eap_type == eap_type_none)
+						{
+							EAP_TRACE_DEBUG(
+								m_am_tools,
+								TRACE_FLAGS_DEFAULT,
+								(EAPL("TIMER: %s: %s, EAP-Reponse/Nak did not include any valid EAp-type.\n"),
+								 (m_is_client_role == true ? "client": "server"),
+								 (m_is_tunneled_eap == true) ? "tunneled": "outer most"
+								 ));
+						}
+
+						// 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 (m_process_eap_nak_immediately == true)
+						{
+							if (used_eap_type == eap_type_none)
+							{
+								// No acceptable EAP-type.
+								// Send a EAP-Failure.
+								status = send_eap_failure(
+									&send_network_id,
+									static_cast<u8_t>(eap.get_identifier()+1ul));
+
+								EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+								return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_eap_type);
+							}
+							else
+							{
+								// First remove current EAP-type.
+								eap_variable_data_c selector(m_am_tools);
+								status = selector.set_copy_of_buffer(
+									&m_current_eap_type,
+									sizeof(m_current_eap_type));
+								if (status != eap_status_ok)
+								{
+									EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+									return EAP_STATUS_RETURN(m_am_tools, status);
+								}
+								eap_base_type_c *handler = m_type_map.get_handler(&selector);
+
+								// Change the current EAP-type here because shutdown could
+								// cause state notifications from old EAP-type.
+								m_current_eap_type = used_eap_type;
+
+								if (handler != 0)
+								{
+									status = handler->shutdown();
+									if (status != eap_status_ok)
+									{
+										EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+										return EAP_STATUS_RETURN(m_am_tools, status);
+									}
+
+									status = m_type_map.remove_handler(&selector, true);
+									if (status != eap_status_ok)
+									{
+										EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+										return EAP_STATUS_RETURN(m_am_tools, status);
+									}
+								}
+
+								// Now restart authentication with proposed EAP type.
+								status = restart_with_new_type(
+									used_eap_type,
+									receive_network_id,
+									eap.get_identifier());
+
+								EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+								return EAP_STATUS_RETURN(m_am_tools, status);
+							}
+						}
+						else //if (m_process_eap_nak_immediately == false)
+						{
+							status = eap_status_illegal_eap_type;
+
+							if (m_nak_process_timer_active == false)
+							{
+								eap_core_nak_info_c * const nak_info
+									= new eap_core_nak_info_c(
+										m_am_tools,
+										receive_network_id,
+										used_eap_type,
+										eap.get_identifier());
+
+								status = m_partner->set_timer(
+									this,
+									EAP_CORE_DELAYED_EAP_NAK_PROCESS_ID, 
+									nak_info,
+									EAP_CORE_DELAYED_EAP_NAK_PROCESS_TIMEOUT);
+								if (status == eap_status_ok)
+								{
+									m_nak_process_timer_active = true;
+								}
+								else
+								{
+									// ERROR.
+									// NOTE: timer queue did call timer_delete_data() function to free nak_info.
+								}
+
+								EAP_TRACE_DEBUG(
+									m_am_tools,
+									TRACE_FLAGS_DEFAULT,
+									(EAPL("TIMER: %s: %s, EAP_CORE_DELAYED_EAP_NAK_PROCESS_ID set %d ms.\n"),
+									 (m_is_client_role == true ? "client": "server"),
+									 (m_is_tunneled_eap == true) ? "tunneled": "outer most",
+									 EAP_CORE_DELAYED_EAP_NAK_PROCESS_TIMEOUT
+									 ));
+							}
+						}
+
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return EAP_STATUS_RETURN(m_am_tools, status);						
+
+					} // if (eap.get_type_data_length() >= sizeof(u8_t))
+					else
+					{
+						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+						return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_eap_type);
+					}
+				}
+			}
+		}
+		else
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_eap_code);
+		}
+	}
+#endif //#if defined(USE_EAP_CORE_SERVER)
+	else if (eap.get_code() == eap_code_success
+		|| eap.get_code() == eap_code_failure)
+	{
+		if (m_current_eap_type != eap_type_none)
+		{
+			// Here we are again on thin ice.
+			// Use saved EAP type, this is our best quess of other peer's EAP type.
+			// Other peer just informs status of authentication.
+			used_eap_type = m_current_eap_type;
+		}
+		else
+		{
+			// No EAP-type loaded, drop message quietly.
+			EAP_TRACE_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("WARNING: EAP_Core: %s,%s, %s packet dropped quietly. m_eap_type_response_sent %d, ")
+				 EAPL("m_current_eap_type 0x%08x\n"),
+				 (m_is_client_role == true) ? "client": "server",
+				 (m_is_tunneled_eap == true) ? "tunneled": "outer most",
+				 eap.get_code_string(),
+				 m_eap_type_response_sent,
+				 convert_eap_type_to_u32_t(m_current_eap_type)));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
+		}
+	}
+	else
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_eap_code);
+	}
+
+
+	status = packet_process_type(
+		used_eap_type,
+		receive_network_id,
+		packet_data,
+		packet_length);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eap_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_header_wr_c eap(
+		m_am_tools,
+		sent_packet->get_data_offset(
+			header_offset, data_length),
+		data_length);
+
+	if (eap.get_is_valid() == false)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("packet_send: %s, %s, packet buffer corrupted.\n"),
+			 (m_is_client_role == true) ? "client": "server",
+			 (m_is_tunneled_eap == true) ? "tunneled": "outer most"
+			 ));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+	}
+
+	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);
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools, 
+		EAP_TRACE_FLAGS_MESSAGE_DATA|TRACE_TEST_VECTORS, 
+		(EAPL("EAP-packet"),
+		 eap.get_header_buffer(data_length),
+		 data_length));
+
+	trace_eap_packet("<-", &eap);
+
+	if (m_shutdown_was_called == true
+		&& m_is_client_role == true)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("WARNING: EAP_Core: %s,%s, eap_core_c::packet_send(): %s packet dropped quietly because shutdown was already called. m_eap_type_response_sent %d, ")
+			 EAPL("m_current_eap_type 0x%08x\n"),
+			 (m_is_client_role == true) ? "client": "server",
+			 (m_is_tunneled_eap == true) ? "tunneled": "outer most",
+			 eap.get_code_string(),
+			 m_eap_type_response_sent,
+			 convert_eap_type_to_u32_t(m_current_eap_type)));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
+	}
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_TEST_VECTORS, 
+		(EAPL("--------------------------------------------------------\n")));
+
+
+	cancel_retransmission();
+
+	if (sent_packet->get_do_packet_retransmission() == true)
+	{
+		// Both EAP-client and EAP-server initializes re-transmission.
+		// EAP-client will respond to re-transmitted EAP-request with the matching packet.
+		// EAP-server will re-transmit the packet when timer elapses and no response is received.
+		// Note the EAP-type could do re-transmission itself too. When EAP-type do re-transmission
+		// itself EAP-type should set flag of re-transmission in the packet to true with the
+		// set_do_packet_retransmission(true) function.
+		init_retransmission(
+			send_network_id,
+			sent_packet,
+			header_offset,
+			data_length,
+			eap.get_code(),
+			eap.get_identifier(),
+			eap.get_type());
+	}
+
+
+	eap_status_e status = m_partner->packet_send(
+		send_network_id, sent_packet, header_offset, data_length, buffer_length);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	if (m_is_client_role == true
+		&& eap.get_type() == m_current_eap_type
+		&& m_current_eap_type != eap_type_none)
+	{
+		/*
+		 * Once a peer has sent a Response of the same Type as the initial
+		 * Request, an authenticator MUST NOT send a Request of a different Type
+		 * prior to completion of the final round of a given method (with the
+		 * exception of a Notification-Request) and MUST NOT send a Request for
+		 * an additional method of any Type after completion of the initial
+		 * authentication method; a peer receiving such Requests MUST treat them
+		 * as invalid, and silently discard them. As a result, Identity Requery
+		 * is not supported.
+		 */
+		if (m_eap_type_response_sent == false)
+		{
+			// Send state change notification
+			eap_state_notification_c notification(
+				m_am_tools,
+				send_network_id,
+				m_is_client,
+				eap_state_notification_eap,
+				eap_protocol_layer_eap,
+				m_current_eap_type,
+				eap_state_none,
+				eap_state_eap_response_sent,
+				eap.get_identifier(),
+				false);
+			state_notification(&notification);
+		}
+		m_eap_type_response_sent = true;
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e eap_core_c::resend_packet(
+	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,
+	const u32_t retransmission_counter)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_UNREFERENCED_PARAMETER(retransmission_counter); // Only trace uses this.
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("<- EAP_Core: %s: %s, eap_core_c::resend_packet(), counter %d.\n"),
+		 (m_is_client_role == true) ? "client": "server",
+		 (m_is_tunneled_eap == true) ? "tunneled": "outer most",
+		 retransmission_counter
+		 ));
+
+	// We make a copy because random error test may corrupt the data.
+	eap_buf_chain_wr_c * const copy_packet = sent_packet->copy();
+
+	if (copy_packet == 0)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	EAP_ASSERT(m_eap_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);
+
+	// NOTE: send packet directly to partner object.
+	// This will skip initialization of re-transmission tfor re-transmitted packet.
+	eap_status_e status = m_partner->packet_send(
+		send_network_id, 
+		copy_packet, 
+		header_offset, 
+		data_length,
+		buffer_length
+		);
+
+	delete copy_packet;
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e eap_core_c::cancel_retransmission()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	//if (m_retransmission != 0)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("TIMER: %s: %s, EAP_CORE_TIMER_RETRANSMISSION_ID cancelled.\n"),
+			 (m_is_client_role == true ? "client": "server"),
+			 (m_is_tunneled_eap == true) ? "tunneled": "outer most"
+			 ));
+
+		if (m_is_client_role == false)
+		{
+			// Only EAP-server uses timer to re-transmits EAP-packets.
+			m_partner->cancel_timer(this, EAP_CORE_TIMER_RETRANSMISSION_ID);
+		}
+
+		if (m_retransmission != 0)
+		{
+			delete m_retransmission;
+			m_retransmission = 0;
+		}
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e eap_core_c::init_retransmission(
+	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 eap_code_value_e eap_code,
+	const u8_t eap_identifier,
+	const eap_type_value_e eap_type
+	)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	if (m_is_client_role == false)
+	{
+		if (m_retransmission_time == 0u
+			|| m_retransmission_counter == 0u)
+		{
+			// No retransmission.
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+		}
+	}
+
+	EAP_ASSERT(send_network_id->get_source() != 0);
+	EAP_ASSERT(send_network_id->get_destination() != 0);
+
+	if (m_retransmission != 0)
+	{
+		delete m_retransmission;
+		m_retransmission = 0;
+	}
+
+	m_retransmission = new eap_core_retransmission_c(
+		m_am_tools, 
+		send_network_id, 
+		sent_packet, 
+		header_offset, 
+		data_length, 
+		m_retransmission_time, 
+		m_retransmission_counter,
+		eap_code,
+		eap_identifier,
+		eap_type
+		);
+
+	if (m_is_client_role == false)
+	{
+		// Only EAP-server uses timer to re-transmits EAP-packets.
+		m_partner->cancel_timer(this, EAP_CORE_TIMER_RETRANSMISSION_ID);
+
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("TIMER: %s: %s, EAP_CORE_TIMER_RETRANSMISSION_ID cancelled.\n"),
+			 (m_is_client_role == true ? "client": "server"),
+			 (m_is_tunneled_eap == true) ? "tunneled": "outer most"
+			 ));
+	}
+
+	if (m_retransmission == 0)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	if (m_retransmission->get_is_valid() == true)
+	{
+		// Because this object do re-transmission other layers must not do re-transmission of this packet.
+		sent_packet->set_do_packet_retransmission(false);
+
+		if (m_is_client_role == false)
+		{
+			// Only EAP-server uses timer to re-transmits EAP-packets.
+			u32_t next_retransmission_time = m_retransmission->get_next_retransmission_time();
+
+			eap_status_e status = m_partner->set_timer(this, EAP_CORE_TIMER_RETRANSMISSION_ID, 0,
+				next_retransmission_time);
+			if (status != eap_status_ok)
+			{
+				delete m_retransmission;
+				m_retransmission = 0;
+			}
+
+			EAP_TRACE_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("TIMER: %s: %s, EAP_CORE_TIMER_RETRANSMISSION_ID set %d ms.\n"),
+				 (m_is_client_role == true ? "client": "server"),
+				 (m_is_tunneled_eap == true) ? "tunneled": "outer most",
+				 next_retransmission_time));
+
+			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_ok);
+		}
+	}
+	else
+	{
+		delete m_retransmission;
+		m_retransmission = 0;
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eap_core_c::set_eap_failure_timeout()
+{
+	eap_status_e status = m_partner->set_timer(
+		this,
+		EAP_CORE_FAILURE_RECEIVED_ID,
+		0,
+		m_eap_core_failure_received_timeout);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("ERROR: TIMER: %s: %s, EAP_CORE_FAILURE_RECEIVED_ID failed.\n"),
+			 (m_is_client_role == true ? "client": "server"),
+			 (m_is_tunneled_eap == true) ? "tunneled": "outer most"
+			 ));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	else
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("TIMER: %s: %s, EAP_CORE_FAILURE_RECEIVED_ID set %d ms.\n"),
+			 (m_is_client_role == true ? "client": "server"),
+			 (m_is_tunneled_eap == true) ? "tunneled": "outer most",
+			 m_eap_core_failure_received_timeout
+			 ));
+	}
+
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eap_core_c::cancel_eap_failure_timeout()
+{
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("TIMER: %s: %s, EAP_CORE_FAILURE_RECEIVED_ID cancelled.\n"),
+		 (m_is_client_role == true ? "client": "server"),
+		 (m_is_tunneled_eap == true) ? "tunneled": "outer most"
+		 ));
+
+	return m_partner->cancel_timer(
+		this,
+		EAP_CORE_FAILURE_RECEIVED_ID);
+}
+
+//--------------------------------------------------
+
+#if defined(USE_EAP_CORE_WAIT_REQUEST_TYPE_TIMER)
+
+//
+eap_status_e eap_core_c::set_wait_eap_request_type_timeout()
+{
+	EAP_ASSERT_TOOLS(m_am_tools, (m_wait_eap_request_type_timeout_set == false));
+
+	eap_status_e status = m_partner->set_timer(
+		this,
+		EAP_CORE_WAIT_EAP_REQUEST_TYPE_ID,
+		0,
+		m_wait_eap_request_type_timeout);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("ERROR: TIMER: %s: %s, EAP_CORE_WAIT_EAP_REQUEST_TYPE_ID failed.\n"),
+			 (m_is_client_role == true ? "client": "server"),
+			 (m_is_tunneled_eap == true) ? "tunneled": "outer most"
+			 ));
+
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	else
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("TIMER: %s: %s, EAP_CORE_WAIT_EAP_REQUEST_TYPE_ID set %d ms.\n"),
+			 (m_is_client_role == true ? "client": "server"),
+			 (m_is_tunneled_eap == true) ? "tunneled": "outer most",
+			 m_wait_eap_request_type_timeout
+			 ));
+
+		m_wait_eap_request_type_timeout_set = true;
+	}
+
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+#endif //#if defined(USE_EAP_CORE_WAIT_REQUEST_TYPE_TIMER)
+
+//--------------------------------------------------
+
+#if defined(USE_EAP_CORE_WAIT_REQUEST_TYPE_TIMER)
+
+//
+eap_status_e eap_core_c::cancel_wait_eap_request_type_timeout()
+{
+	if (m_wait_eap_request_type_timeout_set == true)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("TIMER: %s: %s, EAP_CORE_WAIT_EAP_REQUEST_TYPE_ID cancelled.\n"),
+			 (m_is_client_role == true ? "client": "server"),
+			 (m_is_tunneled_eap == true) ? "tunneled": "outer most"
+			 ));
+
+		m_wait_eap_request_type_timeout_set = false;
+
+		return m_partner->cancel_timer(
+			this,
+			EAP_CORE_WAIT_EAP_REQUEST_TYPE_ID);
+	}
+	else
+	{
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+	}
+}
+
+#endif //#if defined(USE_EAP_CORE_WAIT_REQUEST_TYPE_TIMER)
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT u32_t eap_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);
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("eap_core_c::get_header_offset(): offset=%d, MTU=%d, trailer_length=%d\n"),
+		offset,
+		*MTU,
+		*trailer_length));
+
+	return offset;
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eap_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 handler,
+	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);
+
+	const eap_status_e status = m_partner->load_module(
+		type,
+		tunneling_type,
+		partner,
+		handler,
+		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 eap_core_c::configure()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+#if !defined(USE_EAP_DEBUG_TRACE)
+	EAP_TRACE_ALWAYS(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eap_core_c::configure(): %s: %s.\n"),
+		 ((m_is_client == true) ? "client": "server"),
+		 (m_is_tunneled_eap == true) ? "tunneled": "outer most"));
+#else
+	EAP_TRACE_ALWAYS(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eap_core_c::configure(): %s: %s, this = 0x%08x => 0x%08x.\n"),
+		 ((m_is_client == true) ? "client": "server"),
+		 (m_is_tunneled_eap == true) ? "tunneled": "outer most",
+		 this,
+		 dynamic_cast<abs_eap_base_timer_c *>(this)));
+#endif
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: eap_core_c::configure()");
+
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	eap_status_e status(eap_status_process_general_error);
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	{
+		eap_variable_data_c data(m_am_tools);
+
+		status = m_partner->read_configure(
+			cf_str_EAP_default_type_hex_data.get_field(),
+			&data);
+		if (status == eap_status_illegal_configure_type)
+		{
+			status = m_partner->read_configure(
+				cf_str_EAP_default_type_u32_t.get_field(),
+				&data);
+		}
+
+#if defined(USE_EAP_CORE_SERVER)
+		if (m_is_client == false)
+		{
+			// This option is only applicable in server.
+
+			eap_variable_data_c server_data(m_am_tools);
+
+			eap_status_e server_status = m_partner->read_configure(
+				cf_str_EAP_server_default_type_hex_data.get_field(),
+				&server_data);
+			if (server_status == eap_status_illegal_configure_type)
+			{
+				server_status = m_partner->read_configure(
+					cf_str_EAP_server_default_type_u32_t.get_field(),
+					&server_data);
+			}
+
+			if (server_status == eap_status_ok
+				&& server_data.get_is_valid_data() == true)
+			{
+				status = data.set_copy_of_buffer(&server_data);
+			}
+		}
+#endif //#if defined(USE_EAP_CORE_SERVER)
+
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+		else if (data.get_data_length() == sizeof(u32_t)
+			&& data.get_data(data.get_data_length()) != 0)
+		{
+			m_default_eap_type
+				= *(reinterpret_cast<eap_type_ietf_values_e *>(
+						data.get_data(data.get_data_length())));
+		}
+		else if (data.get_data_length() == eap_expanded_type_c::get_eap_expanded_type_size()
+			&& data.get_data(data.get_data_length()) != 0)
+		{
+			eap_expanded_type_c eap_type(eap_type_none);
+
+			status = eap_type.set_expanded_type_data(
+				m_am_tools,
+				&data);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			status = eap_type.get_type_data(
+				m_am_tools,
+				&m_default_eap_type);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+		else
+		{
+			EAP_TRACE_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("ERROR: %s: %s, No EAP-type configured, %s.\n"),
+				 (m_is_client == true ? "client": "server"),
+				 (m_is_tunneled_eap == true) ? "tunneled": "outer most",
+				 cf_str_EAP_default_type_hex_data.get_field()->get_field()));
+			
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_configure_field);
+		}
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+#if defined(USE_EAP_TEST_VECTORS)
+
+	{
+		eap_variable_data_c data(m_am_tools);
+
+		status = m_partner->read_configure(
+			cf_str_EAP_TRACE_only_trace_messages.get_field(),
+			&data);
+		if (status == eap_status_ok
+			&& data.get_data_length() == sizeof(u32_t)
+			&& data.get_data(data.get_data_length()) != 0)
+		{
+			if (*(reinterpret_cast<u32_t *>(data.get_data(data.get_data_length()))) != 0u)
+			{
+				// Activate only EAP message traces.
+				m_am_tools->set_trace_mask(
+					eap_am_tools_c::eap_trace_mask_always
+					| eap_am_tools_c::eap_trace_mask_eap_messages);
+			}
+			else
+			{
+				// Disable only EAP message traces.
+				m_am_tools->set_trace_mask(
+					m_am_tools->get_trace_mask() & (~eap_am_tools_c::eap_trace_mask_eap_messages));
+			}
+		}
+		else
+		{
+			// Disable only EAP message traces.
+			m_am_tools->set_trace_mask(
+				m_am_tools->get_trace_mask() & (~eap_am_tools_c::eap_trace_mask_eap_messages));
+		}
+	}
+
+
+	{
+		eap_variable_data_c data(m_am_tools);
+
+		status = m_partner->read_configure(
+			cf_str_EAP_TRACE_only_test_vectors.get_field(),
+			&data);
+		if (status == eap_status_ok
+			&& data.get_data_length() == sizeof(u32_t)
+			&& data.get_data(data.get_data_length()) != 0)
+		{
+			if (*(reinterpret_cast<u32_t *>(data.get_data(data.get_data_length()))) != 0u)
+			{
+				// Activates only EAP test vector traces.
+				m_am_tools->set_trace_mask(eap_am_tools_c::eap_trace_mask_test_vectors);
+			}
+		}
+	}
+
+
+	{
+		eap_variable_data_c data(m_am_tools);
+
+		status = m_partner->read_configure(
+			cf_str_EAP_TRACE_crypto_test_vectors_sha1.get_field(),
+			&data);
+		if (status == eap_status_ok
+			&& data.get_data_length() == sizeof(u32_t)
+			&& data.get_data(data.get_data_length()) != 0)
+		{
+			if (*(reinterpret_cast<u32_t *>(data.get_data(data.get_data_length()))) != 0u)
+			{
+				// Activates SHA1 EAP test vector traces.
+				m_am_tools->set_trace_mask(m_am_tools->get_trace_mask()
+					| eap_am_tools_c::eap_trace_mask_crypto_sha1);
+			}
+		}
+	}
+
+
+	{
+		eap_variable_data_c data(m_am_tools);
+
+		status = m_partner->read_configure(
+			cf_str_EAP_TRACE_crypto_test_vectors_rc4.get_field(),
+			&data);
+		if (status == eap_status_ok
+			&& data.get_data_length() == sizeof(u32_t)
+			&& data.get_data(data.get_data_length()) != 0)
+		{
+			if (*(reinterpret_cast<u32_t *>(data.get_data(data.get_data_length()))) != 0u)
+			{
+				// Activates RC4 EAP test vector traces.
+				m_am_tools->set_trace_mask(m_am_tools->get_trace_mask()
+					| eap_am_tools_c::eap_trace_mask_crypto_rc4);
+			}
+		}
+	}
+
+
+	{
+		eap_variable_data_c data(m_am_tools);
+
+		status = m_partner->read_configure(
+			cf_str_EAP_TRACE_crypto_test_vectors_md4.get_field(),
+			&data);
+		if (status == eap_status_ok
+			&& data.get_data_length() == sizeof(u32_t)
+			&& data.get_data(data.get_data_length()) != 0)
+		{
+			if (*(reinterpret_cast<u32_t *>(data.get_data(data.get_data_length()))) != 0u)
+			{
+				// Activates MD4 EAP test vector traces.
+				m_am_tools->set_trace_mask(m_am_tools->get_trace_mask()
+					| eap_am_tools_c::eap_trace_mask_crypto_md4);
+			}
+		}
+	}
+
+
+	{
+		eap_variable_data_c data(m_am_tools);
+
+		status = m_partner->read_configure(
+			cf_str_EAP_TRACE_crypto_test_vectors_test_random.get_field(),
+			&data);
+		if (status == eap_status_ok
+			&& data.get_data_length() == sizeof(u32_t)
+			&& data.get_data(data.get_data_length()) != 0)
+		{
+			if (*(reinterpret_cast<u32_t *>(data.get_data(data.get_data_length()))) != 0u)
+			{
+				// Activates test random generator EAP test vector traces.
+				m_am_tools->set_trace_mask(m_am_tools->get_trace_mask()
+					| eap_am_tools_c::eap_trace_mask_crypto_test_random
+					| eap_am_tools_c::eap_trace_mask_crypto_sha1);
+			}
+		}
+	}
+
+#endif //#if defined(USE_EAP_TEST_VECTORS)
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+#if defined(USE_EAP_CORE_SERVER)
+	if (m_is_client == false)
+	{
+		eap_variable_data_c data(m_am_tools);
+
+		status = m_partner->read_configure(
+			cf_str_EAP_CORE_process_EAP_Nak_immediately.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_process_eap_nak_immediately = true;
+				}
+				else
+				{
+					m_process_eap_nak_immediately = false;
+				}
+			}
+		}
+	}
+#endif //#if defined(USE_EAP_CORE_SERVER)
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+#if defined(USE_EAP_CORE_SERVER)
+	if (m_is_client == false)
+	{
+		eap_variable_data_c retransmission_time(m_am_tools);
+
+		status = read_configure(
+			cf_str_EAP_CORE_retransmission_time.get_field(),
+			&retransmission_time);
+		if (status == eap_status_ok
+			&& retransmission_time.get_is_valid_data() == true)
+		{
+			u32_t *retransmission_time_value = reinterpret_cast<u32_t *>(
+				retransmission_time.get_data(sizeof(u32_t)));
+			if (retransmission_time_value != 0)
+			{
+				m_retransmission_time = *retransmission_time_value;
+			}
+			else
+			{
+				m_retransmission_time = EAP_CORE_RETRANSMISSION_TIME;
+			}
+		}
+		else
+		{
+			m_retransmission_time = EAP_CORE_RETRANSMISSION_TIME;
+		}
+	}
+#endif //#if defined(USE_EAP_CORE_SERVER)
+
+	//if (m_is_client == false)
+	{
+		eap_variable_data_c retransmission_counter(m_am_tools);
+
+		status = read_configure(
+			cf_str_EAP_CORE_retransmission_counter.get_field(),
+			&retransmission_counter);
+		if (status == eap_status_ok
+			&& retransmission_counter.get_is_valid_data() == true)
+		{
+			u32_t *retransmission_counter_value = reinterpret_cast<u32_t *>(
+				retransmission_counter.get_data(sizeof(u32_t)));
+			if (retransmission_counter_value != 0)
+			{
+				m_retransmission_counter = *retransmission_counter_value;
+			}
+			else
+			{
+				m_retransmission_counter = EAP_CORE_RETRANSMISSION_COUNTER;
+			}
+		}
+		else
+		{
+			m_retransmission_counter = EAP_CORE_RETRANSMISSION_COUNTER;
+		}
+	}
+
+	//----------------------------------------------------------
+
+	{
+		eap_variable_data_c session_timeout(m_am_tools);
+
+		status = read_configure(
+			cf_str_EAP_CORE_session_timeout.get_field(),
+			&session_timeout);
+		if (status == eap_status_ok
+			&& session_timeout.get_is_valid_data() == true)
+		{
+			u32_t *handler_timeout = reinterpret_cast<u32_t *>(
+				session_timeout.get_data(sizeof(u32_t)));
+			if (handler_timeout != 0)
+			{
+				m_session_timeout = *handler_timeout;
+			}
+			else
+			{
+				m_session_timeout = EAP_CORE_SESSION_TIMEOUT;
+			}
+		}
+		else
+		{
+			m_session_timeout = EAP_CORE_SESSION_TIMEOUT;
+		}
+	}
+
+
+#if defined(USE_EAP_CORE_SERVER)
+
+	if (m_is_client == false)
+	{
+		eap_variable_data_c session_timeout(m_am_tools);
+
+		status = read_configure(
+			cf_str_EAP_CORE_server_session_timeout.get_field(),
+			&session_timeout);
+		if (status == eap_status_ok
+			&& session_timeout.get_is_valid_data() == true)
+		{
+			u32_t *handler_timeout = reinterpret_cast<u32_t *>(
+				session_timeout.get_data(sizeof(u32_t)));
+			if (handler_timeout != 0)
+			{
+				// This is optional.
+				m_session_timeout = *handler_timeout;
+			}
+		}
+	}
+
+	if (m_is_client == false)
+	{
+		eap_variable_data_c data(m_am_tools);
+
+		status = m_partner->read_configure(
+			cf_str_EAP_CORE_send_eap_success_after_notification.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_send_eap_success_after_notification = true;
+				}
+				else
+				{
+					m_send_eap_success_after_notification = false;
+				}
+			}
+		}
+	}
+
+#endif //#if defined(USE_EAP_CORE_SERVER)
+
+	//----------------------------------------------------------
+
+	{
+		eap_variable_data_c failure_received_timeout(m_am_tools);
+
+		status = read_configure(
+			cf_str_EAP_CORE_failure_received_timeout.get_field(),
+			&failure_received_timeout);
+		if (status == eap_status_ok
+			&& failure_received_timeout.get_is_valid_data() == true)
+		{
+			u32_t *timeout = reinterpret_cast<u32_t *>(
+				failure_received_timeout.get_data(sizeof(u32_t)));
+			if (timeout != 0)
+			{
+				m_eap_core_failure_received_timeout = *timeout;
+			}
+		}
+	}
+
+	//----------------------------------------------------------
+
+	if (m_is_tunneled_eap == false)
+	{
+		eap_variable_data_c remove_session_timeout(m_am_tools);
+
+		status = read_configure(
+			cf_str_EAP_CORE_remove_session_timeout.get_field(),
+			&remove_session_timeout);
+		if (status == eap_status_ok
+			&& remove_session_timeout.get_is_valid_data() == true)
+		{
+			u32_t *remove_session_timeout_value = reinterpret_cast<u32_t *>(
+				remove_session_timeout.get_data(sizeof(u32_t)));
+			if (remove_session_timeout_value != 0)
+			{
+				m_remove_session_timeout = *remove_session_timeout_value;
+			}
+		}
+	}
+	else
+	{
+		// Inside the tunnel we do not need any timeout.
+		m_remove_session_timeout = 0ul;
+	}
+
+	//----------------------------------------------------------
+
+#if defined(USE_EAP_EXPANDED_TYPES)
+	{
+		eap_variable_data_c use_eap_expanded_type(m_am_tools);
+
+		status = m_partner->read_configure(
+			cf_str_EAP_CORE_use_eap_expanded_type.get_field(),
+			&use_eap_expanded_type);
+		if (status == eap_status_ok
+			&& use_eap_expanded_type.get_data_length() == sizeof(u32_t)
+			&& use_eap_expanded_type.get_data() != 0)
+		{
+			u32_t *flag = reinterpret_cast<u32_t *>(use_eap_expanded_type.get_data(use_eap_expanded_type.get_data_length()));
+
+			if (flag != 0)
+			{
+				if ((*flag) != 0ul)
+				{
+					m_use_eap_expanded_type = true;
+				}
+				else
+				{
+					m_use_eap_expanded_type = false;
+				}
+			}
+		}
+	}
+#endif //#if defined(USE_EAP_EXPANDED_TYPES)
+
+	//----------------------------------------------------------
+
+#if defined(USE_EAP_CORE_WAIT_REQUEST_TYPE_TIMER)
+	{
+		eap_variable_data_c wait_eap_request_type_timeout(m_am_tools);
+
+		status = read_configure(
+			cf_str_EAP_CORE_wait_eap_request_type_timeout.get_field(),
+			&wait_eap_request_type_timeout);
+		if (status == eap_status_ok
+			&& wait_eap_request_type_timeout.get_is_valid_data() == true)
+		{
+			u32_t *timeout = reinterpret_cast<u32_t *>(
+				wait_eap_request_type_timeout.get_data(sizeof(u32_t)));
+			if (timeout != 0)
+			{
+				m_wait_eap_request_type_timeout = *timeout;
+			}
+		}
+	}
+
+#if defined(USE_EAP_CORE_SERVER)
+	if (m_is_tunneled_eap == false)
+	{
+		
+		eap_variable_data_c skip_eap_request_identity(m_am_tools);
+
+		status = m_partner->read_configure(
+			cf_str_EAP_CORE_skip_eap_request_identity.get_field(),
+			&skip_eap_request_identity);
+		if (status == eap_status_ok
+			&& skip_eap_request_identity.get_data_length() == sizeof(u32_t)
+			&& skip_eap_request_identity.get_data() != 0)
+		{
+			u32_t *flag = reinterpret_cast<u32_t *>(skip_eap_request_identity.get_data(skip_eap_request_identity.get_data_length()));
+
+			if (flag != 0)
+			{
+				if ((*flag) != 0ul)
+				{
+					m_skip_eap_request_identity = true;
+				}
+				else
+				{
+					m_skip_eap_request_identity = false;
+				}
+			}
+		}
+	}
+#endif //#if defined(USE_EAP_CORE_SERVER)
+
+#endif //#if defined(USE_EAP_CORE_WAIT_REQUEST_TYPE_TIMER)
+
+	//----------------------------------------------------------
+
+	m_eap_header_offset = m_partner->get_header_offset(&m_MTU, &m_trailer_length);
+
+
+	// Add session timeout.
+	status = initialize_session_timeout(m_session_timeout);
+	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_EAP_CORE_WAIT_REQUEST_TYPE_TIMER)
+
+	if (m_is_tunneled_eap == false
+		&& m_is_client_role == true)
+	{
+		status = cancel_wait_eap_request_type_timeout();
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = set_wait_eap_request_type_timeout();
+	}
+
+#endif //#if defined(USE_EAP_CORE_WAIT_REQUEST_TYPE_TIMER)
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eap_core_c::shutdown_operation(
+	eap_base_type_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_TRACE_ALWAYS(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eap_core_c::shutdown_operation(): handler=0x%08x.\n"),
+		 handler));
+
+	eap_status_e status = handler->shutdown();
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eap_core_c::shutdown()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+	
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+#if !defined(USE_EAP_DEBUG_TRACE)
+	EAP_TRACE_ALWAYS(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eap_core_c::shutdown(): %s: %s, m_shutdown_was_called=%d.\n"),
+		 ((m_is_client == true) ? "client": "server"),
+		 (m_is_tunneled_eap == true) ? "tunneled": "outer most",
+		 m_shutdown_was_called));
+#else
+	EAP_TRACE_ALWAYS(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eap_core_c::shutdown(): %s: %s, this = 0x%08x => 0x%08x, ")
+		 EAPL("m_shutdown_was_called=%d.\n"),
+		 ((m_is_client == true) ? "client": "server"),
+		 (m_is_tunneled_eap == true) ? "tunneled": "outer most",
+		 this,
+		 dynamic_cast<abs_eap_base_timer_c *>(this),
+		 m_shutdown_was_called));
+#endif
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: eap_core_c::shutdown()");
+
+	if (m_shutdown_was_called == true)
+	{
+		// Shutdown was already called once.
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+	}
+	m_shutdown_was_called = true;
+
+	if (m_partner == 0)
+	{
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_general_error);
+	}
+
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("eap_core_c::shutdown(): m_is_tunneled_eap=%d, m_eap_type_response_sent=%d\n"),
+		m_is_tunneled_eap,
+		m_eap_type_response_sent));
+
+	if (m_is_client == true
+		&& m_is_tunneled_eap == false
+		&& m_eap_type_response_sent == false)
+	{
+		// EAP-authentication failed before any EAP-messages.
+
+		EAP_TRACE_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("eap_core_c::shutdown(): EAP-authentication failed before any EAP-messages.\n")));
+
+		eap_am_network_id_c send_network_id(m_am_tools,
+			m_receive_network_id.get_destination_id(),
+			m_receive_network_id.get_source_id(),
+			m_receive_network_id.get_type());
+
+		eap_state_notification_c notification(
+			m_am_tools,
+			&send_network_id,
+			m_is_client,
+			eap_state_notification_eap,
+			eap_protocol_layer_eap,
+			m_current_eap_type,
+			eap_state_none,
+			eap_state_authentication_terminated_unsuccessfully,
+			m_eap_identity_request_identifier_client,
+			false);
+
+		notification.set_authentication_error(eap_status_authentication_failure);
+
+		state_notification(&notification);
+	}
+
+	eap_status_e status = m_type_map.for_each(shutdown_operation, true);
+
+
+	cancel_retransmission();
+
+	cancel_session_timeout();
+
+	cancel_eap_failure_timeout();
+
+	cancel_asynchronous_init_remove_eap_session();
+
+#if defined(USE_EAP_CORE_WAIT_REQUEST_TYPE_TIMER)
+	if (m_is_tunneled_eap == false
+		&& m_is_client_role == true)
+	{
+		cancel_wait_eap_request_type_timeout();
+	}
+#endif //#if defined(USE_EAP_CORE_WAIT_REQUEST_TYPE_TIMER)
+
+
+	if (m_partner != 0)
+	{
+
+#if defined(USE_EAP_CORE_SERVER)
+		m_partner->cancel_timer(this, EAP_CORE_DELAYED_EAP_NAK_PROCESS_ID);
+
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("TIMER: %s: %s, EAP_CORE_DELAYED_EAP_NAK_PROCESS_ID ")
+			 EAPL("cancelled, this = 0x%08x.\n"),
+			 (m_is_client == true ? "client": "server"),
+			 (m_is_tunneled_eap == true) ? "tunneled": "outer most",
+			 this));
+#endif //#if defined(USE_EAP_CORE_SERVER)
+
+		m_partner->cancel_timer(this, EAP_CORE_SESSION_TIMEOUT_ID);
+
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("TIMER: %s: %s, EAP_CORE_SESSION_TIMEOUT_ID cancelled, this = 0x%08x.\n"),
+			 (m_is_client == true ? "client": "server"),
+			 (m_is_tunneled_eap == true) ? "tunneled": "outer most",
+			 this));
+	}
+
+#if !defined(USE_EAP_DEBUG_TRACE)
+	EAP_TRACE_ALWAYS(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eap_core_c::shutdown(): %s: %s, m_shutdown_was_called=%d, status=%d returns.\n"),
+		 ((m_is_client == true) ? "client": "server"),
+		 (m_is_tunneled_eap == true) ? "tunneled": "outer most",
+		 m_shutdown_was_called,
+		 status));
+#else
+	EAP_TRACE_ALWAYS(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eap_core_c::shutdown(): %s: %s, this = 0x%08x => 0x%08x, ")
+		 EAPL("m_shutdown_was_called=%d, status=%d returns.\n"),
+		 ((m_is_client == true) ? "client": "server"),
+		 (m_is_tunneled_eap == true) ? "tunneled": "outer most",
+		 this,
+		 dynamic_cast<abs_eap_base_timer_c *>(this),
+		 m_shutdown_was_called,
+		 status));
+#endif
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eap_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 eap_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);
+
+	eap_status_e status(eap_status_process_general_error);
+
+	/**
+	 * @{ 2003-10-01 draft-ietf-eap-rfc2284bis-06.txt chapter 3.4 Lower layer indications:
+	 * To improve reliability, if a peer receives a lower layer success
+	 * indication as defined in Section 7.2, it MAY conclude that a Success
+	 * packet has been lost, and behave as if it had actually received a
+	 * Success packet.  This includes choosing to ignore the Success in some
+	 * circumstances as described in Section 4.2.
+	 * Add call to current EAP-type. Maybe the EAP-Success packet could
+	 * be created here and send to EAP-type.
+	 * }
+	 */
+
+	if (m_current_eap_type != eap_type_none)
+	{
+		// Here we query the current EAP-type.
+		eap_variable_data_c selector(m_am_tools);
+		u64_t selector_eap_type = convert_eap_type_to_u64_t(m_current_eap_type);
+		status = selector.set_buffer(&selector_eap_type, sizeof(selector_eap_type), false, false);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+		eap_base_type_c *handler = m_type_map.get_handler(&selector);
+		
+		// Check if we already have this type loaded.
+		if (handler != 0)
+		{
+			status = handler->eap_acknowledge(receive_network_id);
+
+			if (status == eap_status_not_supported)
+			{
+				// This is too noisy.
+				/**
+				 * @{ 2004-09-02 Fix all eap_acknowledge() functions. }
+				 */
+				status = eap_status_ok;
+			}
+		}
+		else
+		{
+			// Here we do not care of missing handler.
+			// Acknowledge is meaningfull only for existing handler.
+			status = eap_status_ok;
+		}
+	}
+	else
+	{
+		// Here we do not care of missing handler.
+		// Acknowledge is meaningfull only for existing handler.
+		status = eap_status_ok;
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eap_core_c::restart_authentication(
+	const eap_am_network_id_c * const send_network_id,
+	const bool is_client_when_true)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	// Here we swap the addresses.
+	eap_am_network_id_c receive_network_id(m_am_tools,
+		send_network_id->get_destination_id(),
+		send_network_id->get_source_id(),
+		send_network_id->get_type());
+
+	eap_status_e status = eap_status_process_general_error;
+
+	initialize_session_timeout(m_session_timeout);
+
+	if (is_client_when_true == false)
+	{
+		// This is much faster.
+		status = m_partner->restart_authentication(
+			&receive_network_id,
+			is_client_when_true,
+			true);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		m_client_restart_authentication_initiated = true;
+	}
+	else
+	{
+		if (m_client_restart_authentication_initiated == true)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+		}
+
+		// This is much faster.
+		status = m_partner->restart_authentication(
+			&receive_network_id,
+			is_client_when_true,
+			true);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		m_client_restart_authentication_initiated = true;
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+#if defined(USE_EAP_CORE_SERVER)
+
+//
+EAP_FUNC_EXPORT eap_status_e eap_core_c::send_eap_identity_request(
+	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_ASSERT(m_is_client == false);
+
+	eap_status_e status = eap_status_process_general_error;
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eap_core_c::send_eap_identity_request(): %s, %s\n"),
+		 (m_is_client == true) ? "client": "server",
+		 (m_is_tunneled_eap == true) ? "tunneled": "outer most"
+		 ));
+
+	EAP_TRACE_RETURN_STRING(m_am_tools, "returns: eap_core_c::send_eap_identity_request()");
+
+	if (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);
+	}
+
+	// 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)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+#if defined(USE_EAP_CORE_WAIT_REQUEST_TYPE_TIMER)
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eap_core_c::send_eap_identity_request(): %s, %s, m_skip_eap_request_identity=%d, m_eap_identity_request_send=%d, m_eap_identity_response_accepted=%d\n"),
+		 (m_is_client == true) ? "client": "server",
+		 (m_is_tunneled_eap == true) ? "tunneled": "outer most",
+		 m_skip_eap_request_identity,
+		 m_eap_identity_request_send,
+		 m_eap_identity_response_accepted
+		 ));
+
+	if (m_skip_eap_request_identity == true)
+	{
+		if (m_eap_identity_request_send == true)
+		{
+			// Do nothing, this have been done already.
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+		}
+
+		if (m_eap_identity.get_is_valid_data() == false)
+		{
+			// No saved EAP-Identity. Set an empty EAP-Identity.
+			status = m_eap_identity.init(0ul);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+
+		// We will skip EAP-Request/Identity and EAP-Response/Identity for testing purposes.
+		// Now restart authentication with proposed EAP type.
+		status = restart_with_new_type(
+			m_default_eap_type,
+			receive_network_id,
+			0ul);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		m_eap_identity_request_send = true;
+		m_eap_identity_response_accepted = true;
+	}
+	else
+#endif //#if defined(USE_EAP_CORE_WAIT_REQUEST_TYPE_TIMER)
+	{
+		// Creates a identity request message.
+		eap_buf_chain_wr_c request_packet(
+			eap_write_buffer, 
+			m_am_tools, 
+			EAP_CORE_PACKET_BUFFER_LENGTH);
+
+		if (request_packet.get_is_valid() == false)
+		{
+			EAP_TRACE_ERROR(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("send_eap_identity_request(): %s, %s, packet buffer corrupted.\n"),
+				 (m_is_client == true) ? "client": "server",
+				 (m_is_tunneled_eap == true) ? "tunneled": "outer most"
+				 ));
+			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_CORE_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_eap_header_offset);
+		eap_header_wr_c eap_request(
+			m_am_tools,
+			request_packet.get_data_offset(
+				m_eap_header_offset,
+				(buffer_size-m_eap_header_offset)),
+			(buffer_size-m_eap_header_offset));
+
+		if (eap_request.get_is_valid() == false)
+		{
+			EAP_TRACE_ERROR(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("send_eap_identity_request(): %s, %s, packet buffer corrupted.\n"),
+				 (m_is_client == true) ? "client": "server",
+				 (m_is_tunneled_eap == true) ? "tunneled": "outer most"
+				 ));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+		}
+
+		eap_request.set_length(
+			static_cast<u16_t>((EAP_CORE_PACKET_BUFFER_LENGTH-m_eap_header_offset)),
+			m_use_eap_expanded_type);
+		eap_request.set_code(eap_code_request);
+		eap_request.set_identifier(0);
+		eap_request.set_type_data_length(0ul, m_use_eap_expanded_type);
+		eap_request.set_type(eap_type_identity, m_use_eap_expanded_type);
+
+		request_packet.set_data_length(m_eap_header_offset+eap_request.get_length());
+		request_packet.set_do_packet_retransmission(true);
+
+		EAP_ASSERT(m_eap_header_offset < request_packet.get_data_length());
+		EAP_ASSERT(eap_request.get_length() <= request_packet.get_data_length());
+		EAP_ASSERT(request_packet.get_data_length() <= EAP_CORE_PACKET_BUFFER_LENGTH);
+
+		status = packet_send(
+			&send_network_id, &request_packet, m_eap_header_offset,
+			eap_request.get_length(),
+			EAP_CORE_PACKET_BUFFER_LENGTH);
+
+		if (status == eap_status_ok)
+		{
+			eap_state_notification_c notification(
+				m_am_tools,
+				&send_network_id,
+				m_is_client,
+				eap_state_notification_eap,
+				eap_protocol_layer_eap,
+				m_current_eap_type,
+				eap_state_none,
+				eap_state_identity_request_sent,
+				eap_request.get_identifier(),
+				false);
+			state_notification(&notification);
+		}
+
+		m_eap_identity_request_send = true;
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+#endif //#if defined(USE_EAP_CORE_SERVER)
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eap_core_c::send_eap_nak_response(
+	const eap_am_network_id_c * const receive_network_id,
+	const u8_t eap_identifier,
+	const eap_array_c<eap_type_value_e> * const eap_type_list)
+{
+	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;
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eap_core_c::send_eap_nak_response(): %s, %s\n"),
+		 (m_is_client == true) ? "client": "server",
+		 (m_is_tunneled_eap == true) ? "tunneled": "outer most"
+		 ));
+
+	// 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());
+
+	// Creates a identity request message.
+	eap_buf_chain_wr_c nak_packet(
+		eap_write_buffer, 
+		m_am_tools, 
+		EAP_CORE_PACKET_BUFFER_LENGTH);
+
+	if (nak_packet.get_is_valid() == false)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("eap_core_c::send_eap_nak_response(): %s, %s, packet buffer corrupted.\n"),
+			 (m_is_client == true) ? "client": "server",
+			 (m_is_tunneled_eap == true) ? "tunneled": "outer most"
+			 ));
+		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_CORE_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_eap_header_offset);
+	eap_header_wr_c eap_nak_hdr(
+		m_am_tools,
+		nak_packet.get_data_offset(
+			m_eap_header_offset,
+			(buffer_size-m_eap_header_offset)),
+		(buffer_size-m_eap_header_offset)
+		);
+
+	if (eap_nak_hdr.get_is_valid() == false)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("eap_core_c::send_eap_nak_response(): %s, %s, packet buffer corrupted.\n"),
+			 (m_is_client == true) ? "client": "server",
+			 (m_is_tunneled_eap == true) ? "tunneled": "outer most"
+			 ));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+	}
+
+	bool write_expanded_type(false);
+	u32_t ind = 0ul;
+	u32_t count_of_eap_types = eap_type_list->get_object_count();
+
+	for (ind = 0ul; ind < count_of_eap_types; ind++)
+	{
+		const eap_type_value_e * const type = eap_type_list->get_object(ind);
+		if (type != 0
+			&& eap_expanded_type_c::is_ietf_type(*type) == false)
+		{
+			write_expanded_type = true;
+			break;
+		}
+	}
+
+	eap_nak_hdr.set_length(
+		static_cast<u16_t>((EAP_CORE_PACKET_BUFFER_LENGTH-m_eap_header_offset)),
+		write_expanded_type);
+	eap_nak_hdr.set_code(eap_code_response);
+	eap_nak_hdr.set_identifier(eap_identifier);
+	eap_nak_hdr.set_type_data_length(eap_nak_hdr.get_length(), write_expanded_type);
+	eap_nak_hdr.set_type(eap_type_nak, write_expanded_type);
+
+	u32_t required_data_length = count_of_eap_types
+		*eap_expanded_type_c::get_eap_expanded_type_size();
+
+	if (eap_nak_hdr.get_length() < required_data_length)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	u32_t type_length = eap_expanded_type_c::m_ietf_type_size;
+	if (write_expanded_type == true)
+	{
+		type_length = eap_expanded_type_c::m_eap_expanded_type_size;
+	}
+
+	u8_t * const type_data = eap_nak_hdr.get_data_offset(type_length, required_data_length);
+
+	if (type_data == 0)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("eap_core_c::send_eap_nak_response(): %s, %s, packet buffer corrupted.\n"),
+			 (m_is_client == true) ? "client": "server",
+			 (m_is_tunneled_eap == true) ? "tunneled": "outer most"
+			 ));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+
+	u8_t * const data = type_data;
+
+	for (ind = 0ul; ind < count_of_eap_types; ind++)
+	{
+		const eap_type_value_e * const type = eap_type_list->get_object(ind);
+
+		if (type != 0)
+		{
+			EAP_TRACE_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("%s: eap_core_c::send_eap_nak_response(): allowed EAP-type %d.\n"),
+				 (m_is_client == true ? "client": "server"),
+				 convert_eap_type_to_u32_t(*type)));
+
+			status = eap_expanded_type_c::write_type(
+				m_am_tools,
+				ind,
+				data,
+				eap_nak_hdr.get_type_data_length()
+				-eap_expanded_type_c::get_eap_expanded_type_size()*ind,
+				write_expanded_type,
+				*type);
+		}
+		else
+		{
+			EAP_TRACE_ERROR(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("eap_core_c::send_eap_nak_response(): %s, %s, ")
+				 EAPL("No EAP-type supported.\n"),
+				 (m_is_client == true) ? "client": "server",
+				 (m_is_tunneled_eap == true) ? "tunneled": "outer most"));
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_not_supported);
+		}
+	} // for()
+
+	eap_nak_hdr.set_type_data_length(
+		static_cast<u16_t>(count_of_eap_types*type_length),
+		write_expanded_type);
+	nak_packet.set_data_length(m_eap_header_offset+eap_nak_hdr.get_length());
+
+	EAP_ASSERT(m_eap_header_offset < nak_packet.get_data_length());
+	EAP_ASSERT(eap_nak_hdr.get_length() <= nak_packet.get_data_length());
+	EAP_ASSERT(nak_packet.get_data_length() <= EAP_CORE_PACKET_BUFFER_LENGTH);
+
+	status = packet_send(
+		&send_network_id, &nak_packet, m_eap_header_offset,
+		eap_nak_hdr.get_length(),
+		EAP_CORE_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 eap_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_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	const eap_status_e status = m_partner->packet_data_crypto_keys(
+		send_network_id,
+		master_session_key);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e eap_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 eap_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 eap_status_e eap_core_c::timer_expired(
+	const u32_t id, void *data)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_UNREFERENCED_PARAMETER(data); // Only trace uses this.
+	
+	EAP_TRACE_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("TIMER: [0x%08x]->eap_core_c::timer_expired(id 0x%02x, data 0x%08x), %s, %s.\n"),
+		 this,
+		 id,
+		 data,
+		 (m_is_client == true) ? "client": "server",
+		 (m_is_tunneled_eap == true) ? "tunneled": "outer most"));
+
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	eap_status_e status = eap_status_process_general_error;
+
+	if (id == EAP_CORE_FAILURE_RECEIVED_ID)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("TIMER: %s: %s, EAP_CORE_FAILURE_RECEIVED_ID elapsed.\n"),
+			 (m_is_client == true ? "client": "server"),
+			 (m_is_tunneled_eap == true) ? "tunneled": "outer most"
+			 ));
+
+		{
+			eap_am_network_id_c send_network_id(m_am_tools,
+				m_receive_network_id.get_destination_id(),
+				m_receive_network_id.get_source_id(),
+				m_receive_network_id.get_type());
+
+			eap_state_notification_c notification(
+				m_am_tools,
+				&send_network_id,
+				m_is_client,
+				eap_state_notification_eap,
+				eap_protocol_layer_eap,
+				m_current_eap_type,
+				eap_state_none,
+				eap_state_authentication_terminated_unsuccessfully,
+				m_eap_identity_request_identifier_client,
+				false);
+
+			notification.set_authentication_error(eap_status_authentication_failure);
+
+			state_notification(&notification);
+		}
+
+		status = eap_status_ok;
+	}
+	else if (id == EAP_CORE_TIMER_RETRANSMISSION_ID)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("TIMER: %s: %s, EAP_CORE_TIMER_RETRANSMISSION_ID elapsed.\n"),
+			 (m_is_client == true ? "client": "server"),
+			 (m_is_tunneled_eap == true) ? "tunneled": "outer most"
+			 ));
+
+		if (m_retransmission != 0
+			&& m_retransmission->get_is_valid() == true
+			&& m_retransmission->get_retransmission_counter() > 0)
+		{
+			EAP_TRACE_DEBUG(
+				m_am_tools,
+				TRACE_FLAGS_DEFAULT,
+				(EAPL("TIMER: %s, %s, new retransmission, m_retransmission->get_is_valid()=%d, ")
+				 EAPL("m_retransmission->get_retransmission_counter()=%d.\n"),
+				 (m_is_client == true) ? "client": "server",
+				 (m_is_tunneled_eap == true) ? "tunneled": "outer most",
+				 m_retransmission->get_is_valid(),
+				 m_retransmission->get_retransmission_counter()));
+
+			// This packet send is initialized by timer event.
+
+			status = resend_packet(
+				m_retransmission->get_send_network_id(),
+				m_retransmission->get_sent_packet(),
+				m_retransmission->get_header_offset(),
+				m_retransmission->get_data_length(),
+				m_retransmission->get_buffer_size(),
+				m_retransmission->get_retransmission_counter()
+				);
+
+			if (status == eap_status_ok)
+			{
+				if (m_retransmission->get_retransmission_counter() > 0u)
+				{
+					// OK, initialize the next time to retransmit.
+					u32_t next_retransmission_time
+						= m_retransmission->get_next_retransmission_time();
+
+					status = m_partner->set_timer(
+						this,
+						EAP_CORE_TIMER_RETRANSMISSION_ID,
+						0,
+						next_retransmission_time);
+					if (status != eap_status_ok)
+					{
+						delete m_retransmission;
+						m_retransmission = 0;
+					}
+					else
+					{
+						m_retransmission->get_next_retransmission_counter(); // This decrements the counter.
+
+						EAP_TRACE_DEBUG(
+							m_am_tools, 
+							TRACE_FLAGS_DEFAULT, 
+							(EAPL("TIMER: %s: %s, EAP_CORE_TIMER_RETRANSMISSION_ID ")
+							 EAPL("set %d ms, retransmission_counter %d.\n"),
+							 (m_is_client == true ? "client": "server"),
+							 (m_is_tunneled_eap == true) ? "tunneled": "outer most",
+							 next_retransmission_time,
+							 m_retransmission->get_retransmission_counter()));
+					}
+				}
+
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+			else
+			{
+				delete m_retransmission;
+				m_retransmission = 0;
+
+				status = eap_status_ok;
+
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+		else
+		{
+			EAP_TRACE_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("TIMER: %s, %s, no retransmission, m_retransmission=0x%08x.\n"),
+				 (m_is_client == true) ? "client": "server",
+				 (m_is_tunneled_eap == true) ? "tunneled": "outer most",
+				 m_retransmission));
+			if (m_retransmission != 0)
+			{
+				EAP_TRACE_DEBUG(
+					m_am_tools, 
+					TRACE_FLAGS_DEFAULT, 
+					(EAPL("TIMER: %s, %s, no retransmission, m_retransmission->get_is_valid()=%d, ")
+					 EAPL("m_retransmission->get_retransmission_counter()=%d.\n"),
+					 (m_is_client == true) ? "client": "server",
+					 (m_is_tunneled_eap == true) ? "tunneled": "outer most",
+					 m_retransmission->get_is_valid(),
+					 m_retransmission->get_retransmission_counter()));
+			}
+
+			// No good EAP-Response received to EAP-Requests.
+			// Terminate the session.
+
+			{
+				eap_am_network_id_c send_network_id(m_am_tools,
+					m_receive_network_id.get_destination_id(),
+					m_receive_network_id.get_source_id(),
+					m_receive_network_id.get_type());
+
+				eap_state_notification_c notification(
+					m_am_tools,
+					&send_network_id,
+					m_is_client,
+					eap_state_notification_eap,
+					eap_protocol_layer_eap,
+					m_current_eap_type,
+					eap_state_none,
+					eap_state_authentication_terminated_unsuccessfully,
+					m_eap_identity_request_identifier_client,
+					false);
+
+				notification.set_authentication_error(eap_status_authentication_failure);
+
+				state_notification(&notification);
+			}
+
+			status = eap_status_ok;
+		}
+	}
+#if defined(USE_EAP_CORE_SERVER)
+	else if (id == EAP_CORE_DELAYED_EAP_NAK_PROCESS_ID)
+	{
+		// Now restart authentication with proposed EAP type.
+		const eap_core_nak_info_c * const nak_info
+			= reinterpret_cast<const eap_core_nak_info_c *>(data);
+		
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("TIMER: %s: %s, EAP_CORE_DELAYED_EAP_NAK_PROCESS_ID elapsed.\n"),
+			 (m_is_client == true ? "client": "server"),
+			 (m_is_tunneled_eap == true) ? "tunneled": "outer most"
+			 ));
+
+		eap_type_value_e used_eap_type = nak_info->get_proposed_eap_type();
+
+		m_nak_process_timer_active = false;
+
+		{
+			// First remove current EAP-type.
+			eap_variable_data_c selector(m_am_tools);
+			status = selector.set_copy_of_buffer(
+				&m_current_eap_type,
+				sizeof(m_current_eap_type));
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+			eap_base_type_c *handler = m_type_map.get_handler(&selector);
+
+			// Change the current EAP-type here because shutdown could
+			// cause state notifications from old EAP-type.
+			m_current_eap_type = used_eap_type;
+
+			if (handler != 0)
+			{
+				status = handler->shutdown();
+				if (status != eap_status_ok)
+				{
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, status);
+				}
+
+				status = m_type_map.remove_handler(&selector, true);
+				if (status != eap_status_ok)
+				{
+					EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+					return EAP_STATUS_RETURN(m_am_tools, status);
+				}
+			}
+
+			// Now restart authentication with proposed EAP type.
+			status = restart_with_new_type(
+				used_eap_type,
+				nak_info->get_network_id(),
+				nak_info->get_eap_identifier());
+
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+#endif //#if defined(USE_EAP_CORE_SERVER)
+	else if (id == EAP_CORE_SESSION_TIMEOUT_ID
+#if defined(USE_EAP_CORE_WAIT_REQUEST_TYPE_TIMER)
+				|| id == EAP_CORE_WAIT_EAP_REQUEST_TYPE_ID
+#endif //#if defined(USE_EAP_CORE_WAIT_REQUEST_TYPE_TIMER)
+		)
+	{
+#if defined(USE_EAP_CORE_WAIT_REQUEST_TYPE_TIMER)
+		if (id == EAP_CORE_WAIT_EAP_REQUEST_TYPE_ID)
+		{
+			EAP_TRACE_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("TIMER: %s: %s, EAP_CORE_WAIT_EAP_REQUEST_TYPE_ID elapsed.\n"),
+				 (m_is_client == true ? "client": "server"),
+				 (m_is_tunneled_eap == true) ? "tunneled": "outer most"
+				 ));
+		}
+		else
+#endif //#if defined(USE_EAP_CORE_WAIT_REQUEST_TYPE_TIMER)
+		{
+			EAP_TRACE_DEBUG(
+				m_am_tools, 
+				TRACE_FLAGS_DEFAULT, 
+				(EAPL("TIMER: %s: %s, EAP_CORE_SESSION_TIMEOUT_ID elapsed.\n"),
+				 (m_is_client == true ? "client": "server"),
+				 (m_is_tunneled_eap == true) ? "tunneled": "outer most"
+				 ));
+		}
+
+		// we will remove this session immediately.
+		status = initialize_asynchronous_init_remove_eap_session(0ul);
+
+		eap_am_network_id_c send_network_id(m_am_tools,
+				m_receive_network_id.get_destination_id(),
+				m_receive_network_id.get_source_id(),
+				m_receive_network_id.get_type());
+
+		eap_state_notification_c notification(
+			m_am_tools,
+			&send_network_id,
+			m_is_client,
+			eap_state_notification_eap,
+			eap_protocol_layer_eap,
+			m_current_eap_type,
+			eap_state_none,
+			eap_state_authentication_terminated_unsuccessfully,
+			m_eap_identity_request_identifier_client,
+			false);
+
+		notification.set_authentication_error(eap_status_authentication_failure);
+
+		state_notification(&notification);
+
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	else if (id == EAP_CORE_REMOVE_SESSION_TIMEOUT_ID)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("TIMER: %s: %s, EAP_CORE_REMOVE_SESSION_TIMEOUT_ID elapsed.\n"),
+			 (m_is_client == true ? "client": "server"),
+			 (m_is_tunneled_eap == true) ? "tunneled": "outer most"
+			 ));
+
+		status = asynchronous_init_remove_eap_session();
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eap_core_c::timer_delete_data(
+	const u32_t id, void *data)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	EAP_UNREFERENCED_PARAMETER(data); // Only trace uses this.
+
+	EAP_TRACE_DEBUG(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("TIMER: [0x%08x]->eap_core_c::timer_delete_data(id 0x%02x, data 0x%08x): %s, %s.\n"),
+		 this,
+		 id,
+		 data,
+		 (m_is_client == true) ? "client": "server",
+		 (m_is_tunneled_eap == true) ? "tunneled": "outer most"
+		 ));
+
+	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);
+
+	if (id == EAP_CORE_TIMER_RETRANSMISSION_ID)
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("TIMER: %s: %s, EAP_CORE_TIMER_RETRANSMISSION_ID delete data.\n"),
+			 (m_is_client == true ? "client": "server"),
+			 (m_is_tunneled_eap == true) ? "tunneled": "outer most"
+			 ));
+
+		if (m_retransmission != 0
+			&& m_retransmission->get_is_valid() == true
+			&& m_retransmission->get_retransmission_counter() > 0)
+		{
+			// Do not delete yet.
+			// cancel_retransmission() will delete m_retransmission.
+		}
+		else if (m_retransmission != 0)
+		{
+			delete m_retransmission;
+			m_retransmission = 0;
+		}
+	}
+#if defined(USE_EAP_CORE_SERVER)
+	else if (id == EAP_CORE_DELAYED_EAP_NAK_PROCESS_ID)
+	{
+		const eap_core_nak_info_c * const nak_info
+			= reinterpret_cast<const eap_core_nak_info_c *>(data);
+		delete nak_info;
+	}
+#endif //#if defined(USE_EAP_CORE_SERVER)
+	else if (id == EAP_CORE_REMOVE_SESSION_TIMEOUT_ID)
+	{
+		// Nothing to do.
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eap_core_c::reset_operation(
+	eap_base_type_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_TRACE_ALWAYS(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eap_core_c::reset_operation(): handler=0x%08x.\n"),
+		 handler));
+
+	eap_status_e status = handler->reset();
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eap_core_c::reset()
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+#if !defined(USE_EAP_DEBUG_TRACE)
+	EAP_TRACE_ALWAYS(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eap_core_c::reset(): %s: %s.\n"),
+		 ((m_is_client == true) ? "client": "server"),
+		 (m_is_tunneled_eap == true) ? "tunneled": "outer most"));
+#else
+	EAP_TRACE_ALWAYS(
+		m_am_tools, 
+		TRACE_FLAGS_DEFAULT, 
+		(EAPL("eap_core_c::reset(): %s: %s, this = 0x%08x => 0x%08x.\n"),
+		 ((m_is_client == true) ? "client": "server"),
+		 (m_is_tunneled_eap == true) ? "tunneled": "outer most",
+		 this,
+		 dynamic_cast<abs_eap_base_timer_c *>(this)));
+#endif
+
+	eap_status_e status = eap_status_ok;
+
+	eap_variable_data_c selector(m_am_tools);
+	u64_t tmp_eap_type = convert_eap_type_to_u64_t(m_current_eap_type);
+	status = selector.set_buffer(&tmp_eap_type, sizeof(tmp_eap_type), false, false);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	cancel_retransmission();
+
+	cancel_session_timeout();
+
+	cancel_eap_failure_timeout();
+
+	cancel_asynchronous_init_remove_eap_session();
+
+#if defined(USE_EAP_CORE_WAIT_REQUEST_TYPE_TIMER)
+
+	if (m_is_tunneled_eap == false
+		&& m_is_client_role == true)
+	{
+		status = cancel_wait_eap_request_type_timeout();
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = set_wait_eap_request_type_timeout();
+		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_EAP_CORE_WAIT_REQUEST_TYPE_TIMER)
+
+	m_eap_identity_response_accepted = false;
+	m_eap_type_response_sent = false;
+
+#if defined(USE_EAP_CORE_SERVER)
+	m_eap_identity_request_send = false;
+	m_eap_identity_response_received = false;
+	m_eap_failure_sent = false;
+#endif //#if defined(USE_EAP_CORE_SERVER)
+
+	m_ignore_eap_failure = false;
+
+	status = m_type_map.for_each(reset_operation, true);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	m_current_eap_type = eap_type_none;
+	m_eap_identity.reset();
+
+	// Add session timeout.
+	initialize_session_timeout(m_session_timeout);
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+// 
+EAP_FUNC_EXPORT eap_status_e eap_core_c::handle_eap_identity_request(
+	const eap_type_value_e used_eap_type,
+	const u8_t eap_identifier,
+	const eap_am_network_id_c * const receive_network_id)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	eap_status_e status = eap_status_process_general_error;
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+#if defined(USE_EAP_CORE_WAIT_REQUEST_TYPE_TIMER)
+
+	if (m_is_tunneled_eap == false
+		&& m_is_client_role == true)
+	{
+		status = cancel_wait_eap_request_type_timeout();
+		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_EAP_CORE_WAIT_REQUEST_TYPE_TIMER)
+
+	// Here we query the desired EAP-type.
+	eap_variable_data_c selector(m_am_tools);
+	u64_t tmp_used_eap_type = convert_eap_type_to_u64_t(used_eap_type);
+	status = selector.set_buffer(&tmp_used_eap_type, sizeof(tmp_used_eap_type), false, false);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	eap_base_type_c *handler = m_type_map.get_handler(&selector);
+
+	if (handler == 0
+		|| handler->get_is_valid() == false)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_type_does_not_exists_error);
+	}
+
+	m_client_restart_authentication_initiated = false;
+
+	// 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());
+
+
+	// Send state change notification
+	eap_state_notification_c notification(
+		m_am_tools,
+		&send_network_id,
+		m_is_client,
+		eap_state_notification_eap,
+		eap_protocol_layer_eap,
+		m_current_eap_type,
+		eap_state_none,
+		eap_state_identity_request_received,
+		eap_identifier,
+		false);
+	state_notification(&notification);
+
+	// Save EAP-identifier
+	m_eap_identity_request_identifier_client = eap_identifier;
+
+	status = handler->query_eap_identity(
+		false,
+		&m_eap_identity,
+		receive_network_id,
+		eap_identifier);
+	if (status == eap_status_drop_packet_quietly)
+	{
+		// This packet was dropped.
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	else if (status == eap_status_pending_request)
+	{
+		// This is pending query, that will be completed by complete_eap_identity_query() call.
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	else if (status == eap_status_completed_request)
+	{
+		// This is already completed by complete_eap_identity_query() call.
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+	}
+	else if (status != eap_status_ok)
+	{
+		// Send failure notification
+		eap_state_notification_c notification(
+			m_am_tools,
+			&send_network_id,
+			m_is_client,
+			eap_state_notification_eap,
+			eap_protocol_layer_eap,
+			m_current_eap_type,
+			eap_state_none,
+			eap_state_authentication_terminated_unsuccessfully,
+			eap_identifier,
+			false);
+
+		notification.set_authentication_error(eap_status_authentication_failure);
+
+		state_notification(&notification);
+
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	else // status == eap_status_ok
+	{
+		// The query_eap_identity() function call is synchronous.
+		// We must call send_eap_identity_response().
+
+		status = send_eap_identity_response(
+			&send_network_id,
+			&m_eap_identity,
+			m_eap_identity_request_identifier_client); // Uses the EAP-Identifier from the latest EAP-Request/Identity.
+	}
+
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+#if defined(USE_EAP_CORE_SERVER)
+
+// 
+EAP_FUNC_EXPORT eap_status_e eap_core_c::handle_eap_identity_response(
+	eap_base_type_c * const handler,
+	const eap_type_value_e used_eap_type,
+	const eap_am_network_id_c * const receive_network_id,
+	eap_header_wr_c * const eap,
+	const u32_t packet_length)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+	EAP_UNREFERENCED_PARAMETER(used_eap_type); 
+
+	eap_status_e status = eap_status_process_general_error;
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	EAP_ASSERT(m_is_client == false);
+
+	if (handler == 0)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_type_does_not_exists_error);
+	}
+
+	status = handler->packet_process(
+		receive_network_id,
+		eap,
+		packet_length);
+
+	if (status == eap_status_ok)
+	{
+		// We need to copy EAP-Identity for later use.
+
+		const u8_t * identity = eap->get_type_data(eap->get_type_data_length());
+		const u32_t identity_length = eap->get_type_data_length();
+		const u8_t empty_identity[] = "";
+
+		if (identity == 0
+			|| identity_length == 0ul)
+		{
+			identity = empty_identity;
+		}
+
+		status = m_eap_identity.set_copy_of_buffer(
+			identity,
+			identity_length);
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+#endif //#if defined(USE_EAP_CORE_SERVER)
+
+//--------------------------------------------------
+
+#if defined(USE_EAP_CORE_SERVER)
+
+//
+EAP_FUNC_EXPORT eap_status_e eap_core_c::send_eap_success(
+	const eap_am_network_id_c * const send_network_id,
+	const u8_t eap_identifier)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	eap_buf_chain_wr_c eap_success_packet(
+		eap_write_buffer, 
+		m_am_tools, 
+		EAP_CORE_PACKET_BUFFER_LENGTH);
+
+	if (eap_success_packet.get_is_valid() == false)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("eap_core_c::send_eap_success(): packet buffer corrupted: %s, %s\n"),
+			 (m_is_client == true) ? "client": "server",
+			 (m_is_tunneled_eap == true) ? "tunneled": "outer most"
+			 ));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	EAP_ASSERT_ALWAYS(EAP_CORE_PACKET_BUFFER_LENGTH >= (m_eap_header_offset+m_trailer_length));
+	u32_t packet_buffer_free = EAP_CORE_PACKET_BUFFER_LENGTH-m_trailer_length;
+
+	if (m_eap_header_offset+m_MTU < packet_buffer_free)
+	{
+		packet_buffer_free = m_eap_header_offset+m_MTU;
+	}
+
+	eap_header_wr_c eap_response(
+		m_am_tools,
+		eap_success_packet.get_data_offset(
+			m_eap_header_offset,
+			(packet_buffer_free-m_eap_header_offset)),
+		(packet_buffer_free-m_eap_header_offset));
+
+	if (eap_response.get_is_valid() == false)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("eap_core_c::send_eap_success(): %s, %s, packet buffer corrupted.\n"),
+			 (m_is_client == true) ? "client": "server",
+			 (m_is_tunneled_eap == true) ? "tunneled": "outer most"
+			 ));
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_general_error);
+	}
+
+	eap_response.reset_header(
+		static_cast<u16_t>((packet_buffer_free-m_eap_header_offset)),
+		m_use_eap_expanded_type);
+	eap_response.set_length(
+		static_cast<u16_t>((eap_header_wr_c::get_header_length())),
+		m_use_eap_expanded_type);
+	eap_response.set_code(eap_code_success);
+	eap_response.set_identifier(eap_identifier);
+
+	// - - - - - - - - - - - - - - - - - - - - - - - -
+
+	eap_status_e status = eap_status_process_general_error;
+
+	// - - - - - - - - - - - - - - - - - - - - - - - -
+
+	eap_success_packet.set_data_length(m_eap_header_offset+eap_response.get_header_length());
+
+	EAP_TRACE_DATA_DEBUG(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("    send: EAP-success packet"),
+		eap_response.get_header_buffer(eap_response.get_length()),
+		eap_response.get_length()));
+
+	status = packet_send(
+		send_network_id,
+		&eap_success_packet,
+		m_eap_header_offset,
+		eap_response.get_length(),
+		EAP_CORE_PACKET_BUFFER_LENGTH
+		);
+
+	if (status == eap_status_ok)
+	{
+		// After EAP-Success is sent no re-transmissions must occur.
+		cancel_retransmission();
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - -
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+#endif //#if defined(USE_EAP_CORE_SERVER)
+
+//--------------------------------------------------
+
+#if defined(USE_EAP_CORE_SERVER)
+
+//
+EAP_FUNC_EXPORT eap_status_e eap_core_c::send_eap_failure(
+	const eap_am_network_id_c * const send_network_id,
+	const u8_t eap_identifier)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	if (m_eap_failure_sent == true)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("eap_core_c::send_eap_failure(): %s, %s, EAP-Failure already sent.\n"),
+			 (m_is_client == true) ? "client": "server",
+			 (m_is_tunneled_eap == true) ? "tunneled": "outer most"
+			 ));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+	}
+
+	eap_buf_chain_wr_c eap_failure_packet(
+		eap_write_buffer, 
+		m_am_tools, 
+		EAP_CORE_PACKET_BUFFER_LENGTH);
+
+	if (eap_failure_packet.get_is_valid() == false)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("eap_core_c::send_eap_failure(): %s, %s, packet buffer corrupted.\n"),
+			 (m_is_client == true) ? "client": "server",
+			 (m_is_tunneled_eap == true) ? "tunneled": "outer most"
+			 ));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	EAP_ASSERT_ALWAYS(EAP_CORE_PACKET_BUFFER_LENGTH >= (m_eap_header_offset+m_trailer_length));
+	u32_t packet_buffer_free = EAP_CORE_PACKET_BUFFER_LENGTH-m_trailer_length;
+
+	if (m_eap_header_offset+m_MTU < packet_buffer_free)
+	{
+		packet_buffer_free = m_eap_header_offset+m_MTU;
+	}
+
+	eap_header_wr_c eap_response(
+		m_am_tools,
+		eap_failure_packet.get_data_offset(
+			m_eap_header_offset,
+			(packet_buffer_free-m_eap_header_offset)),
+		(packet_buffer_free-m_eap_header_offset));
+
+	if (eap_response.get_is_valid() == false)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("eap_core_c::send_eap_failure(): %s, %s, packet buffer corrupted.\n"),
+			 (m_is_client == true) ? "client": "server",
+			 (m_is_tunneled_eap == true) ? "tunneled": "outer most"
+			 ));
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+	}
+
+	eap_response.reset_header(
+		static_cast<u16_t>((packet_buffer_free-m_eap_header_offset)),
+		m_use_eap_expanded_type);
+	eap_response.set_length(
+		static_cast<u16_t>((eap_header_wr_c::get_header_length())),
+		m_use_eap_expanded_type);
+	eap_response.set_code(eap_code_failure);
+	eap_response.set_identifier(eap_identifier);
+
+	// - - - - - - - - - - - - - - - - - - - - - - - -
+
+	eap_status_e status = eap_status_process_general_error;
+
+	// - - - - - - - - - - - - - - - - - - - - - - - -
+
+	eap_failure_packet.set_data_length(m_eap_header_offset+eap_response.get_header_length());
+
+	EAP_TRACE_DATA_DEBUG(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("    send: EAP-failure packet"),
+		eap_response.get_header_buffer(eap_response.get_length()),
+		eap_response.get_length()));
+
+	status = packet_send(
+		send_network_id,
+		&eap_failure_packet,
+		m_eap_header_offset,
+		eap_response.get_length(),
+		EAP_CORE_PACKET_BUFFER_LENGTH
+		);
+
+	if (status == eap_status_ok)
+	{
+		m_eap_failure_sent = true;
+
+		// After EAP-Failure is sent no re-transmissions must occur.
+		cancel_retransmission();
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - -
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+#endif //#if defined(USE_EAP_CORE_SERVER)
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eap_core_c::send_eap_notification_response(
+	const eap_am_network_id_c * const send_network_id,
+	const u8_t eap_identifier)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	eap_buf_chain_wr_c eap_notification_packet(
+		eap_write_buffer, 
+		m_am_tools, 
+		EAP_CORE_PACKET_BUFFER_LENGTH);
+
+	if (eap_notification_packet.get_is_valid() == false)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("eap_core_c::send_eap_notification_response(): ")
+			 EAPL("%s, %s, packet buffer corrupted.\n"),
+			 (m_is_client == true) ? "client": "server",
+			 (m_is_tunneled_eap == true) ? "tunneled": "outer most"
+			 ));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	EAP_ASSERT_ALWAYS(EAP_CORE_PACKET_BUFFER_LENGTH >= (m_eap_header_offset+m_trailer_length));
+	u32_t packet_buffer_free = EAP_CORE_PACKET_BUFFER_LENGTH-m_trailer_length;
+
+	if (m_eap_header_offset+m_MTU < packet_buffer_free)
+	{
+		packet_buffer_free = m_eap_header_offset+m_MTU;
+	}
+
+	eap_header_wr_c eap_response(
+		m_am_tools,
+		eap_notification_packet.get_data_offset(
+			m_eap_header_offset,
+			(packet_buffer_free-m_eap_header_offset)),
+		(packet_buffer_free-m_eap_header_offset));
+
+	if (eap_response.get_is_valid() == false)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("eap_core_c::send_eap_notification_response(): ")
+			 EAPL("%s, %s, packet buffer corrupted.\n"),
+			 (m_is_client == true) ? "client": "server",
+			 (m_is_tunneled_eap == true) ? "tunneled": "outer most"
+			 ));
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_general_error);
+	}
+
+	eap_response.reset_header(
+		static_cast<u16_t>((packet_buffer_free-m_eap_header_offset)),
+		m_use_eap_expanded_type);
+	eap_response.set_length(
+		static_cast<u16_t>((eap_header_base_c::get_header_length()+1u)),
+		m_use_eap_expanded_type);
+	eap_response.set_code(eap_code_response);
+	eap_response.set_identifier(eap_identifier);
+	eap_response.set_type(eap_type_notification, m_use_eap_expanded_type);
+	
+
+	eap_status_e status = eap_status_process_general_error;
+
+	// - - - - - - - - - - - - - - - - - - - - - - - -
+
+	eap_notification_packet.set_data_length(
+		m_eap_header_offset+eap_response.get_header_length()+1u);
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("    send: EAP-Response/Notification packet"),
+		 eap_response.get_header_buffer(eap_response.get_length()),
+		 eap_response.get_length()));
+	
+	status = packet_send(
+		send_network_id,
+		&eap_notification_packet,
+		m_eap_header_offset,
+		eap_response.get_length(),
+		EAP_CORE_PACKET_BUFFER_LENGTH
+		);
+
+	if (status == eap_status_ok)
+	{
+		// After EAP-Notification is sent no re-transmissions must occur.
+		cancel_retransmission();
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - -
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eap_core_c::create_eap_identity_response(
+	eap_buf_chain_wr_c * const response_packet,
+	const eap_variable_data_c * const identity,
+	const u8_t eap_identifier
+	)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	if (identity == 0
+		|| identity->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 (response_packet == 0
+		|| response_packet->get_is_valid() == false)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("packet buffer corrupted: %s, %s\n"),
+			 (m_is_client == true) ? "client": "server",
+			 (m_is_tunneled_eap == true) ? "tunneled": "outer most"
+			 ));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	EAP_ASSERT_ALWAYS(EAP_CORE_PACKET_BUFFER_LENGTH >= (m_eap_header_offset+m_trailer_length));
+	u32_t packet_buffer_free = EAP_CORE_PACKET_BUFFER_LENGTH-m_trailer_length;
+	u32_t packet_buffer_offset = 0u;
+
+	if (m_eap_header_offset+m_MTU < packet_buffer_free)
+	{
+		packet_buffer_free = m_eap_header_offset+m_MTU;
+	}
+
+	eap_header_wr_c eap_response(
+		m_am_tools,
+		response_packet->get_data_offset(
+			m_eap_header_offset,
+			(EAP_CORE_PACKET_BUFFER_LENGTH-(m_eap_header_offset+m_trailer_length))),
+		EAP_CORE_PACKET_BUFFER_LENGTH-(m_eap_header_offset+m_trailer_length));
+
+	if (eap_response.get_is_valid() == false)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("send_eap_identity_response: %s, %s, packet buffer corrupted.\n"),
+			 (m_is_client == true) ? "client": "server",
+			 (m_is_tunneled_eap == true) ? "tunneled": "outer most"
+			 ));
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+	}
+
+	eap_response.reset_header(
+		static_cast<u16_t>((packet_buffer_free-m_eap_header_offset)),
+		m_use_eap_expanded_type);
+
+	eap_response.set_length(
+		static_cast<u16_t>((packet_buffer_free-m_eap_header_offset)),
+		m_use_eap_expanded_type);
+	eap_response.set_code(eap_code_response);
+	eap_response.set_identifier(eap_identifier);
+	eap_response.set_type(eap_type_identity, m_use_eap_expanded_type);
+
+	packet_buffer_offset += eap_response.get_header_length();
+
+	u8_t * const target_nai = eap_response.get_type_data(
+		identity->get_data_length());
+	if (target_nai == 0)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("send_eap_identity_response: %s, %s, too long EAP-Identity.\n"),
+			 (m_is_client == true) ? "client": "server",
+			 (m_is_tunneled_eap == true) ? "tunneled": "outer most"
+			 ));
+
+		EAP_TRACE_DATA_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("Identity"),
+			 identity->get_data(identity->get_data_length()),
+			 identity->get_data_length()));
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	m_am_tools->memmove(
+		target_nai,
+		identity->get_data(identity->get_data_length()), 
+		identity->get_data_length());
+
+	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+	eap_response.set_type_data_length(
+		static_cast<u16_t>(identity->get_data_length()),
+		m_use_eap_expanded_type);
+
+	response_packet->set_data_length(m_eap_header_offset+eap_response.get_length());
+
+	EAP_TRACE_DATA_DEBUG(
+		m_am_tools,
+		TRACE_FLAGS_DEFAULT,
+		(EAPL("send EAP-identity NAI"),
+		 eap_response.get_type_data(eap_response.get_type_data_length()),
+		 eap_response.get_type_data_length()));
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eap_core_c::send_eap_identity_response(
+	const eap_am_network_id_c * const send_network_id,
+	const eap_variable_data_c * const identity,
+	const u8_t eap_identifier
+	)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	eap_status_e status = eap_status_process_general_error;
+
+	if (send_network_id == 0
+		|| identity == 0)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_process_general_error);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - -
+
+	eap_buf_chain_wr_c response_packet(
+		eap_write_buffer, 
+		m_am_tools, 
+		EAP_CORE_PACKET_BUFFER_LENGTH);
+
+	if (response_packet.get_is_valid() == false)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("packet buffer corrupted: %s, %s.\n"),
+			 (m_is_client == true) ? "client": "server",
+			 (m_is_tunneled_eap == true) ? "tunneled": "outer most"
+			 ));
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - -
+
+	// Get add possible NAI decoration and 
+	// extra routing info to identity
+
+	eap_variable_data_c local_identity(m_am_tools);
+	status = local_identity.set_copy_of_buffer(identity);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	status = set_eap_identity_routing_info_and_nai_decoration(&local_identity);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// - - - - - - - - - - - - - - - - - - - - - - - -
+
+	status = create_eap_identity_response(
+		&response_packet,
+		&local_identity,
+		eap_identifier);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	eap_header_wr_c eap_response(
+		m_am_tools,
+		response_packet.get_data_offset(
+			m_eap_header_offset,
+			(response_packet.get_buffer_length()-(m_eap_header_offset+m_trailer_length))),
+		response_packet.get_buffer_length()-(m_eap_header_offset+m_trailer_length));
+	if (eap_response.get_is_valid() == false)
+	{
+		EAP_TRACE_ERROR(
+			m_am_tools, 
+			TRACE_FLAGS_DEFAULT, 
+			(EAPL("send_eap_identity_response: %s, %s, packet buffer corrupted.\n"),
+			 (m_is_client == true) ? "client": "server",
+			 (m_is_tunneled_eap == true) ? "tunneled": "outer most"
+			 ));
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
+	}
+
+	status = packet_send(
+		send_network_id,
+		&response_packet,
+		m_eap_header_offset,
+		eap_response.get_length(),
+		response_packet.get_buffer_length());
+	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_EAP_CORE_WAIT_REQUEST_TYPE_TIMER)
+
+	if (m_is_tunneled_eap == false
+		&& m_is_client_role == true)
+	{
+		status = cancel_wait_eap_request_type_timeout();
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+
+		status = set_wait_eap_request_type_timeout();
+	}
+
+#endif //#if defined(USE_EAP_CORE_WAIT_REQUEST_TYPE_TIMER)
+
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+eap_status_e eap_core_c::set_eap_identity_routing_info_and_nai_decoration(
+	eap_variable_data_c * const identity)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	if (identity == 0
+		|| identity->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);
+	}
+
+	bool routing_info_available(true);
+	bool nai_decoration_available(true);
+
+	eap_status_e status(eap_status_process_general_error);
+
+	// read routing info from AM
+	eap_variable_data_c routing_info(m_am_tools);
+	status = read_configure(
+		cf_str_EAP_outer_identity_routing.get_field(),
+		&routing_info);
+	if (status != eap_status_ok 
+		|| routing_info.get_is_valid_data() == false 
+		|| routing_info.get_data_length() == 0)
+	{
+		routing_info_available = false;
+	}
+
+	// read NAI decoration from AM
+	eap_variable_data_c nai_decoration(m_am_tools);
+	status = read_configure(
+		cf_str_EAP_outer_identity_decoration.get_field(),
+		&nai_decoration);
+	if (status != eap_status_ok 
+		|| nai_decoration.get_is_valid_data() == false 
+		|| nai_decoration.get_data_length() == 0)
+	{
+		nai_decoration_available = false;
+	}
+
+	// nothing to be added (which is ok)
+	if(routing_info_available == false 
+		&& nai_decoration_available == false) 
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+	}
+
+	eap_variable_data_c username(m_am_tools);
+	eap_variable_data_c home_realm(m_am_tools);
+
+	// get username and home realm from the current NAI
+	status = m_am_tools->parse_nai(
+		identity,
+		&username,
+		&home_realm);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	// username must be present
+	if (username.get_is_valid_data() == false
+		/* || home_realm.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);
+	}
+
+	// original NAI can be cleared,
+	// the new one is constructed to the same place
+	identity->reset();
+
+	// routing_info contains a string in format 
+	// "RoutingRealm1!RoutingRealm2!RoutingRealm3"
+	// in which RoutingRealm3 is replaced with the home
+	// realm and RoutingRealm1 is appended after @-sign in NAI
+	if(routing_info_available == true) 
+	{
+		// this points to the last byte of routing_info
+		const u8_t* const p_last = 
+			routing_info.get_data() + 
+			(routing_info.get_data_length() - 1);
+
+		// first and last characters cannot be !-signs
+		if(*p_last == EAP_NAI_ROUTING_REALM_SEPARATOR[0]
+			|| *(routing_info.get_data()) == EAP_NAI_ROUTING_REALM_SEPARATOR[0])
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
+		}
+
+		// find first !-sign
+		const u8_t *separator = reinterpret_cast<const u8_t *>(
+			m_am_tools->memchr(
+				routing_info.get_data(),
+				EAP_NAI_ROUTING_REALM_SEPARATOR[0],
+				routing_info.get_data_length())
+			);
+
+		// !-sign found, more than one realm present
+		// (the sign is not first or last character)
+		if (separator != 0)
+		{
+			// others except the first realm are put in front
+			status = identity->add_data(
+				separator + 1, 
+				p_last - separator);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			// add !-sign
+			status = identity->add_data(EAP_NAI_ROUTING_REALM_SEPARATOR, 1);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+		}
+
+		// add home realm if it existed
+		if(home_realm.get_is_valid_data() == true)
+		{
+			status = identity->add_data(
+				home_realm.get_data(), 
+				home_realm.get_data_length());
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+
+			// add !-sign
+			status = identity->add_data(EAP_NAI_ROUTING_REALM_SEPARATOR, 1);
+			if (status != eap_status_ok)
+			{
+				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+				return EAP_STATUS_RETURN(m_am_tools, status);
+			}
+		}
+
+		// store the first realm of routing info here
+		home_realm.reset();
+
+		// more than one realm in routing info
+		if (separator != 0) 
+		{
+			status = home_realm.add_data(
+				routing_info.get_data(), 
+				separator - routing_info.get_data()
+				);
+		}
+		// only one realm in routing info
+		else
+		{
+			status = home_realm.add_data(
+				routing_info.get_data(), 
+				routing_info.get_data_length());
+		}
+
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	// nai_decoration contains a string which is placed 
+	// in front of the username in NAI
+	if(nai_decoration_available == true) 
+	{
+		status = identity->add_data(nai_decoration.get_data(), nai_decoration.get_data_length());
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	status = identity->add_data(username.get_data(), username.get_data_length());
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+	
+	// add realm if it exists
+        if (home_realm.get_is_valid_data() == true) {
+	  // add @-sign
+	  status = identity->add_data(EAP_NAI_AT_CHARACTER, 1);
+	  if (status != eap_status_ok)
+	    {
+	      EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	      return EAP_STATUS_RETURN(m_am_tools, status);
+	    }
+	  
+	  status = identity->add_data(home_realm.get_data(), home_realm.get_data_length());
+	}
+	
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eap_core_c::complete_eap_identity_query(
+	const eap_am_network_id_c * const send_network_id,
+	const eap_variable_data_c * const identity,
+	const u8_t /*eap_identifier*/) // Remove eap_identifier parameter.
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+	eap_status_e status(eap_status_illegal_eap_identity);
+
+	if (identity != 0)
+	{
+		status = m_eap_identity.set_copy_of_buffer(identity);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+			
+		status = send_eap_identity_response(
+			send_network_id,
+			identity,
+			m_eap_identity_request_identifier_client); // Uses the EAP-Identifier from the latest EAP-Request/Identity.
+	}
+
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eap_core_c::get_saved_eap_identity(
+	eap_variable_data_c * const identity)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	eap_status_e status = eap_status_illegal_eap_identity;
+
+	if (m_eap_identity.get_is_valid_data() == true)
+	{
+		status = identity->set_copy_of_buffer(&m_eap_identity);
+	}
+	else
+	{
+		EAP_TRACE_DEBUG(
+			m_am_tools,
+			TRACE_FLAGS_DEFAULT,
+			(EAPL("WARNING: %s, %s, EAP-identity is unknown: current EAP-type 0x%08x\n"),
+			 (m_is_client == true) ? "client": "server",
+			 (m_is_tunneled_eap == true) ? "tunneled": "outer most",
+			 convert_eap_type_to_u32_t(m_current_eap_type)));
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN_WARNING(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eap_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_status_e status = initialize_session_timeout(session_timeout_ms);
+	if (status != eap_status_ok)
+	{
+		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+		return EAP_STATUS_RETURN(m_am_tools, status);
+	}
+
+	if (m_is_tunneled_eap == true)
+	{
+		status = m_partner->set_session_timeout(session_timeout_ms);
+		if (status != eap_status_ok)
+		{
+			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+			return EAP_STATUS_RETURN(m_am_tools, status);
+		}
+	}
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, status);
+}
+
+//--------------------------------------------------
+
+//
+EAP_FUNC_EXPORT eap_status_e eap_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 eap_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 eap_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 eap_core_c::set_authentication_role(const bool when_true_set_client)
+{
+	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
+
+	cancel_retransmission();
+
+	cancel_eap_failure_timeout();
+
+	m_is_client_role = when_true_set_client;
+
+	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
+	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
+}
+
+//--------------------------------------------------
+
+EAP_FUNC_EXPORT eap_status_e eap_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);
+
+	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 bool eap_core_c::get_is_tunneled_eap() const
+{
+	return m_is_tunneled_eap;
+}
+
+//--------------------------------------------------
+//--------------------------------------------------
+
+
+
+// End.