Commit 6e3b9b04 authored by aligungr's avatar aligungr

gNB AN release and radio link failure handling

parent 7c9902a3
......@@ -448,8 +448,9 @@ int GetProcedureCode(NgapMessageType messageType)
return 40;
case NgapMessageType::UEContextReleaseCommand:
case NgapMessageType::UEContextReleaseComplete:
case NgapMessageType::UEContextReleaseRequest:
return 41;
case NgapMessageType::UEContextReleaseRequest:
return 42;
case NgapMessageType::UERadioCapabilityCheckRequest:
case NgapMessageType::UERadioCapabilityCheckResponse:
case NgapMessageType::UERadioCapabilityInfoIndication:
......
......@@ -66,6 +66,10 @@ void GtpTask::onLoop()
handleSessionCreate(w->resource);
break;
}
case NwGnbNgapToGtp::UE_CONTEXT_RELEASE: {
handleUeContextDelete(w->ueId);
break;
}
}
break;
}
......@@ -119,6 +123,34 @@ void GtpTask::handleSessionCreate(PduSessionResource *session)
updateAmbrForSession(sessionInd);
}
void GtpTask::handleUeContextDelete(int ueId)
{
// Find PDU sessions of the UE
std::vector<uint64_t> sessions{};
m_sessionTree.enumerateByUe(ueId, sessions);
for (auto &session : sessions)
{
// Remove all session information from rate limiter
m_rateLimiter->updateSessionUplinkLimit(session, 0);
m_rateLimiter->updateUeDownlinkLimit(session, 0);
// And remove from PDU session table
int teid = m_pduSessions[session]->downTunnel.teid;
m_pduSessions.erase(session);
// And remove from the tree
m_sessionTree.remove(session, teid);
}
// Remove all user information from rate limiter
m_rateLimiter->updateUeUplinkLimit(ueId, 0);
m_rateLimiter->updateUeDownlinkLimit(ueId, 0);
// Remove UE context
m_ueContexts.erase(ueId);
}
void GtpTask::handleUplinkData(int ueId, int psi, OctetString &&pdu)
{
const uint8_t *data = pdu.data();
......
......@@ -48,6 +48,7 @@ class GtpTask : public NtsTask
void handleUdpReceive(const udp::NwUdpServerReceive &msg);
void handleUeContextUpdate(const GtpUeContextUpdate &msg);
void handleSessionCreate(PduSessionResource *session);
void handleUeContextDelete(int ueId);
void handleUplinkData(int ueId, int psi, OctetString &&data);
void updateAmbrForUe(int ueId);
......
......@@ -57,6 +57,16 @@ void PduSessionTree::remove(uint64_t session, uint32_t downTeid)
}
}
void PduSessionTree::enumerateByUe(int ue, std::vector<uint64_t> &output)
{
if (mapByUeId.count(ue) == 0)
return;
auto &map = mapByUeId[ue];
for (auto &item : map)
output.push_back(item.second);
}
TokenBucket::TokenBucket(long byteCapacity) : byteCapacity(byteCapacity)
{
if (byteCapacity > 0)
......
......@@ -11,6 +11,7 @@
#include <gnb/types.hpp>
#include <memory>
#include <unordered_map>
#include <vector>
namespace nr::gnb
{
......@@ -52,6 +53,7 @@ class PduSessionTree
uint64_t findByDownTeid(uint32_t teid);
uint64_t findBySessionId(int ue, int psi);
void remove(uint64_t session, uint32_t downTeid);
void enumerateByUe(int ue, std::vector<uint64_t>& output);
};
class TokenBucket
......
......@@ -8,9 +8,9 @@
#include "task.hpp"
#include "rls.hpp"
#include <gnb/gtp/task.hpp>
#include <gnb/nts.hpp>
#include <gnb/rrc/task.hpp>
#include <gnb/gtp/task.hpp>
#include <utils/constants.hpp>
#include <utils/libc_error.hpp>
......@@ -105,6 +105,10 @@ void GnbMrTask::onLoop()
m_rlsEntity->setAcceptConnections(true);
break;
}
case NwGnbRrcToMr::AN_RELEASE: {
m_rlsEntity->localReleaseConnection(w->ueId, rls::ECause::RRC_RELEASE);
break;
}
}
break;
}
......@@ -150,6 +154,15 @@ void GnbMrTask::onUeConnected(int ue, const std::string &name)
void GnbMrTask::onUeReleased(int ue, rls::ECause cause)
{
if (rls::IsRlf(cause))
{
m_logger->err("Radio link failure for UE[%d] with cause[%s]", ue, rls::CauseToString(cause));
auto *w = new NwGnbMrToRrc(NwGnbMrToRrc::RADIO_LINK_FAILURE);
w->ueId = ue;
m_base->rrcTask->push(w);
}
m_ueMap.erase(ue);
m_logger->info("A UE disconnected from gNB. Total number of UEs is now: %d", m_ueMap.size());
}
......
......@@ -9,6 +9,9 @@
#include "task.hpp"
#include "utils.hpp"
#include <gnb/gtp/task.hpp>
#include <gnb/rrc/task.hpp>
#include <asn/ngap/ASN_NGAP_AMF-UE-NGAP-ID.h>
#include <asn/ngap/ASN_NGAP_InitialContextSetupRequest.h>
#include <asn/ngap/ASN_NGAP_InitialContextSetupResponse.h>
......@@ -22,8 +25,8 @@
#include <asn/ngap/ASN_NGAP_UEContextModificationResponse.h>
#include <asn/ngap/ASN_NGAP_UEContextReleaseCommand.h>
#include <asn/ngap/ASN_NGAP_UEContextReleaseComplete.h>
#include <asn/ngap/ASN_NGAP_UEContextReleaseRequest.h>
#include <asn/ngap/ASN_NGAP_UESecurityCapabilities.h>
#include <gnb/gtp/task.hpp>
namespace nr::gnb
{
......@@ -63,8 +66,15 @@ void NgapTask::receiveContextRelease(int amfId, ASN_NGAP_UEContextReleaseCommand
if (ue == nullptr)
return;
// todo: NG-RAN node shall release all related signalling and user data transport resources
// ...
// Notify RRC task
auto *w1 = new NwGnbNgapToRrc(NwGnbNgapToRrc::AN_RELEASE);
w1->ueId = ue->ctxId;
m_base->rrcTask->push(w1);
// Notify GTP task
auto *w2 = new NwGnbNgapToGtp(NwGnbNgapToGtp::UE_CONTEXT_RELEASE);
w2->ueId = ue->ctxId;
m_base->gtpTask->push(w2);
auto *response = asn::ngap::NewMessagePdu<ASN_NGAP_UEContextReleaseComplete>({});
sendNgapUeAssociated(ue->ctxId, response);
......@@ -103,4 +113,18 @@ void NgapTask::receiveContextModification(int amfId, ASN_NGAP_UEContextModificat
m_base->gtpTask->push(w);
}
void NgapTask::sendContextRelease(int ueId, NgapCause cause)
{
m_logger->debug("Sending UE Context release request (NG-RAN node initiated)");
auto *ieCause = asn::New<ASN_NGAP_UEContextReleaseRequest_IEs>();
ieCause->id = ASN_NGAP_ProtocolIE_ID_id_Cause;
ieCause->criticality = ASN_NGAP_Criticality_ignore;
ieCause->value.present = ASN_NGAP_UEContextReleaseRequest_IEs__value_PR_Cause;
ngap_utils::ToCauseAsn_Ref(cause, ieCause->value.choice.Cause);
auto *pdu = asn::ngap::NewMessagePdu<ASN_NGAP_UEContextReleaseRequest>({ieCause});
sendNgapUeAssociated(ueId, pdu);
}
} // namespace nr::gnb
\ No newline at end of file
//
// This file is a part of UERANSIM open source project.
// Copyright (c) 2021 ALİ GÜNGÖR.
//
// The software and all associated files are licensed under GPL-3.0
// and subject to the terms and conditions defined in LICENSE file.
//
#include "encode.hpp"
#include "task.hpp"
#include "utils.hpp"
#include <gnb/gtp/task.hpp>
#include <gnb/rrc/task.hpp>
namespace nr::gnb
{
void NgapTask::handleRadioLinkFailure(int ueId)
{
// Notify GTP task
auto *w2 = new NwGnbNgapToGtp(NwGnbNgapToGtp::UE_CONTEXT_RELEASE);
w2->ueId = ueId;
m_base->gtpTask->push(w2);
// Notify AMF
sendContextRelease(ueId, NgapCause::RadioNetwork_radio_connection_with_ue_lost);
}
} // namespace nr::gnb
......@@ -60,6 +60,10 @@ void NgapTask::onLoop()
handleUplinkNasTransport(w->ueId, w->pdu);
break;
}
case NwGnbRrcToNgap::RADIO_LINK_FAILURE: {
handleRadioLinkFailure(w->ueId);
break;
}
}
break;
}
......
......@@ -108,10 +108,14 @@ class NgapTask : public NtsTask
void receiveInitialContextSetup(int amfId, ASN_NGAP_InitialContextSetupRequest *msg);
void receiveContextRelease(int amfId, ASN_NGAP_UEContextReleaseCommand *msg);
void receiveContextModification(int amfId, ASN_NGAP_UEContextModificationRequest *msg);
void sendContextRelease(int ueId, NgapCause cause);
/* NAS Node Selection */
NgapAmfContext *selectAmf(int ueId);
NgapAmfContext *selectNewAmfForReAllocation(int ueId, int initiatedAmfId, int amfSetId);
/* Radio resource control */
void handleRadioLinkFailure(int ueId);
};
} // namespace nr::gnb
\ No newline at end of file
......@@ -28,11 +28,15 @@ struct NwGnbMrToRrc : NtsMessage
{
enum PR
{
RRC_PDU_DELIVERY
RRC_PDU_DELIVERY,
RADIO_LINK_FAILURE,
} present;
// RRC_PDU_DELIVERY
// RADIO_LINK_FAILURE
int ueId{};
// RRC_PDU_DELIVERY
rrc::RrcChannel channel{};
OctetString pdu{};
......@@ -46,11 +50,15 @@ struct NwGnbRrcToMr : NtsMessage
enum PR
{
NGAP_LAYER_INITIALIZED,
RRC_PDU_DELIVERY
RRC_PDU_DELIVERY,
AN_RELEASE,
} present;
// RRC_PDU_DELIVERY
// AN_RELEASE
int ueId{};
// RRC_PDU_DELIVERY
rrc::RrcChannel channel{};
OctetString pdu{};
......@@ -65,10 +73,14 @@ struct NwGnbNgapToRrc : NtsMessage
{
NGAP_LAYER_INITIALIZED,
NAS_DELIVERY,
AN_RELEASE,
} present;
// NAS_DELIVERY
// AN_RELEASE
int ueId{};
// NAS_DELIVERY
OctetString pdu{};
explicit NwGnbNgapToRrc(PR present) : NtsMessage(NtsMessageType::GNB_NGAP_TO_RRC), present(present)
......@@ -81,12 +93,17 @@ struct NwGnbRrcToNgap : NtsMessage
enum PR
{
INITIAL_NAS_DELIVERY,
UPLINK_NAS_DELIVERY
UPLINK_NAS_DELIVERY,
RADIO_LINK_FAILURE
} present;
// INITIAL_NAS_DELIVERY
// UPLINK_NAS_DELIVERY
// RADIO_LINK_FAILURE
int ueId{};
// INITIAL_NAS_DELIVERY
// UPLINK_NAS_DELIVERY
OctetString pdu{};
// INITIAL_NAS_DELIVERY
......@@ -102,7 +119,8 @@ struct NwGnbNgapToGtp : NtsMessage
enum PR
{
UE_CONTEXT_UPDATE,
SESSION_CREATE
SESSION_CREATE,
UE_CONTEXT_RELEASE,
} present;
// UE_CONTEXT_UPDATE
......@@ -111,6 +129,9 @@ struct NwGnbNgapToGtp : NtsMessage
// SESSION_CREATE
PduSessionResource *resource{};
// UE_CONTEXT_RELEASE
int ueId{};
explicit NwGnbNgapToGtp(PR present) : NtsMessage(NtsMessageType::GNB_NGAP_TO_GTP), present(present)
{
}
......
......@@ -7,6 +7,11 @@
//
#include "task.hpp"
#include <gnb/mr/task.hpp>
#include <gnb/ngap/task.hpp>
#include <rrc/encode.hpp>
#include <asn/rrc/ASN_RRC_BCCH-BCH-Message.h>
#include <asn/rrc/ASN_RRC_BCCH-DL-SCH-Message.h>
#include <asn/rrc/ASN_RRC_CellGroupConfig.h>
......@@ -15,6 +20,8 @@
#include <asn/rrc/ASN_RRC_DLInformationTransfer-IEs.h>
#include <asn/rrc/ASN_RRC_DLInformationTransfer.h>
#include <asn/rrc/ASN_RRC_PCCH-Message.h>
#include <asn/rrc/ASN_RRC_RRCRelease-IEs.h>
#include <asn/rrc/ASN_RRC_RRCRelease.h>
#include <asn/rrc/ASN_RRC_RRCSetup-IEs.h>
#include <asn/rrc/ASN_RRC_RRCSetup.h>
#include <asn/rrc/ASN_RRC_RRCSetupComplete-IEs.h>
......@@ -25,8 +32,6 @@
#include <asn/rrc/ASN_RRC_UL-DCCH-Message.h>
#include <asn/rrc/ASN_RRC_ULInformationTransfer-IEs.h>
#include <asn/rrc/ASN_RRC_ULInformationTransfer.h>
#include <gnb/ngap/task.hpp>
#include <rrc/encode.hpp>
namespace nr::gnb
{
......@@ -134,4 +139,40 @@ void GnbRrcTask::receiveRrcSetupComplete(int ueId, const ASN_RRC_RRCSetupComplet
m_base->ngapTask->push(w);
}
void GnbRrcTask::releaseConnection(int ueId)
{
m_logger->debug("Releasing RRC connection for UE[%d]", ueId);
// Send RRC Release message
auto *pdu = asn::New<ASN_RRC_DL_DCCH_Message>();
pdu->message.present = ASN_RRC_DL_DCCH_MessageType_PR_c1;
pdu->message.choice.c1 = asn::NewFor(pdu->message.choice.c1);
pdu->message.choice.c1->present = ASN_RRC_DL_DCCH_MessageType__c1_PR_rrcRelease;
auto &rrcRelease = pdu->message.choice.c1->choice.rrcRelease = asn::New<ASN_RRC_RRCRelease>();
rrcRelease->rrc_TransactionIdentifier = getNextTid();
rrcRelease->criticalExtensions.present = ASN_RRC_RRCRelease__criticalExtensions_PR_rrcRelease;
rrcRelease->criticalExtensions.choice.rrcRelease = asn::New<ASN_RRC_RRCRelease_IEs>();
sendRrcMessage(ueId, pdu);
// Notify MR task
auto *w = new NwGnbRrcToMr(NwGnbRrcToMr::AN_RELEASE);
w->ueId = ueId;
m_base->mrTask->push(w);
// Delete UE RRC context
m_ueCtx.erase(ueId);
}
void GnbRrcTask::handleRadioLinkFailure(int ueId)
{
// Notify NGAP task
auto *w = new NwGnbRrcToNgap(NwGnbRrcToNgap::RADIO_LINK_FAILURE);
w->ueId = ueId;
m_base->ngapTask->push(w);
// Delete UE RRC context
m_ueCtx.erase(ueId);
}
} // namespace nr::gnb
\ No newline at end of file
......@@ -46,6 +46,10 @@ void GnbRrcTask::onLoop()
handleUplinkRrc(w->ueId, w->channel, w->pdu);
break;
}
case NwGnbMrToRrc::RADIO_LINK_FAILURE: {
handleRadioLinkFailure(w->ueId);
break;
}
}
break;
}
......@@ -61,6 +65,10 @@ void GnbRrcTask::onLoop()
handleDownlinkNasDelivery(w->ueId, w->pdu);
break;
}
case NwGnbNgapToRrc::AN_RELEASE: {
releaseConnection(w->ueId);
break;
}
}
break;
}
......
......@@ -69,6 +69,8 @@ class GnbRrcTask : public NtsTask
void handleUplinkRrc(int ueId, rrc::RrcChannel channel, const OctetString &rrcPdu);
void handleDownlinkNasDelivery(int ueId, const OctetString &nasPdu);
void deliverUplinkNas(int ueId, OctetString &&nasPdu);
void releaseConnection(int ueId);
void handleRadioLinkFailure(int ueId);
void receiveUplinkInformationTransfer(int ueId, const ASN_RRC_ULInformationTransfer &msg);
void receiveRrcSetupRequest(int ueId, const ASN_RRC_RRCSetupRequest &msg);
......
......@@ -70,6 +70,11 @@ void RlsGnbEntity::releaseConnection(int ue, ECause cause)
removeUe(ue, cause);
}
void RlsGnbEntity::localReleaseConnection(int ue, ECause cause)
{
removeUe(ue, cause);
}
void RlsGnbEntity::sendRlsMessage(int ue, const RlsMessage &msg)
{
OctetString buf{};
......@@ -107,6 +112,9 @@ void RlsGnbEntity::sendReleaseIndication(int ue, ECause cause)
void RlsGnbEntity::removeUe(int ue, ECause cause)
{
if (idUeMap.count(ue) == 0)
return;
uint64_t ueToken = idUeMap[ue];
ueIdMap.erase(ueToken);
idUeMap.erase(ue);
......
......@@ -10,10 +10,10 @@
#include "rls.hpp"
#include <utils/network.hpp>
#include <optional>
#include <set>
#include <unordered_map>
#include <utils/network.hpp>
namespace rls
{
......@@ -50,6 +50,7 @@ class RlsGnbEntity
void downlinkPayloadDelivery(int ue, EPayloadType type, OctetString &&payload);
void setAcceptConnections(bool accept);
void releaseConnection(int ue, ECause cause);
void localReleaseConnection(int ue, ECause cause);
private:
void sendReleaseIndication(int ue, ECause cause);
......
......@@ -33,21 +33,13 @@ DecodeRes Decode(const OctetView &stream, RlsMessage &output, octet3 appVersion)
output.msgType != EMessageType::RLS_PAYLOAD_TRANSPORT && output.msgType != EMessageType::RLS_SETUP_RESPONSE)
return DecodeRes::FAILURE;
output.ueToken = stream.read8UL();
if (output.msgType != EMessageType::RLS_SETUP_REQUEST)
output.gnbToken = stream.read8UL();
if (output.msgType == EMessageType::RLS_PAYLOAD_TRANSPORT)
{
output.payloadType = static_cast<EPayloadType>(stream.readI());
uint16_t len = stream.read2US();
output.payload = stream.readOctetString(len);
}
if (output.msgType == EMessageType::RLS_SETUP_FAILURE)
output.cause = static_cast<ECause>(stream.readI());
if (output.msgType == EMessageType::RLS_SETUP_RESPONSE || output.msgType == EMessageType::RLS_SETUP_COMPLETE)
{
uint16_t len = stream.read2US();
len = stream.read2US();
output.str = stream.readUtf8String(len);
}
return DecodeRes::OK;
}
......@@ -67,30 +59,23 @@ bool Encode(const RlsMessage &msg, OctetString &stream)
}
if (msg.msgCls == EMessageClass::NORMAL_MESSAGE)
{
stream.appendOctet3(msg.appVersion);
stream.appendOctet(static_cast<int>(msg.msgType));
stream.appendOctet8(msg.ueToken);
if (msg.msgType != EMessageType::RLS_SETUP_REQUEST && msg.msgType != EMessageType::RLS_SETUP_COMPLETE &&
msg.msgType != EMessageType::RLS_SETUP_FAILURE && msg.msgType != EMessageType::RLS_HEARTBEAT &&
msg.msgType != EMessageType::RLS_RELEASE_INDICATION && msg.msgType != EMessageType::RLS_PAYLOAD_TRANSPORT &&
msg.msgType != EMessageType::RLS_SETUP_RESPONSE)
return false;
if (msg.msgType != EMessageType::RLS_SETUP_REQUEST)
stream.appendOctet3(msg.appVersion);
stream.appendOctet(static_cast<int>(msg.msgType));
stream.appendOctet8(msg.ueToken);
stream.appendOctet8(msg.gnbToken);
if (msg.msgType == EMessageType::RLS_SETUP_FAILURE)
stream.appendOctet(static_cast<int>(msg.cause));
if (msg.msgType == EMessageType::RLS_PAYLOAD_TRANSPORT)
{
stream.appendOctet(static_cast<int>(msg.payloadType));
stream.appendOctet2(msg.payload.length());
stream.append(msg.payload);
}
if (msg.msgType == EMessageType::RLS_SETUP_RESPONSE || msg.msgType == EMessageType::RLS_SETUP_COMPLETE)
{
stream.appendOctet(static_cast<int>(msg.cause));
stream.appendOctet2(msg.str.length());
for (char c : msg.str)
stream.appendOctet(c);
}
stream.appendUtf8(msg.str);
return true;
}
return false;
......@@ -101,15 +86,17 @@ const char *CauseToString(ECause cause)
switch (cause)
{
case ECause::UNSPECIFIED:
return "RLS_UNSPECIFIED";
return "RLS-UNSPECIFIED";
case ECause::TOKEN_CONFLICT:
return "RLS_TOKEN_CONFLICT";
return "RLS-TOKEN-CONFLICT";
case ECause::EMPTY_SEARCH_LIST:
return "RLS_EMPTY_SEARCH_LIST";
return "RLS-EMPTY-SEARCH-LIST";
case ECause::SETUP_TIMEOUT:
return "RLS_SETUP_TIMEOUT";
return "RLS-SETUP-TIMEOUT";
case ECause::HEARTBEAT_TIMEOUT:
return "RLS_HEARTBEAT_TIMEOUT";
return "RLS-HEARTBEAT-TIMEOUT";
case ECause::RRC_RELEASE:
return "RLS-RRC-RELEASE";
default:
return "?";
}
......
......@@ -45,13 +45,23 @@ enum class EMessageType : uint8_t
enum class ECause : uint8_t
{
// Error causes (treated as radio link failure)
UNSPECIFIED = 0,
TOKEN_CONFLICT,
EMPTY_SEARCH_LIST,
SETUP_TIMEOUT,
HEARTBEAT_TIMEOUT
HEARTBEAT_TIMEOUT,
// Successful causes
RRC_RELEASE,
};
// Checks if the cause treated as radio link failure
inline bool IsRlf(ECause cause)
{
return cause != ECause::RRC_RELEASE;
}
enum class EPayloadType : uint8_t
{
RRC,
......
......@@ -15,6 +15,11 @@ void OctetString::append(const OctetString &v)
m_data.insert(m_data.end(), v.m_data.begin(), v.m_data.end());
}
void OctetString::appendUtf8(const std::string &v)
{
m_data.insert(m_data.end(), v.begin(), v.end());
}
void OctetString::appendOctet(uint8_t v)
{
m_data.push_back(v);
......
......@@ -35,6 +35,7 @@ class OctetString
public:
void append(const OctetString &v);
void appendUtf8(const std::string &v);
void appendOctet(uint8_t v);
void appendOctet(int v);
void appendOctet(int bigHalf, int littleHalf);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment