From 0dbe96f62d6667f4e7435c12ee1ee9b40d359579 Mon Sep 17 00:00:00 2001 From: Sachin Nayak Date: Wed, 10 Nov 2021 06:47:30 -0800 Subject: [PATCH] lte: Handover failure test suite and related code --- src/lte/CMakeLists.txt | 1 + src/lte/model/epc-x2-sap.h | 21 ++ src/lte/model/epc-x2.cc | 27 ++ src/lte/model/lte-enb-rrc.cc | 178 +++++++--- src/lte/model/lte-enb-rrc.h | 18 +- src/lte/model/lte-rrc-protocol-ideal.cc | 8 +- src/lte/model/lte-rrc-protocol-real.cc | 1 - src/lte/test/test-lte-handover-failure.cc | 401 ++++++++++++++++++++++ 8 files changed, 601 insertions(+), 54 deletions(-) create mode 100644 src/lte/test/test-lte-handover-failure.cc diff --git a/src/lte/CMakeLists.txt b/src/lte/CMakeLists.txt index f31f4e692..9fca0671a 100644 --- a/src/lte/CMakeLists.txt +++ b/src/lte/CMakeLists.txt @@ -335,6 +335,7 @@ set(test_sources test/test-lte-antenna.cc test/test-lte-epc-e2e-data.cc test/test-lte-handover-delay.cc + test/test-lte-handover-failure.cc test/test-lte-handover-target.cc test/test-lte-rlc-header.cc test/test-lte-rrc.cc diff --git a/src/lte/model/epc-x2-sap.h b/src/lte/model/epc-x2-sap.h index 385eb2fd7..735267d74 100644 --- a/src/lte/model/epc-x2-sap.h +++ b/src/lte/model/epc-x2-sap.h @@ -480,6 +480,13 @@ public: * \param params UE data parameters */ virtual void RecvUeData (UeDataParams params) = 0; + + /** + * Receive handover cancel function + * \param params the receive handover cancel parameters + * + */ + virtual void RecvHandoverCancel (HandoverCancelParams params) = 0; }; /////////////////////////////////////// @@ -701,6 +708,13 @@ public: */ virtual void RecvUeData (UeDataParams params); + /** + * Receive handover cancel function + * \param params the receive handover cancel parameters + * + */ + virtual void RecvHandoverCancel (HandoverCancelParams params); + private: EpcX2SpecificEpcX2SapUser (); C* m_rrc; ///< owner class @@ -773,6 +787,13 @@ EpcX2SpecificEpcX2SapUser::RecvUeData (UeDataParams params) m_rrc->DoRecvUeData (params); } +template +void +EpcX2SpecificEpcX2SapUser::RecvHandoverCancel (HandoverCancelParams params) +{ + m_rrc->DoRecvHandoverCancel (params); +} + } // namespace ns3 #endif // EPC_X2_SAP_H diff --git a/src/lte/model/epc-x2.cc b/src/lte/model/epc-x2.cc index 060164932..10d9dee1a 100644 --- a/src/lte/model/epc-x2.cc +++ b/src/lte/model/epc-x2.cc @@ -362,6 +362,33 @@ EpcX2::RecvFromX2cSocket (Ptr socket) m_x2SapUser->RecvResourceStatusUpdate (params); } } + else if (procedureCode == EpcX2Header::HandoverCancel) + { + if (messageType == EpcX2Header::SuccessfulOutcome) + { + NS_LOG_LOGIC("Recv X2 message: HANDOVER CANCEL"); + + EpcX2HandoverCancelHeader x2HoCancelHeader; + packet->RemoveHeader (x2HoCancelHeader); + + NS_LOG_INFO("X2 HandoverCancel header: " << x2HoCancelHeader); + + EpcX2SapUser::HandoverCancelParams params; + params.oldEnbUeX2apId = x2HoCancelHeader.GetOldEnbUeX2apId (); + params.newEnbUeX2apId = x2HoCancelHeader.GetNewEnbUeX2apId (); + params.sourceCellId = cellsInfo->m_localCellIds.at (0); + params.targetCellId = cellsInfo->m_remoteCellIds.at (0); + params.cause = x2HoCancelHeader.GetCause (); + + NS_LOG_LOGIC("oldEnbUeX2apId = " << params.oldEnbUeX2apId); + NS_LOG_LOGIC("newEnbUeX2apId = " << params.newEnbUeX2apId); + NS_LOG_LOGIC("sourceCellId = " << params.sourceCellId); + NS_LOG_LOGIC("targetCellId = " << params.targetCellId); + NS_LOG_LOGIC("cause = " << params.cause); + + m_x2SapUser->RecvHandoverCancel (params); + } + } else { NS_ASSERT_MSG (false, "ProcedureCode NOT SUPPORTED!!!"); diff --git a/src/lte/model/lte-enb-rrc.cc b/src/lte/model/lte-enb-rrc.cc index 7603c859b..3b2d9bee4 100644 --- a/src/lte/model/lte-enb-rrc.cc +++ b/src/lte/model/lte-enb-rrc.cc @@ -571,11 +571,15 @@ void LteEnbRrc::DoSendReleaseDataRadioBearer (uint64_t imsi, uint16_t rnti, uint8_t bearerId) { NS_LOG_FUNCTION (this << imsi << rnti << (uint16_t) bearerId); - Ptr ueManager = GetUeManager (rnti); - // Bearer de-activation towards UE - ueManager->ReleaseDataRadioBearer (bearerId); - // Bearer de-activation indication towards epc-enb application - m_s1SapProvider->DoSendReleaseIndication (imsi,rnti,bearerId); + + // check if the RNTI to be removed is not stale + if (HasUeManager (rnti)) { + Ptr ueManager = GetUeManager (rnti); + // Bearer de-activation towards UE + ueManager->ReleaseDataRadioBearer (bearerId); + // Bearer de-activation indication towards epc-enb application + m_s1SapProvider->DoSendReleaseIndication (imsi,rnti,bearerId); + } } void @@ -996,7 +1000,7 @@ UeManager::RecvHandoverPreparationFailure (uint16_t cellId) NS_LOG_INFO ("target eNB sent HO preparation failure, aborting HO"); SwitchToState (CONNECTED_NORMALLY); break; - case HANDOVER_LEAVING: //case added to tackle HO joining timer expiration + case HANDOVER_LEAVING: //case added to tackle HO leaving timer expiration NS_ASSERT (cellId == m_targetCellId); NS_LOG_INFO ("target eNB sent HO preparation failure, aborting HO"); m_handoverLeavingTimeout.Cancel (); @@ -1036,6 +1040,14 @@ UeManager::RecvUeContextRelease (EpcX2SapUser::UeContextReleaseParams params) m_handoverLeavingTimeout.Cancel (); } +void +UeManager::RecvHandoverCancel (EpcX2SapUser::HandoverCancelParams params) +{ + NS_LOG_FUNCTION (this); + NS_ASSERT_MSG (m_state == HANDOVER_JOINING, "method unexpected in state " << ToString (m_state)); + m_handoverJoiningTimeout.Cancel (); +} + void UeManager::SendRrcConnectionRelease () { @@ -2541,17 +2553,20 @@ LteEnbRrc::HandoverJoiningTimeout (uint16_t rnti) "HandoverJoiningTimeout in unexpected state " << ToString (GetUeManager (rnti)->GetState ())); m_handoverFailureJoiningTrace (GetUeManager (rnti)->GetImsi (), rnti, ComponentCarrierToCellId (GetUeManager (rnti)->GetComponentCarrierId ())); - /** - * When the handover joining timer expires at the target cell, - * then notify the source cell to release the RRC connection and - * delete the UE context at eNodeB and SGW/PGW. The - * HandoverPreparationFailure message is reused to notify the source cell - * through the X2 interface instead of creating a new message. - */ - Ptr ueManager = GetUeManager (rnti); - EpcX2Sap::HandoverPreparationFailureParams msg = ueManager->BuildHoPrepFailMsg (); - m_x2SapProvider->SendHandoverPreparationFailure (msg); - RemoveUe (rnti); + // check if the RNTI to be removed is not stale + if (HasUeManager (rnti)) { + /** + * When the handover joining timer expires at the target cell, + * then notify the source cell to release the RRC connection and + * delete the UE context at eNodeB and SGW/PGW. The + * HandoverPreparationFailure message is reused to notify the source cell + * through the X2 interface instead of creating a new message. + */ + Ptr ueManager = GetUeManager (rnti); + EpcX2Sap::HandoverPreparationFailureParams msg = ueManager->BuildHoPrepFailMsg (); + m_x2SapProvider->SendHandoverPreparationFailure (msg); + RemoveUe (rnti); + } } void @@ -2562,15 +2577,18 @@ LteEnbRrc::HandoverLeavingTimeout (uint16_t rnti) "HandoverLeavingTimeout in unexpected state " << ToString (GetUeManager (rnti)->GetState ())); m_handoverFailureLeavingTrace (GetUeManager (rnti)->GetImsi (), rnti, ComponentCarrierToCellId (GetUeManager (rnti)->GetComponentCarrierId ())); - /** - * Send HO cancel msg to the target eNB and release the RRC connection - * with the UE and also delete UE context at the source eNB and bearer - * info at SGW and PGW. - */ - Ptr ueManager = GetUeManager (rnti); - EpcX2Sap::HandoverCancelParams msg = ueManager->BuildHoCancelMsg (); - m_x2SapProvider->SendHandoverCancel (msg); - ueManager->SendRrcConnectionRelease (); + // check if the RNTI to be removed is not stale + if (HasUeManager (rnti)) { + /** + * Send HO cancel msg to the target eNB and release the RRC connection + * with the UE and also delete UE context at the source eNB and bearer + * info at SGW and PGW. + */ + Ptr ueManager = GetUeManager (rnti); + EpcX2Sap::HandoverCancelParams msg = ueManager->BuildHoCancelMsg (); + m_x2SapProvider->SendHandoverCancel (msg); + ueManager->SendRrcConnectionRelease (); + } } void @@ -2646,25 +2664,31 @@ void LteEnbRrc::DoRecvIdealUeContextRemoveRequest (uint16_t rnti) { NS_LOG_FUNCTION (this << rnti); - Ptr ueManager = GetUeManager (rnti); - if (ueManager->GetState () == UeManager::HANDOVER_JOINING) + // check if the RNTI to be removed is not stale + if (HasUeManager (rnti)) { - m_handoverFailureMaxRachTrace (GetUeManager (rnti)->GetImsi (), rnti, - ComponentCarrierToCellId (GetUeManager (rnti)->GetComponentCarrierId ())); - /** - * During the HO, when the RACH failure due to the maximum number of - * re-attempts is reached the UE request the target eNB to deletes its - * context. Upon which, the target eNB sends handover preparation - * failure to the source eNB. - */ - EpcX2Sap::HandoverPreparationFailureParams msg = ueManager->BuildHoPrepFailMsg (); - m_x2SapProvider->SendHandoverPreparationFailure (msg); + Ptr ueManager = GetUeManager (rnti); + + if (ueManager->GetState () == UeManager::HANDOVER_JOINING) + { + m_handoverFailureMaxRachTrace (GetUeManager (rnti)->GetImsi (), rnti, + ComponentCarrierToCellId (GetUeManager (rnti)->GetComponentCarrierId ())); + /** + * During the HO, when the RACH failure due to the maximum number of + * re-attempts is reached the UE request the target eNB to deletes its + * context. Upon which, the target eNB sends handover preparation + * failure to the source eNB. + */ + EpcX2Sap::HandoverPreparationFailureParams msg = ueManager->BuildHoPrepFailMsg (); + m_x2SapProvider->SendHandoverPreparationFailure (msg); + } + + GetUeManager (rnti)->RecvIdealUeContextRemoveRequest (rnti); + // delete the UE context at the eNB + RemoveUe (rnti); } - GetUeManager (rnti)->RecvIdealUeContextRemoveRequest (rnti); - //delete the UE context at the eNB - RemoveUe (rnti); } void @@ -2695,7 +2719,8 @@ LteEnbRrc::DoRecvHandoverRequest (EpcX2SapUser::HandoverRequestParams req) NS_LOG_LOGIC ("targetCellId = " << req.targetCellId); NS_LOG_LOGIC ("mmeUeS1apId = " << req.mmeUeS1apId); - if (m_admitHandoverRequest == false) + // if no SRS index is available, then do not accept the handover + if (m_admitHandoverRequest == false || IsMaxSrsReached()) { NS_LOG_INFO ("rejecting handover request from cellId " << req.sourceCellId); EpcX2Sap::HandoverPreparationFailureParams res; @@ -2805,8 +2830,12 @@ LteEnbRrc::DoRecvHandoverPreparationFailure (EpcX2SapUser::HandoverPreparationFa NS_LOG_LOGIC ("criticalityDiagnostics = " << params.criticalityDiagnostics); uint16_t rnti = params.oldEnbUeX2apId; - Ptr ueManager = GetUeManager (rnti); - ueManager->RecvHandoverPreparationFailure (params.targetCellId); + + // check if the RNTI is not stale + if (HasUeManager (rnti)) { + Ptr ueManager = GetUeManager (rnti); + ueManager->RecvHandoverPreparationFailure (params.targetCellId); + } } void @@ -2821,8 +2850,13 @@ LteEnbRrc::DoRecvSnStatusTransfer (EpcX2SapUser::SnStatusTransferParams params) NS_LOG_LOGIC ("erabsSubjectToStatusTransferList size = " << params.erabsSubjectToStatusTransferList.size ()); uint16_t rnti = params.newEnbUeX2apId; - Ptr ueManager = GetUeManager (rnti); - ueManager->RecvSnStatusTransfer (params); + + // check if the RNTI to receive SN transfer for is not stale + if (HasUeManager (rnti)) + { + Ptr ueManager = GetUeManager (rnti); + ueManager->RecvSnStatusTransfer (params); + } } void @@ -2836,8 +2870,12 @@ LteEnbRrc::DoRecvUeContextRelease (EpcX2SapUser::UeContextReleaseParams params) NS_LOG_LOGIC ("newEnbUeX2apId = " << params.newEnbUeX2apId); uint16_t rnti = params.oldEnbUeX2apId; - GetUeManager (rnti)->RecvUeContextRelease (params); - RemoveUe (rnti); + + // check if the RNTI to be removed is not stale + if (HasUeManager (rnti)) { + GetUeManager (rnti)->RecvUeContextRelease (params); + RemoveUe (rnti); + } } void @@ -2889,11 +2927,37 @@ LteEnbRrc::DoRecvUeData (EpcX2SapUser::UeDataParams params) } } +void +LteEnbRrc::DoRecvHandoverCancel (EpcX2SapUser::HandoverCancelParams params) +{ + NS_LOG_FUNCTION (this); + + NS_LOG_LOGIC ("Recv X2 message: HANDOVER CANCEL"); + + NS_LOG_LOGIC ("oldEnbUeX2apId = " << params.oldEnbUeX2apId); + NS_LOG_LOGIC ("newEnbUeX2apId = " << params.newEnbUeX2apId); + NS_LOG_LOGIC ("sourceCellId = " << params.sourceCellId); + NS_LOG_LOGIC ("targetCellId = " << params.targetCellId); + NS_LOG_LOGIC ("cause = " << params.cause); + + uint16_t rnti = params.newEnbUeX2apId; + if (HasUeManager (rnti)) + { + Ptr ueManager = GetUeManager (rnti); + ueManager->RecvHandoverCancel (params); + GetUeManager (rnti)->RecvIdealUeContextRemoveRequest (rnti); + } +} uint16_t LteEnbRrc::DoAllocateTemporaryCellRnti (uint8_t componentCarrierId) { NS_LOG_FUNCTION (this << +componentCarrierId); + // if no SRS index is available, then do not create a new UE context. + if(IsMaxSrsReached()) + { + return 0; // return 0 since new RNTI was not assigned for the received preamble + } return AddUe (UeManager::INITIAL_RANDOM_ACCESS, componentCarrierId); } @@ -3245,7 +3309,23 @@ LteEnbRrc::RemoveSrsConfigurationIndex (uint16_t srcCi) m_ueSrsConfigurationIndexSet.erase (it); } -uint8_t +bool +LteEnbRrc::IsMaxSrsReached () +{ + NS_ASSERT(m_srsCurrentPeriodicityId > 0); + NS_ASSERT(m_srsCurrentPeriodicityId < SRS_ENTRIES); + NS_LOG_DEBUG(this << " SRS p " << g_srsPeriodicity[m_srsCurrentPeriodicityId] << " set " << m_ueSrsConfigurationIndexSet.size ()); + if (m_ueSrsConfigurationIndexSet.size () >= g_srsPeriodicity[m_srsCurrentPeriodicityId]) + { + return true; + } + else + { + return false; + } +} + +uint8_t LteEnbRrc::GetLogicalChannelGroup (EpsBearer bearer) { if (bearer.IsGbr ()) diff --git a/src/lte/model/lte-enb-rrc.h b/src/lte/model/lte-enb-rrc.h index d59e79c3a..f4a6450e1 100644 --- a/src/lte/model/lte-enb-rrc.h +++ b/src/lte/model/lte-enb-rrc.h @@ -266,6 +266,12 @@ public: */ void RecvUeContextRelease (EpcX2SapUser::UeContextReleaseParams params); + /** + * Take the necessary actions in response to the reception of an X2 UE CONTEXT RELEASE message + * + * \param params the SN STATUS + */ + void RecvHandoverCancel (EpcX2SapUser::HandoverCancelParams params); // METHODS FORWARDED FROM ENB RRC SAP /////////////////////////////////////// @@ -1276,6 +1282,13 @@ private: * \param params EpcX2SapUser::UeDataParams */ void DoRecvUeData (EpcX2SapUser::UeDataParams params); + /** + * Receive Handover Cancel function. + * + * \param params EpcX2SapUser::HandoverCancelParams + */ + void DoRecvHandoverCancel (EpcX2SapUser::HandoverCancelParams params); + // CMAC SAP methods @@ -1474,7 +1487,10 @@ private: */ void RemoveSrsConfigurationIndex (uint16_t srcCi); - + /** + * \return true if all the SRS indices are assigned to UEs + */ + bool IsMaxSrsReached(); /** * diff --git a/src/lte/model/lte-rrc-protocol-ideal.cc b/src/lte/model/lte-rrc-protocol-ideal.cc index 25c99d2ad..9a8266913 100644 --- a/src/lte/model/lte-rrc-protocol-ideal.cc +++ b/src/lte/model/lte-rrc-protocol-ideal.cc @@ -299,9 +299,11 @@ LteEnbRrcProtocolIdeal::SetUeRrcSapProvider (uint16_t rnti, LteUeRrcSapProvider* { std::map::iterator it; it = m_enbRrcSapProviderMap.find (rnti); - NS_ASSERT_MSG (it != m_enbRrcSapProviderMap.end (), "Cell id " << m_cellId - << " could not find RNTI = " << rnti); - it->second = p; + // assign UE RRC only if the RNTI is found at eNB + if (it != m_enbRrcSapProviderMap.end ()) + { + it->second = p; + } } void diff --git a/src/lte/model/lte-rrc-protocol-real.cc b/src/lte/model/lte-rrc-protocol-real.cc index 62118780a..71c7255a4 100644 --- a/src/lte/model/lte-rrc-protocol-real.cc +++ b/src/lte/model/lte-rrc-protocol-real.cc @@ -448,7 +448,6 @@ LteEnbRrcProtocolReal::SetUeRrcSapProvider (uint16_t rnti, LteUeRrcSapProvider* { std::map::iterator it; it = m_enbRrcSapProviderMap.find (rnti); - // TODO: remove after merge of ho_failure branch // assign UE RRC only if the RNTI is found at eNB if (it != m_enbRrcSapProviderMap.end ()) { diff --git a/src/lte/test/test-lte-handover-failure.cc b/src/lte/test/test-lte-handover-failure.cc new file mode 100644 index 000000000..592753afe --- /dev/null +++ b/src/lte/test/test-lte-handover-failure.cc @@ -0,0 +1,401 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2013 Magister Solutions (original test-lte-handover-delay.cc) + * Copyright (c) 2021 University of Washington (handover failure cases) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Sachin Nayak + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +using namespace ns3; + +NS_LOG_COMPONENT_DEFINE ("LteHandoverFailureTest"); + +/** + * \ingroup lte-test + * \ingroup tests + * + * \brief Verifying that a handover failure occurs due to various causes + * + * Handover failure cases dealt with in this test include the below. + * + * 1. Handover failure due to max random access channel (RACH) attempts from UE to target eNodeB + * 2. Handover failure due to non-allocation of non-contention preamble to UE at target eNodeB + * 3. Handover failure due to HANDOVER JOINING timeout (3 cases) + * 4. Handover failure due to HANDOVER LEAVING timeout (3 cases) + * + * \sa ns3::LteHandoverFailureTestCase + */ +class LteHandoverFailureTestCase : public TestCase +{ +public: + /** + * Constructor + * + * \param name the name of the test case, to be displayed in the test result + * \param useIdealRrc if true, use the ideal RRC + * \param handoverTime the time of handover + * \param simulationDuration duration of the simulation + * \param numberOfRaPreambles number of random access preambles available for contention based RACH process + * number of non-contention preambles available for handover = (64 - numberRaPreambles) + * as numberOfRaPreambles out of the max 64 are reserved contention based RACH process + * \param preambleTransMax maximum number of random access preamble transmissions from UE to eNodeB + * \param raResponseWindowSize window length for reception of random access response (RAR) + * \param handoverJoiningTimeout time before which RRC RECONFIGURATION COMPLETE must be received + at target eNodeB after it receives a handover request + Else, the UE context is destroyed in the RRC. + Timeout can occur before different stages as below. + i. Reception of RRC CONNECTION RECONFIGURATION at source eNodeB + ii. Non-contention random access procedure from UE to target eNodeB + iii. Reception of RRC CONNECTION RECONFIGURATION COMPLETE at target eNodeB + * \param handoverLeavingTimeout time before which source eNodeB must receive a UE context release + from target eNodeB or RRC CONNECTION RESTABLISHMENT from UE + after issuing a handover request + Else, the UE context is destroyed in the RRC. + Timeout can occur before any of the cases in HANDOVER JOINING TIMEOUT + * \param targeteNodeBPosition position of the target eNodeB + */ + LteHandoverFailureTestCase (std::string name, + bool useIdealRrc, + Time handoverTime, + Time simulationDuration, + uint8_t numberOfRaPreambles, + uint8_t preambleTransMax, + uint8_t raResponseWindowSize, + Time handoverJoiningTimeout, + Time handoverLeavingTimeout, + uint16_t targeteNodeBPosition) + : TestCase (name), + m_useIdealRrc (useIdealRrc), + m_handoverTime (handoverTime), + m_simulationDuration (simulationDuration), + m_numberOfRaPreambles (numberOfRaPreambles), + m_preambleTransMax (preambleTransMax), + m_raResponseWindowSize (raResponseWindowSize), + m_handoverJoiningTimeout (handoverJoiningTimeout), + m_handoverLeavingTimeout (handoverLeavingTimeout), + m_targeteNodeBPosition (targeteNodeBPosition), + m_hasHandoverFailureOccured (false) + {} + +private: + /** + * \brief Run a simulation of a two eNodeB network using the parameters + * provided to the constructor function. + */ + void DoRun () override; + + /** + * \brief Called at the end of simulation and verifies that a handover + * and a handover failure has occured in the simulation. + */ + void DoTeardown () override; + + /** + * UE handover start callback function to indicate start of handover + * \param context the context string + * \param imsi the IMSI + * \param sourceCellId the source cell ID + * \param rnti the RNTI + * \param targetCellId the target cell ID + */ + void UeHandoverStartCallback (std::string context, uint64_t imsi, + uint16_t sourceCellId, uint16_t rnti, uint16_t targetCellId); + + /** + * Handover failure callback due to maximum RACH transmissions reached from UE to target eNodeB + * \param context the context string + * \param imsi the IMSI + * \param rnti the RNTI + * \param targetCellId the target cell ID + */ + void HandoverFailureMaxRach (std::string context, uint64_t imsi, + uint16_t rnti, uint16_t targetCellId); + + /** + * Handover failure callback due to non-allocation of non-contention preamble at target eNodeB + * \param context the context string + * \param imsi the IMSI + * \param rnti the RNTI + * \param targetCellId the target cell ID + */ + void HandoverFailureNoPreamble (std::string context, uint64_t imsi, + uint16_t rnti, uint16_t targetCellId); + + /** + * Handover failure callback due to handover joining timeout at target eNodeB + * \param context the context string + * \param imsi the IMSI + * \param rnti the RNTI + * \param targetCellId the target cell ID + */ + void HandoverFailureJoining (std::string context, uint64_t imsi, + uint16_t rnti, uint16_t targetCellId); + + /** + * Handover failure callback due to handover leaving timeout at source eNodeB + * \param context the context string + * \param imsi the IMSI + * \param rnti the RNTI + * \param targetCellId the target cell ID + */ + void HandoverFailureLeaving (std::string context, uint64_t imsi, + uint16_t rnti, uint16_t targetCellId); + + bool m_useIdealRrc; ///< use ideal RRC? + Time m_handoverTime; ///< handover time + Time m_simulationDuration; ///< the simulation duration + uint8_t m_numberOfRaPreambles; ///< number of random access preambles for contention based RACH process + uint8_t m_preambleTransMax; ///< max number of RACH preambles possible from UE to eNodeB + uint8_t m_raResponseWindowSize; ///< window length for reception of RAR + Time m_handoverJoiningTimeout; ///< handover joining timeout duration at target eNodeB + Time m_handoverLeavingTimeout; ///< handover leaving timeout duration at source eNodeB + uint16_t m_targeteNodeBPosition; ///< position of the target eNodeB + bool m_hasHandoverFailureOccured; ///< has handover failure occured in simulation + +}; // end of class LteHandoverFailureTestCase + + +void +LteHandoverFailureTestCase::DoRun () +{ + + NS_LOG_INFO (this << " " << GetName ()); + + /* + * Helpers. + */ + auto epcHelper = CreateObject (); + + auto lteHelper = CreateObject (); + lteHelper->SetEpcHelper (epcHelper); + + // Set parameters for helpers based on the test case parameters. + lteHelper->SetAttribute ("UseIdealRrc", BooleanValue (m_useIdealRrc)); + Config::SetDefault ("ns3::LteEnbMac::NumberOfRaPreambles", UintegerValue (m_numberOfRaPreambles)); + Config::SetDefault ("ns3::LteEnbMac::PreambleTransMax", UintegerValue (m_preambleTransMax)); + Config::SetDefault ("ns3::LteEnbMac::RaResponseWindowSize", UintegerValue (m_raResponseWindowSize)); + Config::SetDefault ("ns3::LteEnbRrc::HandoverJoiningTimeoutDuration", TimeValue (m_handoverJoiningTimeout)); + Config::SetDefault ("ns3::LteEnbRrc::HandoverLeavingTimeoutDuration", TimeValue (m_handoverLeavingTimeout)); + + // Set PHY model to drastically decrease with distance. + lteHelper->SetPathlossModelType (TypeId::LookupByName ("ns3::LogDistancePropagationLossModel")); + lteHelper->SetPathlossModelAttribute ("Exponent", DoubleValue (3.5)); + lteHelper->SetPathlossModelAttribute ("ReferenceLoss", DoubleValue (35)); + /* + * Physical layer. + * + * eNodeB 0 UE eNodeB 1 + * + * x ----------------------- x -------------------------- x + * 200 m m_targeteNodeBPosition + * source target + */ + // Create nodes. + NodeContainer enbNodes; + enbNodes.Create (2); + auto ueNode = CreateObject (); + + // Setup mobility + auto posAlloc = CreateObject (); + posAlloc->Add (Vector (0, 0, 0)); + posAlloc->Add (Vector (m_targeteNodeBPosition, 0, 0)); + posAlloc->Add (Vector (200, 0, 0)); + + MobilityHelper mobilityHelper; + mobilityHelper.SetMobilityModel ("ns3::ConstantPositionMobilityModel"); + mobilityHelper.SetPositionAllocator (posAlloc); + mobilityHelper.Install (enbNodes); + mobilityHelper.Install (ueNode); + + /* + * Link layer. + */ + auto enbDevs = lteHelper->InstallEnbDevice (enbNodes); + auto ueDev = lteHelper->InstallUeDevice (ueNode).Get (0); + + /* + * Network layer. + */ + InternetStackHelper inetStackHelper; + inetStackHelper.Install (ueNode); + Ipv4InterfaceContainer ueIfs; + ueIfs = epcHelper->AssignUeIpv4Address (ueDev); + + // Setup traces. + Config::Connect ("/NodeList/*/DeviceList/*/LteUeRrc/HandoverStart", + MakeCallback (&LteHandoverFailureTestCase::UeHandoverStartCallback, this)); + Config::Connect ("/NodeList/*/DeviceList/*/LteEnbRrc/HandoverFailureMaxRach", + MakeCallback (&LteHandoverFailureTestCase::HandoverFailureMaxRach, this)); + Config::Connect ("/NodeList/*/DeviceList/*/LteEnbRrc/HandoverFailureNoPreamble", + MakeCallback (&LteHandoverFailureTestCase::HandoverFailureNoPreamble, this)); + Config::Connect ("/NodeList/*/DeviceList/*/LteEnbRrc/HandoverFailureJoining", + MakeCallback (&LteHandoverFailureTestCase::HandoverFailureJoining, this)); + Config::Connect ("/NodeList/*/DeviceList/*/LteEnbRrc/HandoverFailureLeaving", + MakeCallback (&LteHandoverFailureTestCase::HandoverFailureLeaving, this)); + + // Prepare handover. + lteHelper->AddX2Interface (enbNodes); + lteHelper->Attach (ueDev, enbDevs.Get (0)); + lteHelper->HandoverRequest (m_handoverTime, ueDev, enbDevs.Get (0), enbDevs.Get (1)); + + // Run simulation. + Simulator::Stop (m_simulationDuration); + Simulator::Run (); + Simulator::Destroy (); + +} // end of void LteHandoverFailureTestCase::DoRun () + + +void +LteHandoverFailureTestCase::UeHandoverStartCallback (std::string context, uint64_t imsi, + uint16_t sourceCellId, uint16_t rnti, uint16_t targetCellId) +{ + NS_LOG_FUNCTION (this << " " << context << " IMSI-" << imsi << " sourceCellID-" << sourceCellId << " RNTI-"<< rnti << " targetCellID-" << targetCellId); + NS_LOG_INFO ("HANDOVER COMMAND received through at UE " << imsi << " to handover from " << sourceCellId << " to " << targetCellId); +} + + +void +LteHandoverFailureTestCase::HandoverFailureMaxRach (std::string context, uint64_t imsi, + uint16_t rnti, uint16_t targetCellId) +{ + NS_LOG_FUNCTION (this << context << imsi << rnti << targetCellId); + m_hasHandoverFailureOccured = true; +} + + +void +LteHandoverFailureTestCase::HandoverFailureNoPreamble (std::string context, uint64_t imsi, + uint16_t rnti, uint16_t targetCellId) +{ + NS_LOG_FUNCTION (this << context << imsi << rnti << targetCellId); + m_hasHandoverFailureOccured = true; +} + + +void +LteHandoverFailureTestCase::HandoverFailureJoining (std::string context, uint64_t imsi, + uint16_t rnti, uint16_t targetCellId) +{ + NS_LOG_FUNCTION (this << context << imsi << rnti << targetCellId); + m_hasHandoverFailureOccured = true; +} + + +void +LteHandoverFailureTestCase::HandoverFailureLeaving (std::string context, uint64_t imsi, + uint16_t rnti, uint16_t targetCellId) +{ + NS_LOG_FUNCTION (this << context << imsi << rnti << targetCellId); + m_hasHandoverFailureOccured = true; +} + + +void +LteHandoverFailureTestCase::DoTeardown () +{ + NS_LOG_FUNCTION (this); + NS_TEST_ASSERT_MSG_EQ (m_hasHandoverFailureOccured, true, "Handover failure did not occur"); +} + + +/** + * \ingroup lte-test + * \ingroup tests + * + * \brief Lte Handover Failure Test Suite + */ +static class LteHandoverFailureTestSuite : public TestSuite +{ +public: + LteHandoverFailureTestSuite () + : TestSuite ("lte-handover-failure", TestSuite::SYSTEM) + { + LogLevel logLevel = (LogLevel) (LOG_PREFIX_TIME | LOG_LEVEL_INFO); + LogComponentEnable ("LteHandoverFailureTest", logLevel); + LogComponentEnable ("LteEnbRrc", logLevel); + LogComponentEnable ("LteEnbMac", logLevel); + LogComponentEnable ("LteUeRrc", logLevel); + LogComponentEnable ("EpcX2", logLevel); + + // Argument sequence for all test cases: useIdealRrc, handoverTime, simulationDuration, numberOfRaPreambles, preambleTransMax, raResponseWindowSize, + // handoverJoiningTimeout, handoverLeavingTimeout + + // Test cases for REAL RRC protocol + AddTestCase (new LteHandoverFailureTestCase ("REAL Handover failure due to maximum RACH transmissions reached from UE to target eNodeB", + false, Seconds (0.200), Seconds (0.300), 52, 3, 3, MilliSeconds(200), MilliSeconds(500), 2500), TestCase::QUICK); + AddTestCase (new LteHandoverFailureTestCase ("REAL Handover failure due to non-allocation of non-contention preamble at target eNodeB due to max number reached", + false, Seconds (0.100), Seconds (0.200), 64, 50, 3, MilliSeconds(200), MilliSeconds(500), 1500), TestCase::QUICK); + AddTestCase (new LteHandoverFailureTestCase ("REAL Handover failure due to HANDOVER JOINING timeout before reception of RRC CONNECTION RECONFIGURATION at source eNodeB", + false, Seconds (0.100), Seconds (0.200), 52, 50, 3, MilliSeconds(0), MilliSeconds(500), 1500), TestCase::QUICK); + AddTestCase (new LteHandoverFailureTestCase ("REAL Handover failure due to HANDOVER JOINING timeout before completion of non-contention RACH process to target eNodeB", + false, Seconds (0.100), Seconds (0.200), 52, 50, 3, MilliSeconds(15), MilliSeconds(500), 1500), TestCase::QUICK); + AddTestCase (new LteHandoverFailureTestCase ("REAL Handover failure due to HANDOVER JOINING timeout before reception of RRC CONNECTION RECONFIGURATION COMPLETE at target eNodeB", + false, Seconds (0.100), Seconds (0.200), 52, 50, 3, MilliSeconds(18), MilliSeconds(500), 500), TestCase::QUICK); + AddTestCase (new LteHandoverFailureTestCase ("REAL Handover failure due to HANDOVER LEAVING timeout before reception of RRC CONNECTION RECONFIGURATION at source eNodeB", + false, Seconds (0.100), Seconds (0.200), 52, 50, 3, MilliSeconds(200), MilliSeconds(0), 1500), TestCase::QUICK); + AddTestCase (new LteHandoverFailureTestCase ("REAL Handover failure due to HANDOVER LEAVING timeout before completion of non-contention RACH process to target eNodeB", + false, Seconds (0.100), Seconds (0.200), 52, 50, 3, MilliSeconds(200), MilliSeconds(15), 1500), TestCase::QUICK); + AddTestCase (new LteHandoverFailureTestCase ("REAL Handover failure due to HANDOVER LEAVING timeout before reception of RRC CONNECTION RECONFIGURATION COMPLETE at target eNodeB", + false, Seconds (0.100), Seconds (0.200), 52, 50, 3, MilliSeconds(200), MilliSeconds(18), 500), TestCase::QUICK); + + // Test cases for IDEAL RRC protocol + AddTestCase (new LteHandoverFailureTestCase ("IDEAL Handover failure due to maximum RACH transmissions reached from UE to target eNodeB", + true, Seconds (0.100), Seconds (0.200), 52, 3, 3, MilliSeconds(200), MilliSeconds(500), 1500), TestCase::QUICK); + AddTestCase (new LteHandoverFailureTestCase ("IDEAL Handover failure due to non-allocation of non-contention preamble at target eNodeB due to max number reached", + true, Seconds (0.100), Seconds (0.200), 64, 50, 3, MilliSeconds(200), MilliSeconds(500), 1500), TestCase::QUICK); + AddTestCase (new LteHandoverFailureTestCase ("IDEAL Handover failure due to HANDOVER JOINING timeout before reception of RRC CONNECTION RECONFIGURATION at source eNodeB", + true, Seconds (0.100), Seconds (0.200), 52, 50, 3, MilliSeconds(0), MilliSeconds(500), 1500), TestCase::QUICK); + AddTestCase (new LteHandoverFailureTestCase ("IDEAL Handover failure due to HANDOVER JOINING timeout before completion of non-contention RACH process to target eNodeB", + true, Seconds (0.100), Seconds (0.200), 52, 50, 3, MilliSeconds(10), MilliSeconds(500), 1500), TestCase::QUICK); + AddTestCase (new LteHandoverFailureTestCase ("IDEAL Handover failure due to HANDOVER JOINING timeout before reception of RRC CONNECTION RECONFIGURATION COMPLETE at target eNodeB", + true, Seconds (0.100), Seconds (0.200), 52, 50, 3, MilliSeconds(4), MilliSeconds(500), 500), TestCase::QUICK); + AddTestCase (new LteHandoverFailureTestCase ("IDEAL Handover failure due to HANDOVER LEAVING timeout before reception of RRC CONNECTION RECONFIGURATION at source eNodeB", + true, Seconds (0.100), Seconds (0.200), 52, 50, 3, MilliSeconds(500), MilliSeconds(0), 1500), TestCase::QUICK); + AddTestCase (new LteHandoverFailureTestCase ("IDEAL Handover failure due to HANDOVER LEAVING timeout before completion of non-contention RACH process to target eNodeB", + true, Seconds (0.100), Seconds (0.200), 52, 50, 3, MilliSeconds(500), MilliSeconds(10), 1500), TestCase::QUICK); + AddTestCase (new LteHandoverFailureTestCase ("IDEAL Handover failure due to HANDOVER LEAVING timeout before reception of RRC CONNECTION RECONFIGURATION COMPLETE at target eNodeB", + true, Seconds (0.100), Seconds (0.200), 52, 50, 3, MilliSeconds(500), MilliSeconds(4), 500), TestCase::QUICK); + + + } +} g_lteHandoverFailureTestSuite; ///< end of LteHandoverFailureTestSuite ()