diff --git a/README.md b/README.md
index 997f6a63b..29e44673c 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
-
+
diff --git a/config/open5gs-gnb.yaml b/config/open5gs-gnb.yaml
index 88e210dfe..d825a80c6 100644
--- a/config/open5gs-gnb.yaml
+++ b/config/open5gs-gnb.yaml
@@ -17,7 +17,6 @@ amfConfigs:
# List of supported S-NSSAIs by this gNB
slices:
- sst: 1
- sd: 1
# Indicates whether or not SCTP stream number errors should be ignored.
ignoreStreamIds: true
diff --git a/config/open5gs-ue.yaml b/config/open5gs-ue.yaml
index 7c1761793..d6779ed65 100644
--- a/config/open5gs-ue.yaml
+++ b/config/open5gs-ue.yaml
@@ -34,7 +34,6 @@ sessions:
# Configured NSSAI for this UE by HPLMN
configured-nssai:
- sst: 1
- sd: 1
# Default Configured NSSAI for this UE
default-nssai:
diff --git a/src/app/cli_cmd.cpp b/src/app/cli_cmd.cpp
index 5a1213c4b..f8ec97502 100644
--- a/src/app/cli_cmd.cpp
+++ b/src/app/cli_cmd.cpp
@@ -150,6 +150,7 @@ static OrderedMap g_gnbCmdEntries = {
{"amf-info", {"Show some status information about the given AMF", "", DefaultDesc, true}},
{"ue-list", {"List all UEs associated with the gNB", "", DefaultDesc, false}},
{"ue-count", {"Print the total number of UEs connected the this gNB", "", DefaultDesc, false}},
+ {"ue-release", {"Request a UE context release for the given UE", "", DefaultDesc, false}},
};
static OrderedMap g_ueCmdEntries = {
@@ -162,6 +163,7 @@ static OrderedMap g_ueCmdEntries = {
{"ps-release-all", {"Trigger PDU session release procedures for all active sessions", "", DefaultDesc, false}},
{"deregister",
{"Perform a de-registration by the UE", "", DefaultDesc, true}},
+ {"coverage", {"Show gNodeB cell coverage information", "", DefaultDesc, false}},
};
static std::unique_ptr GnbCliParseImpl(const std::string &subCmd, const opt::OptionsResult &options,
@@ -199,6 +201,18 @@ static std::unique_ptr GnbCliParseImpl(const std::string &subCmd,
{
return std::make_unique(GnbCliCommand::UE_COUNT);
}
+ else if (subCmd == "ue-release")
+ {
+ auto cmd = std::make_unique(GnbCliCommand::UE_RELEASE_REQ);
+ if (options.positionalCount() == 0)
+ CMD_ERR("UE ID is expected")
+ if (options.positionalCount() > 1)
+ CMD_ERR("Only one UE ID is expected")
+ cmd->ueId = utils::ParseInt(options.getPositional(0));
+ if (cmd->ueId <= 0)
+ CMD_ERR("Invalid UE ID")
+ return cmd;
+ }
return nullptr;
}
@@ -301,6 +315,10 @@ static std::unique_ptr UeCliParseImpl(const std::string &subCmd, c
}
return cmd;
}
+ else if (subCmd == "coverage")
+ {
+ return std::make_unique(UeCliCommand::COVERAGE);
+ }
return nullptr;
}
diff --git a/src/app/cli_cmd.hpp b/src/app/cli_cmd.hpp
index 45b7d2e96..b980c675a 100644
--- a/src/app/cli_cmd.hpp
+++ b/src/app/cli_cmd.hpp
@@ -26,12 +26,16 @@ struct GnbCliCommand
AMF_LIST,
AMF_INFO,
UE_LIST,
- UE_COUNT
+ UE_COUNT,
+ UE_RELEASE_REQ,
} present;
// AMF_INFO
int amfId{};
+ // UE_RELEASE_REQ
+ int ueId{};
+
explicit GnbCliCommand(PR present) : present(present)
{
}
@@ -48,6 +52,7 @@ struct UeCliCommand
PS_RELEASE,
PS_RELEASE_ALL,
DE_REGISTER,
+ COVERAGE,
} present;
// DE_REGISTER
diff --git a/src/asn/utils/utils.hpp b/src/asn/utils/utils.hpp
index 8bd4c23c4..22cef52ba 100644
--- a/src/asn/utils/utils.hpp
+++ b/src/asn/utils/utils.hpp
@@ -198,4 +198,12 @@ inline Unique WrapUnique(T *ptr, asn_TYPE_descriptor_t &desc)
return asn::Unique(ptr, asn::Deleter{desc});
}
+template
+inline Unique UniqueCopy(const T &value, asn_TYPE_descriptor_t &desc)
+{
+ auto *ptr = New();
+ DeepCopy(desc, value, ptr);
+ return WrapUnique(ptr, desc);
+}
+
} // namespace asn
\ No newline at end of file
diff --git a/src/gnb/app/cmd_handler.cpp b/src/gnb/app/cmd_handler.cpp
index 995f8576e..a3b94ccde 100644
--- a/src/gnb/app/cmd_handler.cpp
+++ b/src/gnb/app/cmd_handler.cpp
@@ -10,8 +10,8 @@
#include
#include
-#include
#include
+#include
#include
#include
#include
@@ -36,7 +36,7 @@ void GnbCmdHandler::sendError(const InetAddress &address, const std::string &out
void GnbCmdHandler::pauseTasks()
{
m_base->gtpTask->requestPause();
- m_base->mrTask->requestPause();
+ m_base->rlsTask->requestPause();
m_base->ngapTask->requestPause();
m_base->rrcTask->requestPause();
m_base->sctpTask->requestPause();
@@ -45,7 +45,7 @@ void GnbCmdHandler::pauseTasks()
void GnbCmdHandler::unpauseTasks()
{
m_base->gtpTask->requestUnpause();
- m_base->mrTask->requestUnpause();
+ m_base->rlsTask->requestUnpause();
m_base->ngapTask->requestUnpause();
m_base->rrcTask->requestUnpause();
m_base->sctpTask->requestUnpause();
@@ -55,7 +55,7 @@ bool GnbCmdHandler::isAllPaused()
{
if (!m_base->gtpTask->isPauseConfirmed())
return false;
- if (!m_base->mrTask->isPauseConfirmed())
+ if (!m_base->rlsTask->isPauseConfirmed())
return false;
if (!m_base->ngapTask->isPauseConfirmed())
return false;
@@ -131,7 +131,7 @@ void GnbCmdHandler::handleCmdImpl(NwGnbCliCommand &msg)
for (auto &ue : m_base->ngapTask->m_ueCtx)
{
json.push(Json::Obj({
- {"ue-name", m_base->mrTask->m_ueMap[ue.first].name},
+ {"ue-id", ue.first},
{"ran-ngap-id", ue.second->ranUeNgapId},
{"amf-ngap-id", ue.second->amfUeNgapId},
}));
@@ -143,6 +143,17 @@ void GnbCmdHandler::handleCmdImpl(NwGnbCliCommand &msg)
sendResult(msg.address, std::to_string(m_base->ngapTask->m_ueCtx.size()));
break;
}
+ case app::GnbCliCommand::UE_RELEASE_REQ: {
+ if (m_base->ngapTask->m_ueCtx.count(msg.cmd->ueId) == 0)
+ sendError(msg.address, "UE not found with given ID");
+ else
+ {
+ auto ue = m_base->ngapTask->m_ueCtx[msg.cmd->ueId];
+ m_base->ngapTask->sendContextRelease(ue->ctxId, NgapCause::RadioNetwork_unspecified);
+ sendResult(msg.address, "Requesting UE context release");
+ }
+ break;
+ }
}
}
diff --git a/src/gnb/gnb.cpp b/src/gnb/gnb.cpp
index a277bc437..d63330db0 100644
--- a/src/gnb/gnb.cpp
+++ b/src/gnb/gnb.cpp
@@ -9,13 +9,12 @@
#include "gnb.hpp"
#include "app/task.hpp"
#include "gtp/task.hpp"
-#include "mr/task.hpp"
+#include "rls/task.hpp"
#include "ngap/task.hpp"
#include "rrc/task.hpp"
#include "sctp/task.hpp"
#include
-#include
namespace nr::gnb
{
@@ -33,7 +32,7 @@ GNodeB::GNodeB(GnbConfig *config, app::INodeListener *nodeListener, NtsTask *cli
base->ngapTask = new NgapTask(base);
base->rrcTask = new GnbRrcTask(base);
base->gtpTask = new GtpTask(base);
- base->mrTask = new GnbMrTask(base);
+ base->rlsTask = new GnbRlsTask(base);
taskBase = base;
}
@@ -45,14 +44,14 @@ GNodeB::~GNodeB()
taskBase->ngapTask->quit();
taskBase->rrcTask->quit();
taskBase->gtpTask->quit();
- taskBase->mrTask->quit();
+ taskBase->rlsTask->quit();
delete taskBase->appTask;
delete taskBase->sctpTask;
delete taskBase->ngapTask;
delete taskBase->rrcTask;
delete taskBase->gtpTask;
- delete taskBase->mrTask;
+ delete taskBase->rlsTask;
delete taskBase->logBase;
@@ -65,7 +64,7 @@ void GNodeB::start()
taskBase->sctpTask->start();
taskBase->ngapTask->start();
taskBase->rrcTask->start();
- taskBase->mrTask->start();
+ taskBase->rlsTask->start();
taskBase->gtpTask->start();
}
diff --git a/src/gnb/gtp/task.cpp b/src/gnb/gtp/task.cpp
index e6557e3d0..a061b14f1 100644
--- a/src/gnb/gtp/task.cpp
+++ b/src/gnb/gtp/task.cpp
@@ -10,7 +10,7 @@
#include
#include
-#include
+#include
#include
#include
@@ -76,12 +76,12 @@ void GtpTask::onLoop()
}
break;
}
- case NtsMessageType::GNB_MR_TO_GTP: {
- auto *w = dynamic_cast(msg);
+ case NtsMessageType::GNB_RLS_TO_GTP: {
+ auto *w = dynamic_cast(msg);
switch (w->present)
{
- case NwGnbMrToGtp::UPLINK_DELIVERY: {
- handleUplinkData(w->ueId, w->pduSessionId, std::move(w->data));
+ case NwGnbRlsToGtp::DATA_PDU_DELIVERY: {
+ handleUplinkData(w->ueId, w->psi, std::move(w->pdu));
break;
}
}
@@ -239,11 +239,11 @@ void GtpTask::handleUdpReceive(const udp::NwUdpServerReceive &msg)
if (m_rateLimiter->allowDownlinkPacket(sessionInd, gtp->payload.length()))
{
- auto *w = new NwGnbGtpToMr(NwGnbGtpToMr::DATA_PDU_DELIVERY);
+ auto *w = new NwGnbGtpToRls(NwGnbGtpToRls::DATA_PDU_DELIVERY);
w->ueId = GetUeId(sessionInd);
- w->pduSessionId = GetPsi(sessionInd);
- w->data = std::move(gtp->payload);
- m_base->mrTask->push(w);
+ w->psi = GetPsi(sessionInd);
+ w->pdu = std::move(gtp->payload);
+ m_base->rlsTask->push(w);
}
delete gtp;
diff --git a/src/gnb/mr/rls.cpp b/src/gnb/mr/rls.cpp
deleted file mode 100644
index 36aa37aa4..000000000
--- a/src/gnb/mr/rls.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-//
-// 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 "rls.hpp"
-#include
-
-namespace nr::gnb
-{
-
-GnbRls::GnbRls(std::string nodeName, std::unique_ptr logger, NtsTask *targetTask)
- : RlsGnbEntity(std::move(nodeName)), m_logger(std::move(logger)), m_targetTask(targetTask)
-{
-}
-
-void GnbRls::logWarn(const std::string &msg)
-{
- m_logger->warn(msg);
-}
-
-void GnbRls::logError(const std::string &msg)
-{
- m_logger->err(msg);
-}
-
-void GnbRls::onUeConnected(int ue, std::string name)
-{
- auto *w = new NwGnbMrToMr(NwGnbMrToMr::UE_CONNECTED);
- w->ue = ue;
- w->name = std::move(name);
- m_targetTask->push(w);
-}
-
-void GnbRls::onUeReleased(int ue, rls::ECause cause)
-{
- auto *w = new NwGnbMrToMr(NwGnbMrToMr::UE_RELEASED);
- w->ue = ue;
- w->cause = cause;
- m_targetTask->push(w);
-}
-
-void GnbRls::deliverUplinkPayload(int ue, rls::EPayloadType type, OctetString &&payload)
-{
- auto *w = new NwGnbMrToMr(NwGnbMrToMr::RECEIVE_OVER_UDP);
- w->ue = ue;
- w->type = type;
- w->pdu = std::move(payload);
- m_targetTask->push(w);
-}
-
-void GnbRls::sendRlsPdu(const InetAddress &address, OctetString &&pdu)
-{
- auto *w = new NwGnbMrToMr(NwGnbMrToMr::SEND_OVER_UDP);
- w->address = address;
- w->pdu = std::move(pdu);
- m_targetTask->push(w);
-}
-
-} // namespace nr::gnb
diff --git a/src/gnb/mr/rls.hpp b/src/gnb/mr/rls.hpp
deleted file mode 100644
index 823d3451c..000000000
--- a/src/gnb/mr/rls.hpp
+++ /dev/null
@@ -1,36 +0,0 @@
-//
-// 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.
-//
-
-#pragma once
-
-#include
-#include
-#include
-
-namespace nr::gnb
-{
-
-class GnbRls : public rls::RlsGnbEntity
-{
- private:
- std::unique_ptr m_logger;
- NtsTask *m_targetTask;
-
- public:
- explicit GnbRls(std::string nodeName, std::unique_ptr logger, NtsTask *targetTask);
-
- protected:
- void logWarn(const std::string &msg) override;
- void logError(const std::string &msg) override;
- void onUeConnected(int ue, std::string name) override;
- void onUeReleased(int ue, rls::ECause cause) override;
- void sendRlsPdu(const InetAddress &address, OctetString &&pdu) override;
- void deliverUplinkPayload(int ue, rls::EPayloadType type, OctetString &&payload) override;
-};
-
-} // namespace nr::gnb
diff --git a/src/gnb/mr/task.cpp b/src/gnb/mr/task.cpp
deleted file mode 100644
index 4e7d54f56..000000000
--- a/src/gnb/mr/task.cpp
+++ /dev/null
@@ -1,193 +0,0 @@
-//
-// 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 "task.hpp"
-#include "rls.hpp"
-#include
-#include
-#include
-#include
-#include
-
-static const int TIMER_ID_RLS_HEARTBEAT = 1;
-
-namespace nr::gnb
-{
-
-GnbMrTask::GnbMrTask(TaskBase *base) : m_base{base}, m_udpTask{}, m_rlsEntity{}, m_ueMap{}
-{
- m_logger = m_base->logBase->makeUniqueLogger("mr");
-}
-
-void GnbMrTask::onStart()
-{
- m_rlsEntity = new GnbRls(m_base->config->name, m_base->logBase->makeUniqueLogger("rls"), this);
-
- try
- {
- m_udpTask = new udp::UdpServerTask(m_base->config->portalIp, cons::PortalPort, this);
- m_udpTask->start();
- }
- catch (const LibError &e)
- {
- m_logger->err("MR failure [%s]", e.what());
- quit();
- return;
- }
-
- setTimer(TIMER_ID_RLS_HEARTBEAT, rls::Constants::HB_PERIOD_UE_TO_GNB);
-}
-
-void GnbMrTask::onLoop()
-{
- NtsMessage *msg = take();
- if (!msg)
- return;
-
- switch (msg->msgType)
- {
- case NtsMessageType::GNB_MR_TO_MR: {
- auto *w = dynamic_cast(msg);
- switch (w->present)
- {
- case NwGnbMrToMr::UE_CONNECTED: {
- onUeConnected(w->ue, w->name);
- break;
- }
- case NwGnbMrToMr::UE_RELEASED: {
- onUeReleased(w->ue, w->cause);
- break;
- }
- case NwGnbMrToMr::SEND_OVER_UDP: {
- m_udpTask->send(w->address, w->pdu);
- break;
- }
- case NwGnbMrToMr::RECEIVE_OVER_UDP: {
- receiveUplinkPayload(w->ue, w->type, std::move(w->pdu));
- break;
- }
- }
- break;
- }
- case NtsMessageType::GNB_GTP_TO_MR: {
- auto *w = dynamic_cast(msg);
- switch (w->present)
- {
- case NwGnbGtpToMr::DATA_PDU_DELIVERY: {
- OctetString stream{};
- stream.appendOctet4(static_cast(w->pduSessionId));
- stream.append(w->data);
-
- m_rlsEntity->downlinkPayloadDelivery(w->ueId, rls::EPayloadType::DATA, std::move(stream));
- break;
- }
- }
- break;
- }
- case NtsMessageType::GNB_RRC_TO_MR: {
- auto *w = dynamic_cast(msg);
- switch (w->present)
- {
- case NwGnbRrcToMr::RRC_PDU_DELIVERY: {
- OctetString stream{};
- stream.appendOctet(static_cast(w->channel));
- stream.append(w->pdu);
-
- m_rlsEntity->downlinkPayloadDelivery(w->ueId, rls::EPayloadType::RRC, std::move(stream));
- break;
- }
- case NwGnbRrcToMr::NGAP_LAYER_INITIALIZED: {
- m_rlsEntity->setAcceptConnections(true);
- break;
- }
- case NwGnbRrcToMr::AN_RELEASE: {
- m_rlsEntity->localReleaseConnection(w->ueId, rls::ECause::RRC_NORMAL_RELEASE);
- break;
- }
- }
- break;
- }
- case NtsMessageType::TIMER_EXPIRED: {
- auto *w = dynamic_cast(msg);
- if (w->timerId == TIMER_ID_RLS_HEARTBEAT)
- {
- setTimer(TIMER_ID_RLS_HEARTBEAT, rls::Constants::HB_PERIOD_GNB_TO_UE);
- m_rlsEntity->onHeartbeat();
- }
- break;
- }
- case NtsMessageType::UDP_SERVER_RECEIVE: {
- auto *w = dynamic_cast(msg);
- m_rlsEntity->onReceive(w->fromAddress, w->packet);
- break;
- }
- default:
- m_logger->unhandledNts(msg);
- break;
- }
-
- delete msg;
-}
-
-void GnbMrTask::onQuit()
-{
- delete m_rlsEntity;
-
- if (m_udpTask != nullptr)
- m_udpTask->quit();
- delete m_udpTask;
-}
-
-void GnbMrTask::onUeConnected(int ue, const std::string &name)
-{
- m_ueMap[ue] = {};
- m_ueMap[ue].ueId = ue;
- m_ueMap[ue].name = name;
-
- m_logger->info("New UE connected to gNB. Total number of UEs [%d]", m_ueMap.size());
-}
-
-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 [%d]", m_ueMap.size());
-}
-
-void GnbMrTask::receiveUplinkPayload(int ue, rls::EPayloadType type, OctetString &&payload)
-{
- if (type == rls::EPayloadType::RRC)
- {
- auto *nw = new NwGnbMrToRrc(NwGnbMrToRrc::RRC_PDU_DELIVERY);
- nw->ueId = ue;
- nw->channel = static_cast(payload.getI(0));
- nw->pdu = payload.subCopy(1);
- m_base->rrcTask->push(nw);
- }
- else if (type == rls::EPayloadType::DATA)
- {
- int psi = payload.get4I(0);
- OctetString dataPayload = payload.subCopy(4);
-
- auto *w = new NwGnbMrToGtp(NwGnbMrToGtp::UPLINK_DELIVERY);
- w->ueId = ue;
- w->pduSessionId = psi;
- w->data = std::move(dataPayload);
- m_base->gtpTask->push(w);
- }
-}
-
-} // namespace nr::gnb
diff --git a/src/gnb/mr/task.hpp b/src/gnb/mr/task.hpp
deleted file mode 100644
index 184fcef86..000000000
--- a/src/gnb/mr/task.hpp
+++ /dev/null
@@ -1,53 +0,0 @@
-//
-// 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.
-//
-
-#pragma once
-
-#include "rls.hpp"
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-namespace nr::gnb
-{
-
-class GnbMrTask : public NtsTask
-{
- private:
- TaskBase *m_base;
- std::unique_ptr m_logger;
-
- udp::UdpServerTask *m_udpTask;
- GnbRls *m_rlsEntity;
- std::unordered_map m_ueMap;
-
- friend class GnbCmdHandler;
-
- public:
- explicit GnbMrTask(TaskBase *base);
- ~GnbMrTask() override = default;
-
- protected:
- void onStart() override;
- void onLoop() override;
- void onQuit() override;
-
- private:
- void onUeConnected(int ue, const std::string &name);
- void onUeReleased(int ue, rls::ECause cause);
- void receiveUplinkPayload(int ue, rls::EPayloadType type, OctetString &&payload);
-};
-
-} // namespace nr::gnb
\ No newline at end of file
diff --git a/src/gnb/ngap/interface.cpp b/src/gnb/ngap/interface.cpp
index 6b7ebab77..0f3002dc2 100644
--- a/src/gnb/ngap/interface.cpp
+++ b/src/gnb/ngap/interface.cpp
@@ -195,7 +195,7 @@ void NgapTask::receiveNgSetupResponse(int amfId, ASN_NGAP_NGSetupResponse *msg)
update->isNgapUp = true;
m_base->appTask->push(update);
- m_base->rrcTask->push(new NwGnbNgapToRrc(NwGnbNgapToRrc::NGAP_LAYER_INITIALIZED));
+ m_base->rrcTask->push(new NwGnbNgapToRrc(NwGnbNgapToRrc::RADIO_POWER_ON));
}
}
@@ -238,8 +238,8 @@ void NgapTask::sendErrorIndication(int amfId, NgapCause cause, int ueId)
ieCause->value.present = ASN_NGAP_ErrorIndicationIEs__value_PR_Cause;
ngap_utils::ToCauseAsn_Ref(cause, ieCause->value.choice.Cause);
- m_logger->debug("Sending an error indication with cause: %s",
- ngap_utils::CauseToString(ieCause->value.choice.Cause).c_str());
+ m_logger->warn("Sending an error indication with cause: %s",
+ ngap_utils::CauseToString(ieCause->value.choice.Cause).c_str());
auto *pdu = asn::ngap::NewMessagePdu({ieCause});
diff --git a/src/gnb/ngap/radio.cpp b/src/gnb/ngap/radio.cpp
index 5c2acf1f9..52fecfd3c 100644
--- a/src/gnb/ngap/radio.cpp
+++ b/src/gnb/ngap/radio.cpp
@@ -13,6 +13,9 @@
#include
#include
+#include
+#include
+
namespace nr::gnb
{
@@ -27,4 +30,30 @@ void NgapTask::handleRadioLinkFailure(int ueId)
sendContextRelease(ueId, NgapCause::RadioNetwork_radio_connection_with_ue_lost);
}
+void NgapTask::receivePaging(int amfId, ASN_NGAP_Paging *msg)
+{
+ m_logger->debug("Paging received");
+
+ auto *amf = findAmfContext(amfId);
+ if (amf == nullptr)
+ return;
+
+ auto *ieUePagingIdentity = asn::ngap::GetProtocolIe(msg, ASN_NGAP_ProtocolIE_ID_id_UEPagingIdentity);
+ auto *ieTaiListForPaging = asn::ngap::GetProtocolIe(msg, ASN_NGAP_ProtocolIE_ID_id_TAIListForPaging);
+
+ if (ieUePagingIdentity == nullptr || ieTaiListForPaging == nullptr ||
+ ieUePagingIdentity->UEPagingIdentity.present != ASN_NGAP_UEPagingIdentity_PR_fiveG_S_TMSI)
+ {
+ m_logger->err("Invalid parameters received in Paging message");
+ return;
+ }
+
+ auto *w = new NwGnbNgapToRrc(NwGnbNgapToRrc::PAGING);
+ w->uePagingTmsi =
+ asn::UniqueCopy(*ieUePagingIdentity->UEPagingIdentity.choice.fiveG_S_TMSI, asn_DEF_ASN_NGAP_FiveG_S_TMSI);
+ w->taiListForPaging = asn::UniqueCopy(ieTaiListForPaging->TAIListForPaging, asn_DEF_ASN_NGAP_TAIListForPaging);
+
+ m_base->rrcTask->push(w);
+}
+
} // namespace nr::gnb
diff --git a/src/gnb/ngap/task.hpp b/src/gnb/ngap/task.hpp
index dbcdc7fe6..4b52378d4 100644
--- a/src/gnb/ngap/task.hpp
+++ b/src/gnb/ngap/task.hpp
@@ -31,6 +31,7 @@ extern "C" struct ASN_NGAP_AMFConfigurationUpdate;
extern "C" struct ASN_NGAP_OverloadStart;
extern "C" struct ASN_NGAP_OverloadStop;
extern "C" struct ASN_NGAP_PDUSessionResourceReleaseCommand;
+extern "C" struct ASN_NGAP_Paging;
namespace nr::gnb
{
@@ -118,6 +119,7 @@ class NgapTask : public NtsTask
/* Radio resource control */
void handleRadioLinkFailure(int ueId);
+ void receivePaging(int amfId, ASN_NGAP_Paging *msg);
};
} // namespace nr::gnb
\ No newline at end of file
diff --git a/src/gnb/ngap/transport.cpp b/src/gnb/ngap/transport.cpp
index 788b5d9d3..36f7c1c6c 100644
--- a/src/gnb/ngap/transport.cpp
+++ b/src/gnb/ngap/transport.cpp
@@ -288,6 +288,9 @@ void NgapTask::handleSctpMessage(int amfId, uint16_t stream, const UniqueBuffer
case ASN_NGAP_InitiatingMessage__value_PR_PDUSessionResourceReleaseCommand:
receiveSessionResourceReleaseCommand(amf->ctxId, &value.choice.PDUSessionResourceReleaseCommand);
break;
+ case ASN_NGAP_InitiatingMessage__value_PR_Paging:
+ receivePaging(amf->ctxId, &value.choice.Paging);
+ break;
default:
m_logger->err("Unhandled NGAP initiating-message received (%d)", value.present);
break;
diff --git a/src/gnb/nts.hpp b/src/gnb/nts.hpp
index 4d8904fa0..64248e637 100644
--- a/src/gnb/nts.hpp
+++ b/src/gnb/nts.hpp
@@ -8,61 +8,93 @@
#pragma once
+#include "types.hpp"
#include
#include
+#include
#include
#include
-#include
#include
#include
#include
#include
#include
-#include "types.hpp"
+extern "C" struct ASN_NGAP_FiveG_S_TMSI;
+extern "C" struct ASN_NGAP_TAIListForPaging;
namespace nr::gnb
{
-struct NwGnbMrToRrc : NtsMessage
+struct NwGnbRlsToRrc : NtsMessage
{
enum PR
{
RRC_PDU_DELIVERY,
- RADIO_LINK_FAILURE,
+ SIGNAL_LOST
} present;
// RRC_PDU_DELIVERY
- // RADIO_LINK_FAILURE
+ // SIGNAL_LOST
int ueId{};
// RRC_PDU_DELIVERY
rrc::RrcChannel channel{};
OctetString pdu{};
- explicit NwGnbMrToRrc(PR present) : NtsMessage(NtsMessageType::GNB_MR_TO_RRC), present(present)
+ explicit NwGnbRlsToRrc(PR present) : NtsMessage(NtsMessageType::GNB_RLS_TO_RRC), present(present)
{
}
};
-struct NwGnbRrcToMr : NtsMessage
+struct NwGnbRlsToGtp : NtsMessage
{
enum PR
{
- NGAP_LAYER_INITIALIZED,
- RRC_PDU_DELIVERY,
- AN_RELEASE,
+ DATA_PDU_DELIVERY,
} present;
- // RRC_PDU_DELIVERY
- // AN_RELEASE
+ // DATA_PDU_DELIVERY
int ueId{};
+ int psi{};
+ OctetString pdu{};
+
+ explicit NwGnbRlsToGtp(PR present) : NtsMessage(NtsMessageType::GNB_RLS_TO_GTP), present(present)
+ {
+ }
+};
+
+struct NwGnbGtpToRls : NtsMessage
+{
+ enum PR
+ {
+ DATA_PDU_DELIVERY,
+ } present;
+
+ // DATA_PDU_DELIVERY
+ int ueId{};
+ int psi{};
+ OctetString pdu{};
+
+ explicit NwGnbGtpToRls(PR present) : NtsMessage(NtsMessageType::GNB_GTP_TO_RLS), present(present)
+ {
+ }
+};
+
+struct NwGnbRrcToRls : NtsMessage
+{
+ enum PR
+ {
+ RADIO_POWER_ON,
+ RRC_PDU_DELIVERY,
+ } present;
// RRC_PDU_DELIVERY
+ int ueId{};
rrc::RrcChannel channel{};
OctetString pdu{};
- explicit NwGnbRrcToMr(PR present) : NtsMessage(NtsMessageType::GNB_RRC_TO_MR), present(present)
+ explicit NwGnbRrcToRls(PR present) : NtsMessage(NtsMessageType::GNB_RRC_TO_RLS), present(present)
{
}
};
@@ -71,9 +103,10 @@ struct NwGnbNgapToRrc : NtsMessage
{
enum PR
{
- NGAP_LAYER_INITIALIZED,
+ RADIO_POWER_ON,
NAS_DELIVERY,
AN_RELEASE,
+ PAGING,
} present;
// NAS_DELIVERY
@@ -83,6 +116,10 @@ struct NwGnbNgapToRrc : NtsMessage
// NAS_DELIVERY
OctetString pdu{};
+ // PAGING
+ asn::Unique uePagingTmsi{};
+ asn::Unique taiListForPaging{};
+
explicit NwGnbNgapToRrc(PR present) : NtsMessage(NtsMessageType::GNB_NGAP_TO_RRC), present(present)
{
}
@@ -142,73 +179,6 @@ struct NwGnbNgapToGtp : NtsMessage
}
};
-struct NwGnbMrToGtp : NtsMessage
-{
- enum PR
- {
- UPLINK_DELIVERY,
- } present;
-
- // UPLINK_DELIVERY
- int ueId{};
- int pduSessionId{};
- OctetString data{};
-
- explicit NwGnbMrToGtp(PR present) : NtsMessage(NtsMessageType::GNB_MR_TO_GTP), present(present)
- {
- }
-};
-
-struct NwGnbGtpToMr : NtsMessage
-{
- enum PR
- {
- DATA_PDU_DELIVERY,
- } present;
-
- // DATA_PDU_DELIVERY
- int ueId{};
- int pduSessionId{};
- OctetString data{};
-
- explicit NwGnbGtpToMr(PR present) : NtsMessage(NtsMessageType::GNB_GTP_TO_MR), present(present)
- {
- }
-};
-
-struct NwGnbMrToMr : NtsMessage
-{
- enum PR
- {
- UE_CONNECTED,
- UE_RELEASED,
- SEND_OVER_UDP,
- RECEIVE_OVER_UDP,
- } present;
-
- // UE_CONNECTED
- // UE_RELEASED
- // RECEIVE_OVER_UDP
- int ue{};
-
- // UE_CONNECTED
- std::string name{};
-
- // UE_RELEASED
- rls::ECause cause{};
-
- // SEND_OVER_RLS
- InetAddress address{};
- OctetString pdu{};
-
- // RECEIVE_OVER_UDP
- rls::EPayloadType type{};
-
- explicit NwGnbMrToMr(PR present) : NtsMessage(NtsMessageType::GNB_MR_TO_MR), present(present)
- {
- }
-};
-
struct NwGnbSctp : NtsMessage
{
enum PR
diff --git a/src/gnb/rls/handler.cpp b/src/gnb/rls/handler.cpp
new file mode 100644
index 000000000..477c2927a
--- /dev/null
+++ b/src/gnb/rls/handler.cpp
@@ -0,0 +1,89 @@
+//
+// 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 "task.hpp"
+#include
+#include
+#include
+
+static int MIN_ALLOWED_DBM = -120;
+
+static int EstimateSimulatedDbm(const Vector3 &myPos, const Vector3 &uePos)
+{
+ int deltaX = myPos.x - uePos.x;
+ int deltaY = myPos.y - uePos.y;
+ int deltaZ = myPos.z - uePos.z;
+
+ int distance = static_cast(std::sqrt(deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ));
+ if (distance == 0)
+ return -1; // 0 may be confusing for people
+ return -distance;
+}
+
+namespace nr::gnb
+{
+
+void GnbRlsTask::handleCellInfoRequest(int ueId, const rls::RlsCellInfoRequest &msg)
+{
+ int dbm = EstimateSimulatedDbm(m_base->config->phyLocation, msg.simPos);
+ if (dbm < MIN_ALLOWED_DBM)
+ {
+ // if the simulated signal strength is such low, then do not send a response to this message
+ return;
+ }
+
+ rls::RlsCellInfoResponse resp{m_sti};
+ resp.cellId.nci = m_base->config->nci;
+ resp.cellId.plmn = m_base->config->plmn;
+ resp.tac = m_base->config->tac;
+ resp.dbm = dbm;
+ resp.gnbName = m_base->config->name;
+ resp.linkIp = m_base->config->portalIp;
+
+ sendRlsMessage(ueId, resp);
+}
+
+void GnbRlsTask::handleUplinkPduDelivery(int ueId, rls::RlsPduDelivery &msg)
+{
+ if (msg.pduType == rls::EPduType::RRC)
+ {
+ auto *nw = new NwGnbRlsToRrc(NwGnbRlsToRrc::RRC_PDU_DELIVERY);
+ nw->ueId = ueId;
+ nw->channel = static_cast(msg.payload.get4I(0));
+ nw->pdu = std::move(msg.pdu);
+ m_base->rrcTask->push(nw);
+ }
+ else if (msg.pduType == rls::EPduType::DATA)
+ {
+ auto *nw = new NwGnbRlsToGtp(NwGnbRlsToGtp::DATA_PDU_DELIVERY);
+ nw->ueId = ueId;
+ nw->psi = msg.payload.get4I(0);
+ nw->pdu = std::move(msg.pdu);
+ m_base->gtpTask->push(nw);
+ }
+}
+
+void GnbRlsTask::handleDownlinkDelivery(int ueId, rls::EPduType pduType, OctetString &&pdu, OctetString &&payload)
+{
+ rls::RlsPduDelivery resp{m_sti};
+ resp.pduType = pduType;
+ resp.pdu = std::move(pdu);
+ resp.payload = std::move(payload);
+
+ if (ueId != 0)
+ {
+ sendRlsMessage(ueId, resp);
+ }
+ else
+ {
+ for (auto &ue : m_ueCtx)
+ sendRlsMessage(ue.first, resp);
+ }
+}
+
+} // namespace nr::gnb
diff --git a/src/gnb/rls/management.cpp b/src/gnb/rls/management.cpp
new file mode 100644
index 000000000..193dda79b
--- /dev/null
+++ b/src/gnb/rls/management.cpp
@@ -0,0 +1,73 @@
+//
+// 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 "task.hpp"
+#include
+#include
+#include
+
+static const int64_t LAST_SEEN_THRESHOLD = 3000;
+
+namespace nr::gnb
+{
+
+int GnbRlsTask::updateUeInfo(const InetAddress &addr, uint64_t sti)
+{
+ if (m_stiToUeId.count(sti))
+ {
+ int ueId = m_stiToUeId[sti];
+ auto &ctx = m_ueCtx[ueId];
+ ctx->addr = addr;
+ ctx->lastSeen = utils::CurrentTimeMillis();
+ return ueId;
+ }
+ else
+ {
+ int ueId = ++m_ueIdCounter;
+ m_stiToUeId[sti] = ueId;
+ auto ctx = std::make_unique(ueId);
+ ctx->sti = sti;
+ ctx->addr = addr;
+ ctx->lastSeen = utils::CurrentTimeMillis();
+ m_ueCtx[ueId] = std::move(ctx);
+
+ m_logger->debug("New UE signal detected, total [%d] UEs in coverage", static_cast(m_stiToUeId.size()));
+ return ueId;
+ }
+}
+
+void GnbRlsTask::onPeriodicLostControl()
+{
+ int64_t current = utils::CurrentTimeMillis();
+
+ std::set lostUeId{};
+ std::set lostSti{};
+
+ for (auto &item : m_ueCtx)
+ {
+ if (current - item.second->lastSeen > LAST_SEEN_THRESHOLD)
+ {
+ lostUeId.insert(item.second->ueId);
+ lostSti.insert(item.second->sti);
+ }
+ }
+
+ for (uint64_t sti : lostSti)
+ m_stiToUeId.erase(sti);
+ for (int ueId : lostUeId)
+ {
+ m_ueCtx.erase(ueId);
+ m_logger->debug("Signal lost detected for UE[%d]", ueId);
+
+ auto *w = new NwGnbRlsToRrc(NwGnbRlsToRrc::SIGNAL_LOST);
+ w->ueId = ueId;
+ m_base->rrcTask->push(w);
+ }
+}
+
+} // namespace nr::gnb
diff --git a/src/gnb/rls/task.cpp b/src/gnb/rls/task.cpp
new file mode 100644
index 000000000..1ac19793f
--- /dev/null
+++ b/src/gnb/rls/task.cpp
@@ -0,0 +1,118 @@
+//
+// 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 "task.hpp"
+#include
+#include
+#include
+#include
+#include
+#include
+
+static const int TIMER_ID_LOST_CONTROL = 1;
+static const int TIMER_PERIOD_LOST_CONTROL = 2000;
+
+namespace nr::gnb
+{
+
+GnbRlsTask::GnbRlsTask(TaskBase *base)
+ : m_base{base}, m_udpTask{}, m_powerOn{}, m_ueCtx{}, m_stiToUeId{}, m_ueIdCounter{}
+{
+ m_logger = m_base->logBase->makeUniqueLogger("rls");
+ m_sti = utils::Random64();
+}
+
+void GnbRlsTask::onStart()
+{
+ try
+ {
+ m_udpTask = new udp::UdpServerTask(m_base->config->portalIp, cons::PortalPort, this);
+ m_udpTask->start();
+ }
+ catch (const LibError &e)
+ {
+ m_logger->err("RLS failure [%s]", e.what());
+ quit();
+ return;
+ }
+
+ setTimer(TIMER_ID_LOST_CONTROL, TIMER_PERIOD_LOST_CONTROL);
+}
+
+void GnbRlsTask::onLoop()
+{
+ NtsMessage *msg = take();
+ if (!msg)
+ return;
+
+ switch (msg->msgType)
+ {
+ case NtsMessageType::GNB_RRC_TO_RLS: {
+ auto *w = dynamic_cast(msg);
+ switch (w->present)
+ {
+ case NwGnbRrcToRls::RRC_PDU_DELIVERY: {
+ handleDownlinkDelivery(w->ueId, rls::EPduType::RRC, std::move(w->pdu),
+ OctetString::FromOctet4(static_cast(w->channel)));
+ break;
+ }
+ case NwGnbRrcToRls::RADIO_POWER_ON: {
+ m_powerOn = true;
+ break;
+ }
+ }
+ break;
+ }
+ case NtsMessageType::GNB_GTP_TO_RLS: {
+ auto *w = dynamic_cast(msg);
+ switch (w->present)
+ {
+ case NwGnbGtpToRls::DATA_PDU_DELIVERY: {
+ handleDownlinkDelivery(w->ueId, rls::EPduType::DATA, std::move(w->pdu),
+ OctetString::FromOctet4(static_cast(w->psi)));
+ break;
+ }
+ }
+ break;
+ }
+ case NtsMessageType::UDP_SERVER_RECEIVE: {
+ auto *w = dynamic_cast(msg);
+ auto rlsMsg = rls::DecodeRlsMessage(OctetView{w->packet});
+ if (rlsMsg == nullptr)
+ {
+ m_logger->err("Unable to decode RLS message");
+ break;
+ }
+ receiveRlsMessage(w->fromAddress, *rlsMsg);
+ break;
+ }
+ case NtsMessageType::TIMER_EXPIRED: {
+ auto *w = dynamic_cast(msg);
+ if (w->timerId == TIMER_ID_LOST_CONTROL)
+ {
+ setTimer(TIMER_ID_LOST_CONTROL, TIMER_PERIOD_LOST_CONTROL);
+ onPeriodicLostControl();
+ }
+ break;
+ }
+ default:
+ m_logger->unhandledNts(msg);
+ break;
+ }
+
+ delete msg;
+}
+
+void GnbRlsTask::onQuit()
+{
+ if (m_udpTask != nullptr)
+ m_udpTask->quit();
+ delete m_udpTask;
+}
+
+} // namespace nr::gnb
diff --git a/src/gnb/rls/task.hpp b/src/gnb/rls/task.hpp
new file mode 100644
index 000000000..f5b378095
--- /dev/null
+++ b/src/gnb/rls/task.hpp
@@ -0,0 +1,63 @@
+//
+// 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.
+//
+
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace nr::gnb
+{
+
+class GnbRlsTask : public NtsTask
+{
+ private:
+ TaskBase *m_base;
+ std::unique_ptr m_logger;
+ udp::UdpServerTask *m_udpTask;
+
+ bool m_powerOn;
+ uint64_t m_sti;
+ std::unordered_map> m_ueCtx;
+ std::unordered_map m_stiToUeId;
+ int m_ueIdCounter;
+
+ friend class GnbCmdHandler;
+
+ public:
+ explicit GnbRlsTask(TaskBase *base);
+ ~GnbRlsTask() override = default;
+
+ protected:
+ void onStart() override;
+ void onLoop() override;
+ void onQuit() override;
+
+ private: /* Transport */
+ void receiveRlsMessage(const InetAddress &addr, rls::RlsMessage &msg);
+ void sendRlsMessage(int ueId, const rls::RlsMessage &msg);
+
+ private: /* Handler */
+ void handleCellInfoRequest(int ueId, const rls::RlsCellInfoRequest &msg);
+ void handleUplinkPduDelivery(int ueId, rls::RlsPduDelivery &msg);
+ void handleDownlinkDelivery(int ueId, rls::EPduType pduType, OctetString &&pdu, OctetString &&payload);
+
+ private: /* UE Management */
+ int updateUeInfo(const InetAddress &addr, uint64_t sti);
+ void onPeriodicLostControl();
+};
+
+} // namespace nr::gnb
\ No newline at end of file
diff --git a/src/gnb/rls/transport.cpp b/src/gnb/rls/transport.cpp
new file mode 100644
index 000000000..43eeaaa97
--- /dev/null
+++ b/src/gnb/rls/transport.cpp
@@ -0,0 +1,53 @@
+//
+// 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 "task.hpp"
+
+namespace nr::gnb
+{
+
+void GnbRlsTask::receiveRlsMessage(const InetAddress &addr, rls::RlsMessage &msg)
+{
+ if (!m_powerOn)
+ {
+ // ignore received RLS message
+ return;
+ }
+
+ int ueId = updateUeInfo(addr, msg.sti);
+
+ switch (msg.msgType)
+ {
+ case rls::EMessageType::CELL_INFO_REQUEST: {
+ handleCellInfoRequest(ueId, (const rls::RlsCellInfoRequest &)msg);
+ break;
+ }
+ case rls::EMessageType::PDU_DELIVERY: {
+ handleUplinkPduDelivery(ueId, (rls::RlsPduDelivery &)msg);
+ break;
+ }
+ default:
+ m_logger->err("Unhandled RLS message received with type[%d]", static_cast(msg.msgType));
+ break;
+ }
+}
+
+void GnbRlsTask::sendRlsMessage(int ueId, const rls::RlsMessage &msg)
+{
+ if (!m_ueCtx.count(ueId))
+ {
+ m_logger->err("RLS message sending failure, UE[%d] not exists", ueId);
+ return;
+ }
+
+ OctetString stream{};
+ rls::EncodeRlsMessage(msg, stream);
+ m_udpTask->send(m_ueCtx[ueId]->addr, stream);
+}
+
+} // namespace nr::gnb
diff --git a/src/gnb/rrc/channel.cpp b/src/gnb/rrc/channel.cpp
index b689310ef..9880e7a64 100644
--- a/src/gnb/rrc/channel.cpp
+++ b/src/gnb/rrc/channel.cpp
@@ -9,8 +9,8 @@
#include "task.hpp"
#include
#include
+#include
#include
-#include
namespace nr::gnb
{
@@ -28,42 +28,6 @@ void GnbRrcTask::handleUplinkRrc(int ueId, rrc::RrcChannel channel, const OctetS
asn::Free(asn_DEF_ASN_RRC_BCCH_BCH_Message, pdu);
break;
}
- case rrc::RrcChannel::BCCH_DL_SCH: {
- auto *pdu = rrc::encode::Decode(asn_DEF_ASN_RRC_BCCH_DL_SCH_Message, rrcPdu);
- if (pdu == nullptr)
- m_logger->err("RRC BCCH-DL-SCH PDU decoding failed.");
- else
- receiveRrcMessage(ueId, pdu);
- asn::Free(asn_DEF_ASN_RRC_BCCH_DL_SCH_Message, pdu);
- break;
- }
- case rrc::RrcChannel::DL_CCCH: {
- auto *pdu = rrc::encode::Decode(asn_DEF_ASN_RRC_DL_CCCH_Message, rrcPdu);
- if (pdu == nullptr)
- m_logger->err("RRC DL-CCCH PDU decoding failed.");
- else
- receiveRrcMessage(ueId, pdu);
- asn::Free(asn_DEF_ASN_RRC_DL_CCCH_Message, pdu);
- break;
- }
- case rrc::RrcChannel::DL_DCCH: {
- auto *pdu = rrc::encode::Decode(asn_DEF_ASN_RRC_DL_DCCH_Message, rrcPdu);
- if (pdu == nullptr)
- m_logger->err("RRC DL-DCCH PDU decoding failed.");
- else
- receiveRrcMessage(ueId, pdu);
- asn::Free(asn_DEF_ASN_RRC_DL_DCCH_Message, pdu);
- break;
- }
- case rrc::RrcChannel::PCCH: {
- auto *pdu = rrc::encode::Decode(asn_DEF_ASN_RRC_PCCH_Message, rrcPdu);
- if (pdu == nullptr)
- m_logger->err("RRC PCCH PDU decoding failed.");
- else
- receiveRrcMessage(ueId, pdu);
- asn::Free(asn_DEF_ASN_RRC_PCCH_Message, pdu);
- break;
- }
case rrc::RrcChannel::UL_CCCH: {
auto *pdu = rrc::encode::Decode(asn_DEF_ASN_RRC_UL_CCCH_Message, rrcPdu);
if (pdu == nullptr)
@@ -91,6 +55,11 @@ void GnbRrcTask::handleUplinkRrc(int ueId, rrc::RrcChannel channel, const OctetS
asn::Free(asn_DEF_ASN_RRC_UL_DCCH_Message, pdu);
break;
}
+ case rrc::RrcChannel::PCCH:
+ case rrc::RrcChannel::BCCH_DL_SCH:
+ case rrc::RrcChannel::DL_CCCH:
+ case rrc::RrcChannel::DL_DCCH:
+ break;
}
}
@@ -103,11 +72,11 @@ void GnbRrcTask::sendRrcMessage(int ueId, ASN_RRC_BCCH_BCH_Message *msg)
return;
}
- auto *w = new NwGnbRrcToMr(NwGnbRrcToMr::RRC_PDU_DELIVERY);
+ auto *w = new NwGnbRrcToRls(NwGnbRrcToRls::RRC_PDU_DELIVERY);
w->ueId = ueId;
w->channel = rrc::RrcChannel::BCCH_BCH;
w->pdu = std::move(pdu);
- m_base->mrTask->push(w);
+ m_base->rlsTask->push(w);
}
void GnbRrcTask::sendRrcMessage(int ueId, ASN_RRC_BCCH_DL_SCH_Message *msg)
@@ -119,11 +88,11 @@ void GnbRrcTask::sendRrcMessage(int ueId, ASN_RRC_BCCH_DL_SCH_Message *msg)
return;
}
- auto *w = new NwGnbRrcToMr(NwGnbRrcToMr::RRC_PDU_DELIVERY);
+ auto *w = new NwGnbRrcToRls(NwGnbRrcToRls::RRC_PDU_DELIVERY);
w->ueId = ueId;
w->channel = rrc::RrcChannel::BCCH_DL_SCH;
w->pdu = std::move(pdu);
- m_base->mrTask->push(w);
+ m_base->rlsTask->push(w);
}
void GnbRrcTask::sendRrcMessage(int ueId, ASN_RRC_DL_CCCH_Message *msg)
@@ -135,11 +104,11 @@ void GnbRrcTask::sendRrcMessage(int ueId, ASN_RRC_DL_CCCH_Message *msg)
return;
}
- auto *w = new NwGnbRrcToMr(NwGnbRrcToMr::RRC_PDU_DELIVERY);
+ auto *w = new NwGnbRrcToRls(NwGnbRrcToRls::RRC_PDU_DELIVERY);
w->ueId = ueId;
w->channel = rrc::RrcChannel::DL_CCCH;
w->pdu = std::move(pdu);
- m_base->mrTask->push(w);
+ m_base->rlsTask->push(w);
}
void GnbRrcTask::sendRrcMessage(int ueId, ASN_RRC_DL_DCCH_Message *msg)
@@ -151,14 +120,14 @@ void GnbRrcTask::sendRrcMessage(int ueId, ASN_RRC_DL_DCCH_Message *msg)
return;
}
- auto *w = new NwGnbRrcToMr(NwGnbRrcToMr::RRC_PDU_DELIVERY);
+ auto *w = new NwGnbRrcToRls(NwGnbRrcToRls::RRC_PDU_DELIVERY);
w->ueId = ueId;
w->channel = rrc::RrcChannel::DL_DCCH;
w->pdu = std::move(pdu);
- m_base->mrTask->push(w);
+ m_base->rlsTask->push(w);
}
-void GnbRrcTask::sendRrcMessage(int ueId, ASN_RRC_PCCH_Message *msg)
+void GnbRrcTask::sendRrcMessage(ASN_RRC_PCCH_Message *msg)
{
OctetString pdu = rrc::encode::EncodeS(asn_DEF_ASN_RRC_PCCH_Message, msg);
if (pdu.length() == 0)
@@ -167,59 +136,11 @@ void GnbRrcTask::sendRrcMessage(int ueId, ASN_RRC_PCCH_Message *msg)
return;
}
- auto *w = new NwGnbRrcToMr(NwGnbRrcToMr::RRC_PDU_DELIVERY);
- w->ueId = ueId;
+ auto *w = new NwGnbRrcToRls(NwGnbRrcToRls::RRC_PDU_DELIVERY);
+ w->ueId = 0;
w->channel = rrc::RrcChannel::PCCH;
w->pdu = std::move(pdu);
- m_base->mrTask->push(w);
-}
-
-void GnbRrcTask::sendRrcMessage(int ueId, ASN_RRC_UL_CCCH_Message *msg)
-{
- OctetString pdu = rrc::encode::EncodeS(asn_DEF_ASN_RRC_UL_CCCH_Message, msg);
- if (pdu.length() == 0)
- {
- m_logger->err("RRC UL-CCCH encoding failed.");
- return;
- }
-
- auto *w = new NwGnbRrcToMr(NwGnbRrcToMr::RRC_PDU_DELIVERY);
- w->ueId = ueId;
- w->channel = rrc::RrcChannel::UL_CCCH;
- w->pdu = std::move(pdu);
- m_base->mrTask->push(w);
-}
-
-void GnbRrcTask::sendRrcMessage(int ueId, ASN_RRC_UL_CCCH1_Message *msg)
-{
- OctetString pdu = rrc::encode::EncodeS(asn_DEF_ASN_RRC_UL_CCCH1_Message, msg);
- if (pdu.length() == 0)
- {
- m_logger->err("RRC UL-CCCH1 encoding failed.");
- return;
- }
-
- auto *w = new NwGnbRrcToMr(NwGnbRrcToMr::RRC_PDU_DELIVERY);
- w->ueId = ueId;
- w->channel = rrc::RrcChannel::UL_CCCH1;
- w->pdu = std::move(pdu);
- m_base->mrTask->push(w);
-}
-
-void GnbRrcTask::sendRrcMessage(int ueId, ASN_RRC_UL_DCCH_Message *msg)
-{
- OctetString pdu = rrc::encode::EncodeS(asn_DEF_ASN_RRC_UL_DCCH_Message, msg);
- if (pdu.length() == 0)
- {
- m_logger->err("RRC UL-DCCH encoding failed.");
- return;
- }
-
- auto *w = new NwGnbRrcToMr(NwGnbRrcToMr::RRC_PDU_DELIVERY);
- w->ueId = ueId;
- w->channel = rrc::RrcChannel::UL_DCCH;
- w->pdu = std::move(pdu);
- m_base->mrTask->push(w);
+ m_base->rlsTask->push(w);
}
void GnbRrcTask::receiveRrcMessage(int ueId, ASN_RRC_BCCH_BCH_Message *msg)
@@ -227,26 +148,6 @@ void GnbRrcTask::receiveRrcMessage(int ueId, ASN_RRC_BCCH_BCH_Message *msg)
// TODO
}
-void GnbRrcTask::receiveRrcMessage(int ueId, ASN_RRC_BCCH_DL_SCH_Message *msg)
-{
- // TODO
-}
-
-void GnbRrcTask::receiveRrcMessage(int ueId, ASN_RRC_DL_CCCH_Message *msg)
-{
- // TODO
-}
-
-void GnbRrcTask::receiveRrcMessage(int ueId, ASN_RRC_DL_DCCH_Message *msg)
-{
- // TODO
-}
-
-void GnbRrcTask::receiveRrcMessage(int ueId, ASN_RRC_PCCH_Message *msg)
-{
- // TODO
-}
-
void GnbRrcTask::receiveRrcMessage(int ueId, ASN_RRC_UL_CCCH_Message *msg)
{
if (msg->message.present != ASN_RRC_UL_CCCH_MessageType_PR_c1)
diff --git a/src/gnb/rrc/handler.cpp b/src/gnb/rrc/handler.cpp
index 3e921ea42..c257058e9 100644
--- a/src/gnb/rrc/handler.cpp
+++ b/src/gnb/rrc/handler.cpp
@@ -8,10 +8,10 @@
#include "task.hpp"
-#include
#include
#include
+#include
#include
#include
#include
@@ -20,6 +20,9 @@
#include
#include
#include
+#include
+#include
+#include
#include
#include
#include
@@ -117,7 +120,7 @@ void GnbRrcTask::receiveRrcSetupRequest(int ueId, const ASN_RRC_RRCSetupRequest
asn::SetOctetString(rrcSetupIEs->masterCellGroup,
rrc::encode::EncodeS(asn_DEF_ASN_RRC_CellGroupConfig, &masterCellGroup));
- m_logger->debug("Sending RRC Setup for UE[%d]", ueId);
+ m_logger->info("RRC Setup for UE[%d]", ueId);
sendRrcMessage(ueId, pdu);
}
@@ -141,7 +144,7 @@ void GnbRrcTask::receiveRrcSetupComplete(int ueId, const ASN_RRC_RRCSetupComplet
void GnbRrcTask::releaseConnection(int ueId)
{
- m_logger->debug("Releasing RRC connection for UE[%d]", ueId);
+ m_logger->info("Releasing RRC connection for UE[%d]", ueId);
// Send RRC Release message
auto *pdu = asn::New();
@@ -155,11 +158,6 @@ void GnbRrcTask::releaseConnection(int ueId)
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);
}
@@ -175,4 +173,32 @@ void GnbRrcTask::handleRadioLinkFailure(int ueId)
m_ueCtx.erase(ueId);
}
+void GnbRrcTask::handlePaging(const asn::Unique &tmsi,
+ const asn::Unique &taiList)
+{
+ // Construct and send a Paging message
+ auto *pdu = asn::New();
+ pdu->message.present = ASN_RRC_PCCH_MessageType_PR_c1;
+ pdu->message.choice.c1 = asn::NewFor(pdu->message.choice.c1);
+ pdu->message.choice.c1->present = ASN_RRC_PCCH_MessageType__c1_PR_paging;
+ auto &paging = pdu->message.choice.c1->choice.paging = asn::New();
+
+ auto *record = asn::New();
+ record->ue_Identity.present = ASN_RRC_PagingUE_Identity_PR_ng_5G_S_TMSI;
+
+ OctetString tmsiOctets{};
+ tmsiOctets.appendOctet2(bits::Ranged16({
+ {10, asn::GetBitStringInt<10>(tmsi->aMFSetID)},
+ {6, asn::GetBitStringInt<10>(tmsi->aMFPointer)},
+ }));
+ tmsiOctets.append(asn::GetOctetString(tmsi->fiveG_TMSI));
+
+ asn::SetBitString(record->ue_Identity.choice.ng_5G_S_TMSI, tmsiOctets);
+
+ paging->pagingRecordList = asn::NewFor(paging->pagingRecordList);
+ asn::SequenceAdd(*paging->pagingRecordList, record);
+
+ sendRrcMessage(pdu);
+}
+
} // namespace nr::gnb
\ No newline at end of file
diff --git a/src/gnb/rrc/task.cpp b/src/gnb/rrc/task.cpp
index cd9b6f7ab..a3e58647b 100644
--- a/src/gnb/rrc/task.cpp
+++ b/src/gnb/rrc/task.cpp
@@ -9,8 +9,8 @@
#include "task.hpp"
#include
#include
-#include
#include
+#include
#include
namespace nr::gnb
@@ -38,15 +38,15 @@ void GnbRrcTask::onLoop()
switch (msg->msgType)
{
- case NtsMessageType::GNB_MR_TO_RRC: {
- auto *w = dynamic_cast(msg);
+ case NtsMessageType::GNB_RLS_TO_RRC: {
+ auto *w = dynamic_cast(msg);
switch (w->present)
{
- case NwGnbMrToRrc::RRC_PDU_DELIVERY: {
+ case NwGnbRlsToRrc::RRC_PDU_DELIVERY: {
handleUplinkRrc(w->ueId, w->channel, w->pdu);
break;
}
- case NwGnbMrToRrc::RADIO_LINK_FAILURE: {
+ case NwGnbRlsToRrc::SIGNAL_LOST: {
handleRadioLinkFailure(w->ueId);
break;
}
@@ -57,8 +57,8 @@ void GnbRrcTask::onLoop()
auto *w = dynamic_cast(msg);
switch (w->present)
{
- case NwGnbNgapToRrc::NGAP_LAYER_INITIALIZED: {
- m_base->mrTask->push(new NwGnbRrcToMr(NwGnbRrcToMr::NGAP_LAYER_INITIALIZED));
+ case NwGnbNgapToRrc::RADIO_POWER_ON: {
+ m_base->rlsTask->push(new NwGnbRrcToRls(NwGnbRrcToRls::RADIO_POWER_ON));
break;
}
case NwGnbNgapToRrc::NAS_DELIVERY: {
@@ -69,6 +69,9 @@ void GnbRrcTask::onLoop()
releaseConnection(w->ueId);
break;
}
+ case NwGnbNgapToRrc::PAGING:
+ handlePaging(w->uePagingTmsi, w->taiListForPaging);
+ break;
}
break;
}
diff --git a/src/gnb/rrc/task.hpp b/src/gnb/rrc/task.hpp
index 9cefe6dae..d8cb15319 100644
--- a/src/gnb/rrc/task.hpp
+++ b/src/gnb/rrc/task.hpp
@@ -36,7 +36,6 @@ namespace nr::gnb
{
class NgapTask;
-class GnbMrTask;
class GnbRrcTask : public NtsTask
{
@@ -71,6 +70,8 @@ class GnbRrcTask : public NtsTask
void deliverUplinkNas(int ueId, OctetString &&nasPdu);
void releaseConnection(int ueId);
void handleRadioLinkFailure(int ueId);
+ void handlePaging(const asn::Unique &tmsi,
+ const asn::Unique &taiList);
void receiveUplinkInformationTransfer(int ueId, const ASN_RRC_ULInformationTransfer &msg);
void receiveRrcSetupRequest(int ueId, const ASN_RRC_RRCSetupRequest &msg);
@@ -81,17 +82,10 @@ class GnbRrcTask : public NtsTask
void sendRrcMessage(int ueId, ASN_RRC_BCCH_DL_SCH_Message *msg);
void sendRrcMessage(int ueId, ASN_RRC_DL_CCCH_Message *msg);
void sendRrcMessage(int ueId, ASN_RRC_DL_DCCH_Message *msg);
- void sendRrcMessage(int ueId, ASN_RRC_PCCH_Message *msg);
- void sendRrcMessage(int ueId, ASN_RRC_UL_CCCH_Message *msg);
- void sendRrcMessage(int ueId, ASN_RRC_UL_CCCH1_Message *msg);
- void sendRrcMessage(int ueId, ASN_RRC_UL_DCCH_Message *msg);
+ void sendRrcMessage(ASN_RRC_PCCH_Message *msg);
/* RRC channel receive message */
void receiveRrcMessage(int ueId, ASN_RRC_BCCH_BCH_Message *msg);
- void receiveRrcMessage(int ueId, ASN_RRC_BCCH_DL_SCH_Message *msg);
- void receiveRrcMessage(int ueId, ASN_RRC_DL_CCCH_Message *msg);
- void receiveRrcMessage(int ueId, ASN_RRC_DL_DCCH_Message *msg);
- void receiveRrcMessage(int ueId, ASN_RRC_PCCH_Message *msg);
void receiveRrcMessage(int ueId, ASN_RRC_UL_CCCH_Message *msg);
void receiveRrcMessage(int ueId, ASN_RRC_UL_CCCH1_Message *msg);
void receiveRrcMessage(int ueId, ASN_RRC_UL_DCCH_Message *msg);
diff --git a/src/gnb/types.hpp b/src/gnb/types.hpp
index 0afef9e4d..bad4bd96b 100644
--- a/src/gnb/types.hpp
+++ b/src/gnb/types.hpp
@@ -15,6 +15,7 @@
#include
#include
#include
+#include
#include
#include
@@ -23,9 +24,9 @@ namespace nr::gnb
class GnbAppTask;
class GtpTask;
-class GnbMrTask;
class NgapTask;
class GnbRrcTask;
+class GnbRlsTask;
class SctpTask;
enum class EAmfState
@@ -103,6 +104,18 @@ struct NgapAmfContext
std::vector plmnSupportList{};
};
+struct RlsUeContext
+{
+ const int ueId;
+ uint64_t sti{};
+ InetAddress addr{};
+ int64_t lastSeen{};
+
+ explicit RlsUeContext(int ueId) : ueId(ueId)
+ {
+ }
+};
+
struct AggregateMaximumBitRate
{
uint64_t dlAmbr{};
@@ -299,6 +312,7 @@ struct GnbConfig
/* Assigned by program */
std::string name{};
EPagingDrx pagingDrx{};
+ Vector3 phyLocation{};
[[nodiscard]] inline uint32_t getGnbId() const
{
@@ -320,16 +334,10 @@ struct TaskBase
GnbAppTask *appTask{};
GtpTask *gtpTask{};
- GnbMrTask *mrTask{};
NgapTask *ngapTask{};
GnbRrcTask *rrcTask{};
SctpTask *sctpTask{};
-};
-
-struct MrUeContext
-{
- int ueId{};
- std::string name{};
+ GnbRlsTask *rlsTask{};
};
Json ToJson(const GnbStatusInfo &v);
diff --git a/src/nas/enums.hpp b/src/nas/enums.hpp
index 3c42fc1e6..d810aa58a 100644
--- a/src/nas/enums.hpp
+++ b/src/nas/enums.hpp
@@ -137,7 +137,7 @@ enum class EMmCause
SEC_MODE_REJECTED_UNSPECIFIED = 0b00011000,
NON_5G_AUTHENTICATION_UNACCEPTABLE = 0b00011010,
N1_MODE_NOT_ALLOWED = 0b00011011,
- RESTRICTED_NOT_SERVICE_AREA = 0b00011100,
+ RESTRICTED_SERVICE_AREA = 0b00011100,
LADN_NOT_AVAILABLE = 0b00101011,
MAX_PDU_SESSIONS_REACHED = 0b01000001,
INSUFFICIENT_RESOURCES_FOR_SLICE_AND_DNN = 0b01000011,
@@ -646,6 +646,7 @@ enum class EServiceType
EMERGENCY_SERVICES = 0b0011,
EMERGENCY_SERVICES_FALLBACK = 0b0100,
HIGH_PRIORITY_ACCESS = 0b0101,
+ ELEVATED_SIGNALLING = 0b0110,
UNUSED_SIGNALLING_1 = 0b0110,
UNUSED_SIGNALLING_2 = 0b0111,
UNUSED_SIGNALLING_3 = 0b1000,
diff --git a/src/nas/utils.cpp b/src/nas/utils.cpp
index 0d8bcebd0..d69eca31b 100644
--- a/src/nas/utils.cpp
+++ b/src/nas/utils.cpp
@@ -8,6 +8,7 @@
#include "utils.hpp"
+#include
#include
namespace nas::utils
@@ -95,8 +96,8 @@ const char *EnumToString(EMmCause v)
return "NON_5G_AUTHENTICATION_UNACCEPTABLE";
case EMmCause::N1_MODE_NOT_ALLOWED:
return "N1_MODE_NOT_ALLOWED";
- case EMmCause::RESTRICTED_NOT_SERVICE_AREA:
- return "RESTRICTED_NOT_SERVICE_AREA";
+ case EMmCause::RESTRICTED_SERVICE_AREA:
+ return "RESTRICTED_SERVICE_AREA";
case EMmCause::LADN_NOT_AVAILABLE:
return "LADN_NOT_AVAILABLE";
case EMmCause::MAX_PDU_SESSIONS_REACHED:
@@ -262,7 +263,7 @@ IEDnn DnnFromApn(const std::string &apn)
return dnn;
}
-void AddToPlmnList(IEPlmnList &list, VPlmn item)
+void AddToPlmnList(IEPlmnList &list, const VPlmn &item)
{
if (!std::any_of(list.plmns.begin(), list.plmns.end(), [&item](auto &i) { return DeepEqualsV(i, item); }))
list.plmns.push_back(item);
@@ -289,4 +290,148 @@ SingleSlice SNssaiTo(const IESNssai &v)
return sNssai;
}
+bool PlmnListContains(const IEPlmnList &list, Plmn item)
+{
+ return PlmnListContains(list, PlmnFrom(item));
+}
+
+bool PlmnListContains(const IEPlmnList &list, VPlmn item)
+{
+ return std::any_of(list.plmns.begin(), list.plmns.end(), [&item](auto &i) { return DeepEqualsV(i, item); });
+}
+
+bool TaiListContains(const IE5gsTrackingAreaIdentityList &list, const VTrackingAreaIdentity &tai)
+{
+ return std::any_of(list.list.begin(), list.list.end(), [&tai](auto &i) { return TaiListContains(i, tai); });
+}
+
+bool TaiListContains(const VPartialTrackingAreaIdentityList &list, const VTrackingAreaIdentity &tai)
+{
+ if (list.present == 0)
+ {
+ auto &list0 = list.list00;
+ if (DeepEqualsV(list0->plmn, tai.plmn))
+ {
+ if (std::any_of(list0->tacs.begin(), list0->tacs.end(), [&tai](auto &i) { return (int)i == (int)tai.tac; }))
+ return true;
+ }
+ }
+ else if (list.present == 1)
+ {
+ auto &list1 = list.list01;
+ if (DeepEqualsV(list1->plmn, tai.plmn) && (int)list1->tac == (int)tai.tac)
+ return true;
+ }
+ else if (list.present == 2)
+ {
+ auto &list2 = list.list10;
+ if (std::any_of(list2->tais.begin(), list2->tais.end(), [&tai](auto &i) { return DeepEqualsV(i, tai); }))
+ return true;
+ }
+
+ return false;
+}
+
+bool ServiceAreaListForbidsPlmn(const IEServiceAreaList &list, const VPlmn &plmn)
+{
+ return std::any_of(list.list.begin(), list.list.end(),
+ [&plmn](auto &i) { return ServiceAreaListForbidsPlmn(i, plmn); });
+}
+
+bool ServiceAreaListForbidsTai(const IEServiceAreaList &list, const VTrackingAreaIdentity &tai)
+{
+ return std::any_of(list.list.begin(), list.list.end(),
+ [&tai](auto &i) { return ServiceAreaListForbidsTai(i, tai); });
+}
+
+bool ServiceAreaListForbidsPlmn(const VPartialServiceAreaList &list, const VPlmn &plmn)
+{
+ if (list.present == 3)
+ {
+ if (list.list11->allowedType == EAllowedType::IN_THE_NON_ALLOWED_AREA && DeepEqualsV(list.list11->plmn, plmn))
+ return true;
+ }
+ return false;
+}
+
+bool ServiceAreaListForbidsTai(const VPartialServiceAreaList &list, const VTrackingAreaIdentity &tai)
+{
+ if (ServiceAreaListForbidsPlmn(list, tai.plmn))
+ return true;
+ if (list.present == 0)
+ {
+ if (list.list00->allowedType == EAllowedType::IN_THE_NON_ALLOWED_AREA &&
+ DeepEqualsV(list.list00->plmn, tai.plmn) &&
+ std::any_of(list.list00->tacs.begin(), list.list00->tacs.end(),
+ [&tai](auto &i) { return (int)i == (int)tai.tac; }))
+ return true;
+ }
+ else if (list.present == 1)
+ {
+ if (list.list01->allowedType == EAllowedType::IN_THE_NON_ALLOWED_AREA &&
+ DeepEqualsV(list.list01->plmn, tai.plmn) && (int)list.list01->tac == (int)tai.tac)
+ return true;
+ }
+ else if (list.present == 2)
+ {
+ if (list.list10->allowedType == EAllowedType::IN_THE_NON_ALLOWED_AREA &&
+ std::any_of(list.list10->tais.begin(), list.list10->tais.end(),
+ [tai](auto &i) { return DeepEqualsV(i, tai); }))
+ return true;
+ }
+ return false;
+}
+
+void AddToTaiList(IE5gsTrackingAreaIdentityList &list, const VTrackingAreaIdentity &tai)
+{
+ if (!TaiListContains(list, tai))
+ {
+ VPartialTrackingAreaIdentityList ls{};
+ ls.list01 = VPartialTrackingAreaIdentityList01{tai.plmn, tai.tac};
+ ls.present = 1;
+ list.list.push_back(ls);
+ }
+}
+
+void RemoveFromTaiList(IE5gsTrackingAreaIdentityList &list, const VTrackingAreaIdentity &tai)
+{
+ list.list.erase(std::remove_if(list.list.begin(), list.list.end(),
+ [&tai](auto &itemList) {
+ return itemList.present == 1 && DeepEqualsV(itemList.list01->plmn, tai.plmn) &&
+ (int)itemList.list01->tac == (int)tai.tac;
+ }),
+ list.list.end());
+
+ for (auto &itemList : list.list)
+ {
+ if (itemList.present == 0)
+ {
+ auto &list0 = itemList.list00;
+ if (DeepEqualsV(list0->plmn, tai.plmn))
+ {
+ list0->tacs.erase(std::remove_if(list0->tacs.begin(), list0->tacs.end(),
+ [&tai](auto tac) { return (int)tac == (int)tai.tac; }),
+ list0->tacs.end());
+ }
+ }
+ else if (itemList.present == 2)
+ {
+ auto &list2 = itemList.list10;
+ list2->tais.erase(
+ std::remove_if(list2->tais.begin(), list2->tais.end(), [&tai](auto &i) { return DeepEqualsV(i, tai); }),
+ list2->tais.end());
+ }
+ }
+
+ list.list.erase(
+ std::remove_if(list.list.begin(), list.list.end(),
+ [](auto &itemList) { return itemList.present == 0 && itemList.list00->tacs.empty(); }),
+ list.list.end());
+
+ list.list.erase(
+ std::remove_if(list.list.begin(), list.list.end(),
+ [](auto &itemList) { return itemList.present == 2 && itemList.list10->tais.empty(); }),
+ list.list.end());
+}
+
} // namespace nas::utils
diff --git a/src/nas/utils.hpp b/src/nas/utils.hpp
index b122f8c5c..7a62e86dd 100644
--- a/src/nas/utils.hpp
+++ b/src/nas/utils.hpp
@@ -24,7 +24,17 @@ SingleSlice SNssaiTo(const IESNssai &v);
bool HasValue(const IEGprsTimer3 &v);
bool HasValue(const IEGprsTimer2 &v);
-void AddToPlmnList(IEPlmnList &list, VPlmn item);
+void AddToPlmnList(IEPlmnList &list, const VPlmn &item);
+void AddToTaiList(nas::IE5gsTrackingAreaIdentityList &list, const VTrackingAreaIdentity &tai);
+void RemoveFromTaiList(nas::IE5gsTrackingAreaIdentityList &list, const VTrackingAreaIdentity &tai);
+bool PlmnListContains(const IEPlmnList &list, VPlmn item);
+bool PlmnListContains(const IEPlmnList &list, Plmn item);
+bool TaiListContains(const nas::IE5gsTrackingAreaIdentityList &list, const VTrackingAreaIdentity &tai);
+bool TaiListContains(const nas::VPartialTrackingAreaIdentityList &list, const VTrackingAreaIdentity &tai);
+bool ServiceAreaListForbidsPlmn(const nas::IEServiceAreaList &list, const VPlmn &plmn);
+bool ServiceAreaListForbidsTai(const nas::IEServiceAreaList &list, const VTrackingAreaIdentity &tai);
+bool ServiceAreaListForbidsPlmn(const nas::VPartialServiceAreaList &list, const VPlmn &plmn);
+bool ServiceAreaListForbidsTai(const nas::VPartialServiceAreaList &list, const VTrackingAreaIdentity &tai);
const char *EnumToString(ERegistrationType v);
const char *EnumToString(EMmCause v);
diff --git a/src/ue/app/cmd_handler.cpp b/src/ue/app/cmd_handler.cpp
index 1e98f2547..5863d4fdd 100644
--- a/src/ue/app/cmd_handler.cpp
+++ b/src/ue/app/cmd_handler.cpp
@@ -9,9 +9,9 @@
#include "cmd_handler.hpp"
#include
-#include
#include
#include
+#include
#include
#include
#include
@@ -19,6 +19,17 @@
#define PAUSE_CONFIRM_TIMEOUT 3000
#define PAUSE_POLLING 10
+static std::string SignalDescription(int dbm)
+{
+ if (dbm > -90)
+ return "Excellent";
+ if (dbm > -105)
+ return "Good";
+ if (dbm > -120)
+ return "Fair";
+ return "Poor";
+}
+
namespace nr::ue
{
@@ -34,26 +45,26 @@ void UeCmdHandler::sendError(const InetAddress &address, const std::string &outp
void UeCmdHandler::pauseTasks()
{
- m_base->mrTask->requestPause();
m_base->nasTask->requestPause();
m_base->rrcTask->requestPause();
+ m_base->rlsTask->requestPause();
}
void UeCmdHandler::unpauseTasks()
{
- m_base->mrTask->requestUnpause();
m_base->nasTask->requestUnpause();
m_base->rrcTask->requestUnpause();
+ m_base->rlsTask->requestUnpause();
}
bool UeCmdHandler::isAllPaused()
{
- if (!m_base->mrTask->isPauseConfirmed())
- return false;
if (!m_base->nasTask->isPauseConfirmed())
return false;
if (!m_base->rrcTask->isPauseConfirmed())
return false;
+ if (!m_base->rlsTask->isPauseConfirmed())
+ return false;
return true;
}
@@ -103,6 +114,8 @@ void UeCmdHandler::handleCmdImpl(NwUeCliCommand &msg)
{"rm-state", ToJson(m_base->nasTask->mm->m_rmState)},
{"mm-state", ToJson(m_base->nasTask->mm->m_mmSubState)},
{"5u-state", ToJson(m_base->nasTask->mm->m_usim->m_uState)},
+ {"camped-cell",
+ ::ToJson(m_base->rlsTask->m_servingCell.has_value() ? m_base->rlsTask->m_servingCell->gnbName : "")},
{"sim-inserted", m_base->nasTask->mm->m_usim->isValid()},
{"stored-suci", ToJson(m_base->nasTask->mm->m_usim->m_storedSuci)},
{"stored-guti", ToJson(m_base->nasTask->mm->m_usim->m_storedGuti)},
@@ -150,6 +163,30 @@ void UeCmdHandler::handleCmdImpl(NwUeCliCommand &msg)
sendResult(msg.address, "PDU session establishment procedure triggered");
break;
}
+ case app::UeCliCommand::COVERAGE: {
+ auto &map = m_base->rlsTask->m_activeMeasurements;
+ if (map.empty())
+ {
+ sendResult(msg.address, "No cell exists in the range");
+ break;
+ }
+
+ std::vector cellInfo{};
+ for (auto &entry : map)
+ {
+ auto &measurement = entry.second;
+ cellInfo.push_back(Json::Obj({
+ {"gnb", measurement.gnbName},
+ {"plmn", ToJson(measurement.cellId.plmn)},
+ {"nci", measurement.cellId.nci},
+ {"tac", measurement.tac},
+ {"signal", std::to_string(measurement.dbm) + "dBm [" + SignalDescription(measurement.dbm) + "]"},
+ }));
+ }
+
+ sendResult(msg.address, Json::Arr(cellInfo).dumpYaml());
+ break;
+ }
}
}
diff --git a/src/ue/app/task.cpp b/src/ue/app/task.cpp
index 22a7c339f..61febf220 100644
--- a/src/ue/app/task.cpp
+++ b/src/ue/app/task.cpp
@@ -9,7 +9,8 @@
#include "task.hpp"
#include "cmd_handler.hpp"
#include
-#include
+#include
+#include
#include
#include
#include
@@ -50,17 +51,17 @@ void UeAppTask::onLoop()
switch (msg->msgType)
{
- case NtsMessageType::UE_MR_TO_APP: {
- auto *w = dynamic_cast(msg);
+ case NtsMessageType::UE_RLS_TO_APP: {
+ auto *w = dynamic_cast(msg);
switch (w->present)
{
- case NwUeMrToApp::DATA_PDU_DELIVERY: {
+ case NwUeRlsToApp::DATA_PDU_DELIVERY: {
auto *tunTask = m_tunTasks[w->psi];
if (tunTask)
{
auto *nw = new NwAppToTun(NwAppToTun::DATA_PDU_DELIVERY);
nw->psi = w->psi;
- nw->data = std::move(w->data);
+ nw->data = std::move(w->pdu);
tunTask->push(nw);
}
break;
@@ -73,10 +74,7 @@ void UeAppTask::onLoop()
switch (w->present)
{
case NwUeTunToApp::DATA_PDU_DELIVERY: {
- auto *nw = new NwAppToMr(NwAppToMr::DATA_PDU_DELIVERY);
- nw->psi = w->psi;
- nw->data = std::move(w->data);
- m_base->mrTask->push(nw);
+ handleUplinkDataRequest(w->psi, std::move(w->data));
break;
}
case NwUeTunToApp::TUN_ERROR: {
@@ -135,6 +133,7 @@ void UeAppTask::receiveStatusUpdate(NwUeStatusUpdate &msg)
if (session->pduAddress.has_value())
sessionInfo.address = utils::OctetStringToIp(session->pduAddress->pduAddressInformation);
sessionInfo.isEmergency = session->isEmergency;
+ sessionInfo.uplinkPending = false;
m_pduSessions[session->psi] = std::move(sessionInfo);
@@ -158,6 +157,12 @@ void UeAppTask::receiveStatusUpdate(NwUeStatusUpdate &msg)
}
return;
}
+
+ if (msg.what == NwUeStatusUpdate::CM_STATE)
+ {
+ m_cmState = msg.cmState;
+ return;
+ }
}
void UeAppTask::setupTunInterface(const PduSession *pduSession)
@@ -219,4 +224,40 @@ void UeAppTask::setupTunInterface(const PduSession *pduSession)
allocatedName.c_str(), ipAddress.c_str());
}
+void UeAppTask::handleUplinkDataRequest(int psi, OctetString &&data)
+{
+ if (!m_pduSessions[psi].has_value())
+ return;
+
+ if (m_cmState == ECmState::CM_CONNECTED)
+ {
+ if (m_pduSessions[psi]->uplinkPending)
+ {
+ m_pduSessions[psi]->uplinkPending = false;
+
+ auto *w = new NwUeAppToNas(NwUeAppToNas::UPLINK_STATUS_CHANGE);
+ w->psi = psi;
+ w->isPending = false;
+ m_base->nasTask->push(w);
+ }
+
+ auto *nw = new NwUeAppToRls(NwUeAppToRls::DATA_PDU_DELIVERY);
+ nw->psi = psi;
+ nw->pdu = std::move(data);
+ m_base->rlsTask->push(nw);
+ }
+ else
+ {
+ if (!m_pduSessions[psi]->uplinkPending)
+ {
+ m_pduSessions[psi]->uplinkPending = true;
+
+ auto *w = new NwUeAppToNas(NwUeAppToNas::UPLINK_STATUS_CHANGE);
+ w->psi = psi;
+ w->isPending = true;
+ m_base->nasTask->push(w);
+ }
+ }
+}
+
} // namespace nr::ue
diff --git a/src/ue/app/task.hpp b/src/ue/app/task.hpp
index 75a528b21..ceab73bf8 100644
--- a/src/ue/app/task.hpp
+++ b/src/ue/app/task.hpp
@@ -29,6 +29,7 @@ class UeAppTask : public NtsTask
std::array, 16> m_pduSessions{};
std::array m_tunTasks{};
+ ECmState m_cmState{};
friend class UeCmdHandler;
@@ -44,6 +45,7 @@ class UeAppTask : public NtsTask
private:
void receiveStatusUpdate(NwUeStatusUpdate &msg);
void setupTunInterface(const PduSession *pduSession);
+ void handleUplinkDataRequest(int psi, OctetString &&data);
};
} // namespace nr::ue
diff --git a/src/ue/mm/radio.cpp b/src/ue/mm/radio.cpp
deleted file mode 100644
index 90179caba..000000000
--- a/src/ue/mm/radio.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-//
-// 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 "mm.hpp"
-#include
-#include
-#include
-#include
-
-namespace nr::ue
-{
-
-void NasMm::handlePlmnSearchResponse(const std::string &gnbName)
-{
- if (m_base->nodeListener)
- m_base->nodeListener->onConnected(app::NodeType::UE, m_base->config->getNodeName(), app::NodeType::GNB,
- gnbName);
-
- m_logger->info("UE connected to gNB");
-
- if (m_mmSubState == EMmSubState::MM_REGISTERED_PLMN_SEARCH ||
- m_mmSubState == EMmSubState::MM_REGISTERED_NO_CELL_AVAILABLE)
- switchMmState(EMmState::MM_REGISTERED, EMmSubState::MM_REGISTERED_NORMAL_SERVICE);
- else if (m_mmSubState == EMmSubState::MM_DEREGISTERED_PLMN_SEARCH ||
- m_mmSubState == EMmSubState::MM_DEREGISTERED_NO_CELL_AVAILABLE)
- switchMmState(EMmState::MM_DEREGISTERED, EMmSubState::MM_DEREGISTERED_NORMAL_SERVICE);
-
- resetRegAttemptCounter();
-}
-
-void NasMm::handlePlmnSearchFailure()
-{
- if (m_mmSubState == EMmSubState::MM_REGISTERED_PLMN_SEARCH)
- switchMmState(EMmState::MM_REGISTERED, EMmSubState::MM_REGISTERED_NO_CELL_AVAILABLE);
- else if (m_mmSubState == EMmSubState::MM_DEREGISTERED_PLMN_SEARCH)
- switchMmState(EMmState::MM_DEREGISTERED, EMmSubState::MM_DEREGISTERED_NO_CELL_AVAILABLE);
-}
-
-void NasMm::handleRrcConnectionSetup()
-{
- switchCmState(ECmState::CM_CONNECTED);
-}
-
-void NasMm::handleRrcConnectionRelease()
-{
- switchCmState(ECmState::CM_IDLE);
-}
-
-void NasMm::handleRadioLinkFailure()
-{
- m_logger->debug("Radio link failure detected");
- handleRrcConnectionRelease();
-}
-
-void NasMm::localReleaseConnection()
-{
- m_logger->info("Performing local release of NAS connection");
-
- m_base->rrcTask->push(new NwUeNasToRrc(NwUeNasToRrc::LOCAL_RELEASE_CONNECTION));
-}
-
-} // namespace nr::ue
\ No newline at end of file
diff --git a/src/ue/mm/service.cpp b/src/ue/mm/service.cpp
deleted file mode 100644
index 594c2412d..000000000
--- a/src/ue/mm/service.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-//
-// 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 "mm.hpp"
-
-namespace nr::ue
-{
-
-void NasMm::receiveServiceAccept(const nas::ServiceAccept &msg)
-{
- if (msg.eapMessage.has_value())
- {
- if (msg.eapMessage->eap->code == eap::ECode::FAILURE)
- receiveEapFailureMessage(*msg.eapMessage->eap);
- else
- m_logger->warn("Network sent EAP with inconvenient type in ServiceAccept, ignoring EAP IE.");
- }
-
- // TODO
-}
-
-void NasMm::receiveServiceReject(const nas::ServiceReject &msg)
-{
- if (msg.eapMessage.has_value())
- {
- if (msg.eapMessage->eap->code == eap::ECode::FAILURE)
- receiveEapFailureMessage(*msg.eapMessage->eap);
- else
- m_logger->warn("Network sent EAP with inconvenient type in ServiceAccept, ignoring EAP IE.");
- }
-
- // TODO
-}
-
-} // namespace nr::ue
diff --git a/src/ue/mr/rls.cpp b/src/ue/mr/rls.cpp
deleted file mode 100644
index 69682859e..000000000
--- a/src/ue/mr/rls.cpp
+++ /dev/null
@@ -1,76 +0,0 @@
-//
-// 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 "rls.hpp"
-#include
-#include
-
-namespace nr::ue
-{
-
-UeRls::UeRls(const std::string &nodeName, const std::vector &gnbSearchList, std::unique_ptr logger,
- NtsTask *targetTask)
- : RlsUeEntity(nodeName, gnbSearchList), m_logger(std::move(logger)), m_targetTask(targetTask)
-{
-}
-
-void UeRls::logWarn(const std::string &msg)
-{
- m_logger->warn(msg);
-}
-
-void UeRls::logError(const std::string &msg)
-{
- m_logger->err(msg);
-}
-
-void UeRls::startWaitingTimer(int period)
-{
- auto *w = new NwUeMrToMr(NwUeMrToMr::RLS_START_WAITING_TIMER);
- w->period = period;
- m_targetTask->push(w);
-}
-
-void UeRls::searchFailure(rls::ECause cause)
-{
- auto *w = new NwUeMrToMr(NwUeMrToMr::RLS_SEARCH_FAILURE);
- w->cause = cause;
- m_targetTask->push(w);
-}
-
-void UeRls::onRelease(rls::ECause cause)
-{
- auto *w = new NwUeMrToMr(NwUeMrToMr::RLS_RELEASED);
- w->cause = cause;
- m_targetTask->push(w);
-}
-
-void UeRls::onConnect(const std::string &gnbName)
-{
- auto *w = new NwUeMrToMr(NwUeMrToMr::RLS_CONNECTED);
- w->gnbName = gnbName;
- m_targetTask->push(w);
-}
-
-void UeRls::sendRlsPdu(const InetAddress &address, OctetString &&pdu)
-{
- auto *w = new NwUeMrToMr(NwUeMrToMr::RLS_SEND_OVER_UDP);
- w->address = address;
- w->pdu = std::move(pdu);
- m_targetTask->push(w);
-}
-
-void UeRls::deliverPayload(rls::EPayloadType type, OctetString &&payload)
-{
- auto *w = new NwUeMrToMr(NwUeMrToMr::RLS_RECEIVE_OVER_UDP);
- w->type = type;
- w->pdu = std::move(payload);
- m_targetTask->push(w);
-}
-
-} // namespace nr::ue
diff --git a/src/ue/mr/rls.hpp b/src/ue/mr/rls.hpp
deleted file mode 100644
index 3ba47016e..000000000
--- a/src/ue/mr/rls.hpp
+++ /dev/null
@@ -1,39 +0,0 @@
-//
-// 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.
-//
-
-#pragma once
-
-#include
-#include
-#include
-
-namespace nr::ue
-{
-
-class UeRls : public rls::RlsUeEntity
-{
- private:
- std::unique_ptr m_logger;
- NtsTask *m_targetTask;
-
- public:
- UeRls(const std::string &nodeName, const std::vector &gnbSearchList, std::unique_ptr logger,
- NtsTask *targetTask);
-
- protected:
- void logWarn(const std::string &msg) override;
- void logError(const std::string &msg) override;
- void startWaitingTimer(int period) override;
- void searchFailure(rls::ECause cause) override;
- void onRelease(rls::ECause cause) override;
- void onConnect(const std::string &gnbName) override;
- void sendRlsPdu(const InetAddress &address, OctetString &&pdu) override;
- void deliverPayload(rls::EPayloadType type, OctetString &&payload) override;
-};
-
-} // namespace nr::ue
\ No newline at end of file
diff --git a/src/ue/mr/task.cpp b/src/ue/mr/task.cpp
deleted file mode 100644
index d2a1a1233..000000000
--- a/src/ue/mr/task.cpp
+++ /dev/null
@@ -1,192 +0,0 @@
-//
-// 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 "task.hpp"
-#include
-#include
-#include
-#include
-#include
-
-static const int TIMER_ID_RLS_WAITING_TIMER = 1;
-static const int TIMER_ID_RLS_HEARTBEAT = 2;
-static const int PLMN_SEARCH_FAILED_PRINT_THRESHOLD = 10000;
-
-namespace nr::ue
-{
-
-ue::UeMrTask::UeMrTask(TaskBase *base) : m_base{base}, m_udpTask{}, m_rlsEntity{}, m_lastPlmnSearchFailurePrinted{}
-{
- m_logger = m_base->logBase->makeUniqueLogger(m_base->config->getLoggerPrefix() + "mr");
-}
-
-void UeMrTask::onStart()
-{
- m_udpTask = new udp::UdpServerTask(this);
-
- std::vector gnbSearchList{};
- for (auto &ip : m_base->config->gnbSearchList)
- gnbSearchList.emplace_back(ip, cons::PortalPort);
-
- m_rlsEntity = new UeRls(m_base->config->getNodeName(), gnbSearchList,
- m_base->logBase->makeUniqueLogger(m_base->config->getLoggerPrefix() + "rls"), this);
-
- m_udpTask->start();
-
- setTimer(TIMER_ID_RLS_HEARTBEAT, rls::Constants::HB_PERIOD_UE_TO_GNB);
-}
-
-void UeMrTask::onQuit()
-{
- delete m_rlsEntity;
-
- m_udpTask->quit();
- delete m_udpTask;
-}
-
-void UeMrTask::onLoop()
-{
- NtsMessage *msg = take();
- if (!msg)
- return;
-
- switch (msg->msgType)
- {
- case NtsMessageType::UE_MR_TO_MR: {
- auto *w = dynamic_cast(msg);
- switch (w->present)
- {
- case NwUeMrToMr::RLS_CONNECTED: {
- auto tw = new NwUeMrToRrc(NwUeMrToRrc::PLMN_SEARCH_RESPONSE);
- tw->gnbName = std::move(w->gnbName);
- m_base->rrcTask->push(tw);
- break;
- }
- case NwUeMrToMr::RLS_RELEASED: {
- if (rls::IsRlf(w->cause))
- {
- m_logger->err("Radio link failure with cause[%s]", rls::CauseToString(w->cause));
- m_base->rrcTask->push(new NwUeMrToRrc(NwUeMrToRrc::RADIO_LINK_FAILURE));
- }
- else
- {
- m_logger->debug("UE disconnected from gNB [%s]", rls::CauseToString(w->cause));
- }
- break;
- }
- case NwUeMrToMr::RLS_SEARCH_FAILURE: {
- long current = utils::CurrentTimeMillis();
- if (current - m_lastPlmnSearchFailurePrinted > PLMN_SEARCH_FAILED_PRINT_THRESHOLD)
- {
- m_logger->err("PLMN search failed [%s]", rls::CauseToString(w->cause));
- m_lastPlmnSearchFailurePrinted = current;
- m_base->rrcTask->push(new NwUeMrToRrc(NwUeMrToRrc::PLMN_SEARCH_FAILURE));
- }
- break;
- }
- case NwUeMrToMr::RLS_START_WAITING_TIMER: {
- setTimer(TIMER_ID_RLS_WAITING_TIMER, w->period);
- break;
- }
- case NwUeMrToMr::RLS_SEND_OVER_UDP: {
- m_udpTask->send(w->address, w->pdu);
- break;
- }
- case NwUeMrToMr::RLS_RECEIVE_OVER_UDP: {
- receiveDownlinkPayload(w->type, std::move(w->pdu));
- break;
- }
- }
- break;
- }
- case NtsMessageType::UE_RRC_TO_MR: {
- auto *w = dynamic_cast(msg);
- switch (w->present)
- {
- case NwUeRrcToMr::PLMN_SEARCH_REQUEST: {
- m_rlsEntity->startGnbSearch();
- break;
- }
- case NwUeRrcToMr::RRC_PDU_DELIVERY: {
- // Append channel information
- OctetString stream{};
- stream.appendOctet(static_cast(w->channel));
- stream.append(w->pdu);
-
- m_rlsEntity->onUplinkDelivery(rls::EPayloadType::RRC, std::move(stream));
- break;
- }
- case NwUeRrcToMr::RRC_CONNECTION_RELEASE: {
- m_rlsEntity->localReleaseConnection(w->cause);
- m_rlsEntity->resetEntity();
- break;
- }
- }
- break;
- }
- case NtsMessageType::UE_APP_TO_MR: {
- auto *w = dynamic_cast(msg);
- switch (w->present)
- {
- case NwAppToMr::DATA_PDU_DELIVERY: {
- // Append PDU session information
- OctetString stream{};
- stream.appendOctet4(w->psi);
- stream.append(w->data);
-
- m_rlsEntity->onUplinkDelivery(rls::EPayloadType::DATA, std::move(stream));
- break;
- }
- }
- break;
- }
- case NtsMessageType::TIMER_EXPIRED: {
- auto *w = dynamic_cast(msg);
- if (w->timerId == TIMER_ID_RLS_WAITING_TIMER)
- {
- m_rlsEntity->onWaitingTimerExpire();
- }
- else if (w->timerId == TIMER_ID_RLS_HEARTBEAT)
- {
- setTimer(TIMER_ID_RLS_HEARTBEAT, rls::Constants::HB_PERIOD_UE_TO_GNB);
- m_rlsEntity->onHeartbeat();
- }
- break;
- }
- case NtsMessageType::UDP_SERVER_RECEIVE: {
- auto *w = dynamic_cast(msg);
- m_rlsEntity->onReceive(w->fromAddress, w->packet);
- break;
- }
- default:
- m_logger->unhandledNts(msg);
- break;
- }
-
- delete msg;
-}
-
-void UeMrTask::receiveDownlinkPayload(rls::EPayloadType type, OctetString &&payload)
-{
- if (type == rls::EPayloadType::RRC)
- {
- auto *nw = new NwUeMrToRrc(NwUeMrToRrc::RRC_PDU_DELIVERY);
- nw->channel = static_cast(payload.getI(0));
- nw->pdu = payload.subCopy(1);
- m_base->rrcTask->push(nw);
- }
- else if (type == rls::EPayloadType::DATA)
- {
- auto *nw = new NwUeMrToApp(NwUeMrToApp::DATA_PDU_DELIVERY);
- nw->psi = payload.get4I(0);
- nw->data = payload.subCopy(4);
- m_base->appTask->push(nw);
- }
-}
-
-} // namespace nr::ue
diff --git a/src/ue/mr/task.hpp b/src/ue/mr/task.hpp
deleted file mode 100644
index e5d780831..000000000
--- a/src/ue/mr/task.hpp
+++ /dev/null
@@ -1,50 +0,0 @@
-//
-// 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.
-//
-
-#pragma once
-
-#include "rls.hpp"
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-namespace nr::ue
-{
-
-class UeMrTask : public NtsTask
-{
- private:
- TaskBase *m_base;
- std::unique_ptr m_logger;
-
- udp::UdpServerTask *m_udpTask;
- UeRls *m_rlsEntity;
-
- long m_lastPlmnSearchFailurePrinted;
-
- friend class UeCmdHandler;
-
- public:
- explicit UeMrTask(TaskBase *base);
- ~UeMrTask() override = default;
-
- protected:
- void onStart() override;
- void onLoop() override;
- void onQuit() override;
-
- private:
- void receiveDownlinkPayload(rls::EPayloadType type, OctetString &&payload);
-};
-
-} // namespace nr::ue
\ No newline at end of file
diff --git a/src/ue/nas/enc.cpp b/src/ue/nas/enc.cpp
index 4439f01f8..8f8db4a1c 100644
--- a/src/ue/nas/enc.cpp
+++ b/src/ue/nas/enc.cpp
@@ -14,12 +14,13 @@
namespace nr::ue::nas_enc
{
-static nas::ESecurityHeaderType MakeSecurityHeaderType(const NasSecurityContext &ctx, nas::EMessageType msgType)
+static nas::ESecurityHeaderType MakeSecurityHeaderType(const NasSecurityContext &ctx, nas::EMessageType msgType,
+ bool bypassCiphering)
{
auto &encKey = ctx.keys.kNasEnc;
auto &intKey = ctx.keys.kNasInt;
- bool ciphered = encKey.length() > 0;
+ bool ciphered = !bypassCiphering && encKey.length() > 0;
bool integrityProtected = intKey.length() > 0;
if (!ciphered && !integrityProtected)
@@ -64,7 +65,7 @@ static OctetString EncryptData(nas::ETypeOfCipheringAlgorithm alg, const NasCoun
}
static std::unique_ptr Encrypt(NasSecurityContext &ctx, OctetString &&plainNasMessage,
- nas::EMessageType msgType)
+ nas::EMessageType msgType, bool bypassCiphering)
{
auto count = ctx.uplinkCount;
auto is3gppAccess = ctx.is3gppAccess;
@@ -73,12 +74,13 @@ static std::unique_ptr Encrypt(NasSecurityContext &ctx, O
auto intAlg = ctx.integrity;
auto encAlg = ctx.ciphering;
- auto encryptedData = EncryptData(encAlg, count, is3gppAccess, plainNasMessage, encKey);
+ auto encryptedData =
+ bypassCiphering ? plainNasMessage.copy() : EncryptData(encAlg, count, is3gppAccess, plainNasMessage, encKey);
auto mac = ComputeMac(intAlg, count, is3gppAccess, true, intKey, encryptedData);
auto secured = std::make_unique();
secured->epd = nas::EExtendedProtocolDiscriminator::MOBILITY_MANAGEMENT_MESSAGES;
- secured->sht = MakeSecurityHeaderType(ctx, msgType);
+ secured->sht = MakeSecurityHeaderType(ctx, msgType, bypassCiphering);
secured->messageAuthenticationCode = octet4{mac};
secured->sequenceNumber = count.sqn;
secured->plainNasMessage = std::move(encryptedData);
@@ -120,24 +122,15 @@ static OctetString DecryptData(nas::ETypeOfCipheringAlgorithm alg, const NasCoun
return msg;
}
-std::unique_ptr Encrypt(NasSecurityContext &ctx, const nas::PlainMmMessage &msg)
+std::unique_ptr Encrypt(NasSecurityContext &ctx, const nas::PlainMmMessage &msg,
+ bool bypassCiphering)
{
nas::EMessageType msgType = msg.messageType;
OctetString stream;
nas::EncodeNasMessage(msg, stream);
- return Encrypt(ctx, std::move(stream), msgType);
-}
-
-std::unique_ptr Encrypt(NasSecurityContext &ctx, const nas::SmMessage &msg)
-{
- nas::EMessageType msgType = msg.messageType;
-
- OctetString stream;
- nas::EncodeNasMessage(msg, stream);
-
- return Encrypt(ctx, std::move(stream), msgType);
+ return Encrypt(ctx, std::move(stream), msgType, bypassCiphering);
}
std::unique_ptr Decrypt(NasSecurityContext &ctx, const nas::SecuredMmMessage &msg)
diff --git a/src/ue/nas/enc.hpp b/src/ue/nas/enc.hpp
index 2973585d1..e20b86830 100644
--- a/src/ue/nas/enc.hpp
+++ b/src/ue/nas/enc.hpp
@@ -14,7 +14,7 @@
namespace nr::ue::nas_enc
{
-std::unique_ptr Encrypt(NasSecurityContext &ctx, const nas::PlainMmMessage &msg);
+std::unique_ptr Encrypt(NasSecurityContext &ctx, const nas::PlainMmMessage &msg, bool bypassCiphering);
std::unique_ptr Decrypt(NasSecurityContext &ctx, const nas::SecuredMmMessage &msg);
uint32_t ComputeMac(nas::ETypeOfIntegrityProtectionAlgorithm alg, NasCount count, bool is3gppAccess,
diff --git a/src/ue/mm/access.cpp b/src/ue/nas/mm/access.cpp
similarity index 59%
rename from src/ue/mm/access.cpp
rename to src/ue/nas/mm/access.cpp
index b8ec3baca..7378072af 100644
--- a/src/ue/mm/access.cpp
+++ b/src/ue/nas/mm/access.cpp
@@ -9,7 +9,7 @@
#include "mm.hpp"
#include
-#include
+#include
namespace nr::ue
{
@@ -25,7 +25,7 @@ bool NasMm::hasEmergency()
m_lastRegistrationRequest->registrationType.registrationType == nas::ERegistrationType::EMERGENCY_REGISTRATION)
return true;
- // TODO: Other case which is an emergency PDU session is established, or need to be established (and wanted to be
+ // TODO: Other case which is an emergency PDU session need to be established (and wanted to be
// established soon)
if (m_sm->anyEmergencySession())
return true;
@@ -43,4 +43,27 @@ void NasMm::setN1Capability(bool enabled)
// TODO
}
+bool NasMm::isInNonAllowedArea()
+{
+ if (!m_usim->isValid())
+ return false;
+ if (!m_usim->m_currentPlmn.has_value())
+ return false;
+
+ auto &plmn = *m_usim->m_currentPlmn;
+
+ if (nas::utils::ServiceAreaListForbidsPlmn(m_usim->m_serviceAreaList, nas::utils::PlmnFrom(plmn)))
+ return true;
+
+ if (m_usim->m_servingCell.has_value())
+ {
+ if (nas::utils::ServiceAreaListForbidsTai(
+ m_usim->m_serviceAreaList,
+ nas::VTrackingAreaIdentity{nas::utils::PlmnFrom(plmn), octet3{m_usim->m_servingCell->tac}}))
+ return true;
+ }
+
+ return false;
+}
+
} // namespace nr::ue
diff --git a/src/ue/mm/auth.cpp b/src/ue/nas/mm/auth.cpp
similarity index 98%
rename from src/ue/mm/auth.cpp
rename to src/ue/nas/mm/auth.cpp
index 20f22cd76..ceaf89c2e 100644
--- a/src/ue/mm/auth.cpp
+++ b/src/ue/nas/mm/auth.cpp
@@ -80,7 +80,7 @@ void NasMm::receiveAuthenticationRequestEap(const nas::AuthenticationRequest &ms
auto sqnXorAk = OctetString::Xor(m_usim->m_sqn, milenageAk);
auto ckPrimeIkPrime =
- keys::CalculateCkPrimeIkPrime(ck, ik, keys::ConstructServingNetworkName(m_usim->m_currentPlmn), sqnXorAk);
+ keys::CalculateCkPrimeIkPrime(ck, ik, keys::ConstructServingNetworkName(*m_usim->m_currentPlmn), sqnXorAk);
auto &ckPrime = ckPrimeIkPrime.first;
auto &ikPrime = ckPrimeIkPrime.second;
@@ -198,7 +198,7 @@ void NasMm::receiveAuthenticationRequestEap(const nas::AuthenticationRequest &ms
m_usim->m_nonCurrentNsCtx->keys.kAusf = std::move(kAusf);
m_usim->m_nonCurrentNsCtx->keys.abba = msg.abba.rawData.copy();
- keys::DeriveKeysSeafAmf(*m_base->config, m_usim->m_currentPlmn, *m_usim->m_nonCurrentNsCtx);
+ keys::DeriveKeysSeafAmf(*m_base->config, *m_usim->m_currentPlmn, *m_usim->m_nonCurrentNsCtx);
// m_logger->debug("kSeaf: %s", m_usim->m_nonCurrentNsCtx->keys.kSeaf.toHexString().c_str());
// m_logger->debug("kAmf: %s", m_usim->m_nonCurrentNsCtx->keys.kAmf.toHexString().c_str());
@@ -266,7 +266,7 @@ void NasMm::receiveAuthenticationRequest5gAka(const nas::AuthenticationRequest &
auto &milenageAk = milenage.ak;
auto &milenageMac = milenage.mac_a;
auto sqnXorAk = OctetString::Xor(m_usim->m_sqn, milenageAk);
- auto snn = keys::ConstructServingNetworkName(m_usim->m_currentPlmn);
+ auto snn = keys::ConstructServingNetworkName(*m_usim->m_currentPlmn);
// m_logger->debug("Calculated res[%s] ck[%s] ik[%s] ak[%s] mac_a[%s]", res.toHexString().c_str(),
// ck.toHexString().c_str(), ik.toHexString().c_str(), milenageAk.toHexString().c_str(),
@@ -287,7 +287,7 @@ void NasMm::receiveAuthenticationRequest5gAka(const nas::AuthenticationRequest &
m_usim->m_nonCurrentNsCtx->keys.kAusf = keys::CalculateKAusfFor5gAka(ck, ik, snn, sqnXorAk);
m_usim->m_nonCurrentNsCtx->keys.abba = msg.abba.rawData.copy();
- keys::DeriveKeysSeafAmf(*m_base->config, m_usim->m_currentPlmn, *m_usim->m_nonCurrentNsCtx);
+ keys::DeriveKeysSeafAmf(*m_base->config, *m_usim->m_currentPlmn, *m_usim->m_nonCurrentNsCtx);
// m_logger->debug("Derived kSeaf[%s] kAusf[%s] kAmf[%s]",
// m_usim->m_nonCurrentNsCtx->keys.kSeaf.toHexString().c_str(),
diff --git a/src/ue/mm/base.cpp b/src/ue/nas/mm/base.cpp
similarity index 72%
rename from src/ue/mm/base.cpp
rename to src/ue/nas/mm/base.cpp
index aaeedd39f..f9ba16c3d 100644
--- a/src/ue/mm/base.cpp
+++ b/src/ue/nas/mm/base.cpp
@@ -60,7 +60,23 @@ void NasMm::performMmCycle()
if (m_cmState == ECmState::CM_IDLE)
switchMmState(EMmState::MM_DEREGISTERED, EMmSubState::MM_DEREGISTERED_PLMN_SEARCH);
else
- switchMmState(EMmState::MM_DEREGISTERED, EMmSubState::MM_DEREGISTERED_NORMAL_SERVICE);
+ {
+ if (m_usim->m_servingCell.has_value())
+ {
+ auto cellCategory = m_usim->m_servingCell->cellCategory;
+
+ if (cellCategory == ECellCategory::SUITABLE_CELL)
+ switchMmState(EMmState::MM_DEREGISTERED, EMmSubState::MM_DEREGISTERED_NORMAL_SERVICE);
+ else if (cellCategory == ECellCategory::ACCEPTABLE_CELL)
+ switchMmState(EMmState::MM_DEREGISTERED, EMmSubState::MM_DEREGISTERED_LIMITED_SERVICE);
+ else
+ switchMmState(EMmState::MM_DEREGISTERED, EMmSubState::MM_DEREGISTERED_PLMN_SEARCH);
+ }
+ else
+ {
+ switchMmState(EMmState::MM_DEREGISTERED, EMmSubState::MM_DEREGISTERED_PLMN_SEARCH);
+ }
+ }
}
else
{
@@ -70,11 +86,16 @@ void NasMm::performMmCycle()
if (m_mmSubState == EMmSubState::MM_DEREGISTERED_PLMN_SEARCH ||
m_mmSubState == EMmSubState::MM_DEREGISTERED_NO_CELL_AVAILABLE ||
+ m_mmSubState == EMmSubState::MM_REGISTERED_PLMN_SEARCH ||
m_mmSubState == EMmSubState::MM_REGISTERED_NO_CELL_AVAILABLE)
{
- long current = utils::CurrentTimeMillis();
- long elapsedMs = current - m_lastPlmnSearchTrigger;
- if (elapsedMs > 50)
+ int64_t current = utils::CurrentTimeMillis();
+ int64_t backoff = (m_mmSubState == EMmSubState::MM_DEREGISTERED_PLMN_SEARCH ||
+ m_mmSubState == EMmSubState::MM_REGISTERED_PLMN_SEARCH)
+ ? -1
+ : 1500;
+
+ if (current - m_lastPlmnSearchTrigger > backoff)
{
m_base->rrcTask->push(new NwUeNasToRrc(NwUeNasToRrc::PLMN_SEARCH_REQUEST));
m_lastPlmnSearchTrigger = current;
@@ -85,7 +106,7 @@ void NasMm::performMmCycle()
if (m_mmSubState == EMmSubState::MM_DEREGISTERED_NORMAL_SERVICE)
{
if (!m_timers->t3346.isRunning())
- sendInitialRegistration(false, false);
+ sendInitialRegistration(EInitialRegCause::MM_DEREG_NORMAL_SERVICE);
return;
}
@@ -94,10 +115,44 @@ void NasMm::performMmCycle()
if (startECallInactivityIfNeeded())
return;
}
+
+ if (m_mmSubState == EMmSubState::MM_REGISTERED_NA)
+ {
+ if (m_usim->m_servingCell.has_value())
+ {
+ auto cellCategory = m_usim->m_servingCell->cellCategory;
+
+ if (cellCategory == ECellCategory::SUITABLE_CELL)
+ switchMmState(EMmState::MM_REGISTERED, EMmSubState::MM_REGISTERED_NORMAL_SERVICE);
+ else if (cellCategory == ECellCategory::ACCEPTABLE_CELL)
+ switchMmState(EMmState::MM_REGISTERED, EMmSubState::MM_REGISTERED_LIMITED_SERVICE);
+ else
+ switchMmState(EMmState::MM_REGISTERED, EMmSubState::MM_REGISTERED_PLMN_SEARCH);
+ }
+ else
+ {
+ switchMmState(EMmState::MM_REGISTERED, EMmSubState::MM_REGISTERED_PLMN_SEARCH);
+ }
+ }
}
void NasMm::switchMmState(EMmState state, EMmSubState subState)
{
+ ERmState oldRmState = m_rmState;
+ if (state == EMmState::MM_DEREGISTERED || state == EMmState::MM_REGISTERED_INITIATED)
+ m_rmState = ERmState::RM_DEREGISTERED;
+ else if (state == EMmState::MM_REGISTERED || state == EMmState::MM_SERVICE_REQUEST_INITIATED ||
+ state == EMmState::MM_DEREGISTERED_INITIATED)
+ m_rmState = ERmState::RM_REGISTERED;
+
+ onSwitchRmState(oldRmState, m_rmState);
+
+ if (m_base->nodeListener)
+ {
+ m_base->nodeListener->onSwitch(app::NodeType::UE, m_base->config->getNodeName(), app::StateType::RM,
+ ToJson(oldRmState).str(), ToJson(m_rmState).str());
+ }
+
EMmState oldState = m_mmState;
EMmSubState oldSubState = m_mmSubState;
@@ -120,41 +175,26 @@ void NasMm::switchMmState(EMmState state, EMmSubState subState)
triggerMmCycle();
}
-void NasMm::switchRmState(ERmState state)
-{
- ERmState oldState = m_rmState;
- m_rmState = state;
-
- onSwitchRmState(oldState, m_rmState);
-
- if (m_base->nodeListener)
- {
- m_base->nodeListener->onSwitch(app::NodeType::UE, m_base->config->getNodeName(), app::StateType::RM,
- ToJson(oldState).str(), ToJson(m_rmState).str());
- }
-
- // No need to log it
- // m_logger->info("UE switches to state [%s]", RmStateName(state));
-
- triggerMmCycle();
-}
-
void NasMm::switchCmState(ECmState state)
{
ECmState oldState = m_cmState;
m_cmState = state;
+ if (state != oldState)
+ m_logger->info("UE switches to state [%s]", ToJson(state).str().c_str());
+
onSwitchCmState(oldState, m_cmState);
+ auto *statusUpdate = new NwUeStatusUpdate(NwUeStatusUpdate::CM_STATE);
+ statusUpdate->cmState = m_cmState;
+ m_base->appTask->push(statusUpdate);
+
if (m_base->nodeListener)
{
m_base->nodeListener->onSwitch(app::NodeType::UE, m_base->config->getNodeName(), app::StateType::CM,
ToJson(oldState).str(), ToJson(m_cmState).str());
}
- if (state != oldState)
- m_logger->info("UE switches to state [%s]", ToJson(state).str().c_str());
-
triggerMmCycle();
}
@@ -214,7 +254,6 @@ void NasMm::onSwitchCmState(ECmState oldState, ECmState newState)
if (regType == nas::ERegistrationType::INITIAL_REGISTRATION ||
regType == nas::ERegistrationType::EMERGENCY_REGISTRATION)
{
- switchRmState(ERmState::RM_DEREGISTERED);
switchMmState(EMmState::MM_DEREGISTERED, EMmSubState::MM_DEREGISTERED_NA);
switchUState(E5UState::U2_NOT_UPDATED);
@@ -238,8 +277,6 @@ void NasMm::onSwitchCmState(ECmState oldState, ECmState newState)
else if (m_lastDeregistrationRequest->deRegistrationType.switchOff ==
nas::ESwitchOff::NORMAL_DE_REGISTRATION)
switchMmState(EMmState::MM_DEREGISTERED, EMmSubState::MM_DEREGISTERED_NA);
-
- switchRmState(ERmState::RM_DEREGISTERED);
}
}
}
diff --git a/src/ue/mm/config.cpp b/src/ue/nas/mm/config.cpp
similarity index 100%
rename from src/ue/mm/config.cpp
rename to src/ue/nas/mm/config.cpp
diff --git a/src/ue/mm/dereg.cpp b/src/ue/nas/mm/dereg.cpp
similarity index 96%
rename from src/ue/mm/dereg.cpp
rename to src/ue/nas/mm/dereg.cpp
index 2fcc7f0b5..d40c72ff8 100644
--- a/src/ue/mm/dereg.cpp
+++ b/src/ue/nas/mm/dereg.cpp
@@ -9,7 +9,7 @@
#include "mm.hpp"
#include
#include
-#include
+#include
namespace nr::ue
{
@@ -77,7 +77,6 @@ void NasMm::sendDeregistration(EDeregCause deregCause)
switchMmState(EMmState::MM_DEREGISTERED_INITIATED, EMmSubState::MM_DEREGISTERED_INITIATED_NA);
else
{
- switchRmState(ERmState::RM_DEREGISTERED);
switchMmState(EMmState::MM_DEREGISTERED, EMmSubState::MM_DEREGISTERED_NA);
}
}
@@ -100,8 +99,6 @@ void NasMm::receiveDeregistrationAccept(const nas::DeRegistrationAcceptUeOrigina
m_sm->localReleaseAllSessions();
- switchRmState(ERmState::RM_DEREGISTERED);
-
if (m_lastDeregCause == EDeregCause::DISABLE_5G)
switchMmState(EMmState::MM_NULL, EMmSubState::MM_NULL_NA);
else
@@ -158,6 +155,13 @@ void NasMm::receiveDeregistrationRequest(const nas::DeRegistrationRequestUeTermi
forceIgnoreReregistration = true;
}
}
+ // 5.6.1.7 Abnormal cases in the UE (de-registration collision)
+ else if (m_mmState == EMmState::MM_SERVICE_REQUEST_INITIATED)
+ {
+ // "UE shall progress the DEREGISTRATION REQUEST message and the service request procedure shall be aborted."
+ // (no specific action is required herein to abort service request procedure)
+ (void)0;
+ }
bool reRegistrationRequired =
msg.deRegistrationType.reRegistrationRequired == nas::EReRegistrationRequired::REQUIRED &&
@@ -174,7 +178,6 @@ void NasMm::receiveDeregistrationRequest(const nas::DeRegistrationRequestUeTermi
}
sendNasMessage(nas::DeRegistrationAcceptUeTerminated{});
- switchRmState(ERmState::RM_DEREGISTERED);
// "Upon sending a DEREGISTRATION ACCEPT message, the UE shall delete the rejected NSSAI as specified in
// subclause 4.6.2.2."
diff --git a/src/ue/mm/ecall.cpp b/src/ue/nas/mm/ecall.cpp
similarity index 100%
rename from src/ue/mm/ecall.cpp
rename to src/ue/nas/mm/ecall.cpp
diff --git a/src/ue/mm/identity.cpp b/src/ue/nas/mm/identity.cpp
similarity index 83%
rename from src/ue/mm/identity.cpp
rename to src/ue/nas/mm/identity.cpp
index 154c64a57..7e470d6b9 100644
--- a/src/ue/mm/identity.cpp
+++ b/src/ue/nas/mm/identity.cpp
@@ -31,6 +31,21 @@ void NasMm::receiveIdentityRequest(const nas::IdentityRequest &msg)
resp.mobileIdentity.type = nas::EIdentityType::IMEISV;
resp.mobileIdentity.value = *m_base->config->imeiSv;
}
+ else if (msg.identityType.value == nas::EIdentityType::GUTI)
+ {
+ resp.mobileIdentity = m_usim->m_storedGuti;
+ }
+ else if (msg.identityType.value == nas::EIdentityType::TMSI)
+ {
+ // TMSI is already a part of GUTI
+ resp.mobileIdentity = m_usim->m_storedGuti;
+ if (resp.mobileIdentity.type != nas::EIdentityType::NO_IDENTITY)
+ {
+ resp.mobileIdentity.type = nas::EIdentityType::TMSI;
+ resp.mobileIdentity.gutiOrTmsi.plmn = {};
+ resp.mobileIdentity.gutiOrTmsi.amfRegionId = {};
+ }
+ }
else
{
resp.mobileIdentity.type = nas::EIdentityType::NO_IDENTITY;
@@ -56,7 +71,7 @@ nas::IE5gsMobileIdentity NasMm::getOrGenerateSuci()
nas::IE5gsMobileIdentity NasMm::generateSuci()
{
auto &supi = m_base->config->supi;
- auto &plmn = m_usim->m_currentPlmn;
+ auto &plmn = m_base->config->hplmn;
if (!supi.has_value())
return {};
diff --git a/src/ue/mm/interface.cpp b/src/ue/nas/mm/interface.cpp
similarity index 66%
rename from src/ue/mm/interface.cpp
rename to src/ue/nas/mm/interface.cpp
index 4843f2822..d1ff872fa 100644
--- a/src/ue/mm/interface.cpp
+++ b/src/ue/nas/mm/interface.cpp
@@ -10,6 +10,8 @@
#include
+static constexpr const int64_t SERVICE_REQUEST_NEEDED_FOR_DATA_THRESHOLD = 1000;
+
namespace nr::ue
{
@@ -22,11 +24,7 @@ void NasMm::handleRrcEvent(const NwUeRrcToNas &msg)
break;
}
case NwUeRrcToNas::PLMN_SEARCH_RESPONSE: {
- handlePlmnSearchResponse(msg.gnbName);
- break;
- }
- case NwUeRrcToNas::PLMN_SEARCH_FAILURE: {
- handlePlmnSearchFailure();
+ handlePlmnSearchResponse(msg.measurements);
break;
}
case NwUeRrcToNas::NAS_DELIVERY: {
@@ -44,6 +42,14 @@ void NasMm::handleRrcEvent(const NwUeRrcToNas &msg)
handleRadioLinkFailure();
break;
}
+ case NwUeRrcToNas::SERVING_CELL_CHANGE: {
+ handleServingCellChange(msg.servingCell);
+ break;
+ }
+ case NwUeRrcToNas::PAGING: {
+ handlePaging(msg.pagingTmsi);
+ break;
+ }
}
}
@@ -77,4 +83,16 @@ bool NasMm::isRegisteredForEmergency()
return isRegistered() && m_registeredForEmergency;
}
+void NasMm::serviceNeededForUplinkData()
+{
+ auto currentTime = utils::CurrentTimeMillis();
+ if (currentTime - m_lastTimeServiceReqNeededIndForData > SERVICE_REQUEST_NEEDED_FOR_DATA_THRESHOLD)
+ {
+ sendServiceRequest(m_cmState == ECmState::CM_CONNECTED ? EServiceReqCause::CONNECTED_UPLINK_DATA_PENDING
+ : EServiceReqCause::IDLE_UPLINK_DATA_PENDING);
+
+ m_lastTimeServiceReqNeededIndForData = currentTime;
+ }
+}
+
} // namespace nr::ue
diff --git a/src/ue/mm/mm.hpp b/src/ue/nas/mm/mm.hpp
similarity index 89%
rename from src/ue/mm/mm.hpp
rename to src/ue/nas/mm/mm.hpp
index 73dd01a31..bb5f5a160 100644
--- a/src/ue/mm/mm.hpp
+++ b/src/ue/nas/mm/mm.hpp
@@ -40,8 +40,12 @@ class NasMm
std::unique_ptr m_lastRegistrationRequest{};
// Most recent de-registration request
std::unique_ptr m_lastDeregistrationRequest{};
+ // Most recent service request
+ std::unique_ptr m_lastServiceRequest{};
// Indicates the last de-registration cause
EDeregCause m_lastDeregCause{};
+ // Indicates the last service request cause
+ EServiceReqCause m_lastServiceReqCause{};
// Last time PLMN search is triggered
long m_lastPlmnSearchTrigger{};
// Registration attempt counter
@@ -52,6 +56,8 @@ class NasMm
bool m_registeredForEmergency{};
// Network feature support information
nas::IE5gsNetworkFeatureSupport m_nwFeatureSupport{};
+ // Last time Service Request needed indication for Data
+ long m_lastTimeServiceReqNeededIndForData{};
friend class UeCmdHandler;
@@ -66,7 +72,6 @@ class NasMm
void triggerMmCycle();
void performMmCycle();
void switchMmState(EMmState state, EMmSubState subState);
- void switchRmState(ERmState state);
void switchCmState(ECmState state);
void switchUState(E5UState state);
void onSwitchMmState(EMmState oldState, EMmState newState, EMmSubState oldSubState, EMmSubState newSubSate);
@@ -80,14 +85,13 @@ class NasMm
void receiveMmMessage(const nas::PlainMmMessage &msg);
void receiveDlNasTransport(const nas::DlNasTransport &msg);
void receiveMmStatus(const nas::FiveGMmStatus &msg);
- void receiveMmCause(const nas::IE5gMmCause &msg);
void sendMmStatus(nas::EMmCause cause);
public: /* Registration */
void sendMobilityRegistration(ERegUpdateCause updateCause);
private: /* Registration */
- void sendInitialRegistration(bool isEmergencyReg, bool dueToDereg);
+ void sendInitialRegistration(EInitialRegCause regCause);
void receiveRegistrationAccept(const nas::RegistrationAccept &msg);
void receiveInitialRegistrationAccept(const nas::RegistrationAccept &msg);
void receiveMobilityRegistrationAccept(const nas::RegistrationAccept &msg);
@@ -133,6 +137,7 @@ class NasMm
nas::IE5gsMobileIdentity getOrGeneratePreferredId();
private: /* Service */
+ void sendServiceRequest(EServiceReqCause reqCause);
void receiveServiceAccept(const nas::ServiceAccept &msg);
void receiveServiceReject(const nas::ServiceReject &msg);
@@ -142,16 +147,18 @@ class NasMm
private: /* Radio */
void localReleaseConnection();
- void handlePlmnSearchResponse(const std::string &gnbName);
- void handlePlmnSearchFailure();
+ void handlePlmnSearchResponse(const std::vector