Unverified Commit fe2a9b37 authored by Ali Güngör's avatar Ali Güngör Committed by GitHub

Merge pull request #292 from aligungr/dev

Release of v3.1.7
parents 976522ec fec05fcc
......@@ -2,7 +2,7 @@
<a href="https://github.com/aligungr/UERANSIM"><img src="/.github/logo.png" width="75" title="UERANSIM"></a>
</p>
<p align="center">
<img src="https://img.shields.io/badge/UERANSIM-v3.1.6-blue" />
<img src="https://img.shields.io/badge/UERANSIM-v3.1.7-blue" />
<img src="https://img.shields.io/badge/3GPP-R15-orange" />
<img src="https://img.shields.io/badge/License-GPL--3.0-green"/>
</p>
......
......@@ -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
......@@ -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:
......
......@@ -150,6 +150,7 @@ static OrderedMap<std::string, CmdEntry> g_gnbCmdEntries = {
{"amf-info", {"Show some status information about the given AMF", "<amf-id>", 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", "<ue-id>", DefaultDesc, false}},
};
static OrderedMap<std::string, CmdEntry> g_ueCmdEntries = {
......@@ -162,6 +163,7 @@ static OrderedMap<std::string, CmdEntry> g_ueCmdEntries = {
{"ps-release-all", {"Trigger PDU session release procedures for all active sessions", "", DefaultDesc, false}},
{"deregister",
{"Perform a de-registration by the UE", "<normal|disable-5g|switch-off|remove-sim>", DefaultDesc, true}},
{"coverage", {"Show gNodeB cell coverage information", "", DefaultDesc, false}},
};
static std::unique_ptr<GnbCliCommand> GnbCliParseImpl(const std::string &subCmd, const opt::OptionsResult &options,
......@@ -199,6 +201,18 @@ static std::unique_ptr<GnbCliCommand> GnbCliParseImpl(const std::string &subCmd,
{
return std::make_unique<GnbCliCommand>(GnbCliCommand::UE_COUNT);
}
else if (subCmd == "ue-release")
{
auto cmd = std::make_unique<GnbCliCommand>(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<UeCliCommand> UeCliParseImpl(const std::string &subCmd, c
}
return cmd;
}
else if (subCmd == "coverage")
{
return std::make_unique<UeCliCommand>(UeCliCommand::COVERAGE);
}
return nullptr;
}
......
......@@ -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
......
......@@ -198,4 +198,12 @@ inline Unique<T> WrapUnique(T *ptr, asn_TYPE_descriptor_t &desc)
return asn::Unique<T>(ptr, asn::Deleter<T>{desc});
}
template <typename T>
inline Unique<T> UniqueCopy(const T &value, asn_TYPE_descriptor_t &desc)
{
auto *ptr = New<T>();
DeepCopy(desc, value, ptr);
return WrapUnique(ptr, desc);
}
} // namespace asn
\ No newline at end of file
......@@ -10,8 +10,8 @@
#include <gnb/app/task.hpp>
#include <gnb/gtp/task.hpp>
#include <gnb/mr/task.hpp>
#include <gnb/ngap/task.hpp>
#include <gnb/rls/task.hpp>
#include <gnb/rrc/task.hpp>
#include <gnb/sctp/task.hpp>
#include <utils/common.hpp>
......@@ -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;
}
}
}
......
......@@ -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 <app/cli_base.hpp>
#include <utils/common.hpp>
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();
}
......
......@@ -10,7 +10,7 @@
#include <asn/ngap/ASN_NGAP_QosFlowSetupRequestItem.h>
#include <gnb/gtp/proto.hpp>
#include <gnb/mr/task.hpp>
#include <gnb/rls/task.hpp>
#include <utils/constants.hpp>
#include <utils/libc_error.hpp>
......@@ -76,12 +76,12 @@ void GtpTask::onLoop()
}
break;
}
case NtsMessageType::GNB_MR_TO_GTP: {
auto *w = dynamic_cast<NwGnbMrToGtp *>(msg);
case NtsMessageType::GNB_RLS_TO_GTP: {
auto *w = dynamic_cast<NwGnbRlsToGtp *>(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;
......
//
// 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 <gnb/nts.hpp>
namespace nr::gnb
{
GnbRls::GnbRls(std::string nodeName, std::unique_ptr<Logger> 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
//
// 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 <urs/rls/gnb_entity.hpp>
#include <utils/logger.hpp>
#include <utils/nts.hpp>
namespace nr::gnb
{
class GnbRls : public rls::RlsGnbEntity
{
private:
std::unique_ptr<Logger> m_logger;
NtsTask *m_targetTask;
public:
explicit GnbRls(std::string nodeName, std::unique_ptr<Logger> 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
//
// 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 <gnb/gtp/task.hpp>
#include <gnb/nts.hpp>
#include <gnb/rrc/task.hpp>
#include <utils/constants.hpp>
#include <utils/libc_error.hpp>
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<NwGnbMrToMr *>(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<NwGnbGtpToMr *>(msg);
switch (w->present)
{
case NwGnbGtpToMr::DATA_PDU_DELIVERY: {
OctetString stream{};
stream.appendOctet4(static_cast<int>(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<NwGnbRrcToMr *>(msg);
switch (w->present)
{
case NwGnbRrcToMr::RRC_PDU_DELIVERY: {
OctetString stream{};
stream.appendOctet(static_cast<int>(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<NwTimerExpired *>(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<udp::NwUdpServerReceive *>(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<rrc::RrcChannel>(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
......@@ -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<ASN_NGAP_ErrorIndication>({ieCause});
......
......@@ -13,6 +13,9 @@
#include <gnb/gtp/task.hpp>
#include <gnb/rrc/task.hpp>
#include <asn/ngap/ASN_NGAP_FiveG-S-TMSI.h>
#include <asn/ngap/ASN_NGAP_Paging.h>
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
......@@ -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
......@@ -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;
......
......@@ -8,61 +8,93 @@
#pragma once
#include "types.hpp"
#include <app/cli_base.hpp>
#include <app/cli_cmd.hpp>
#include <asn/utils/utils.hpp>
#include <rrc/rrc.hpp>
#include <sctp/sctp.hpp>
#include <urs/rls/rls.hpp>
#include <utility>
#include <utils/network.hpp>
#include <utils/nts.hpp>
#include <utils/octet_string.hpp>
#include <utils/unique_buffer.hpp>
#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<ASN_NGAP_FiveG_S_TMSI> uePagingTmsi{};
asn::Unique<ASN_NGAP_TAIListForPaging> 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
......
//
// 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 <cmath>
#include <gnb/gtp/task.hpp>
#include <gnb/rrc/task.hpp>
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<int>(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<rrc::RrcChannel>(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
//
// 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 <gnb/rrc/task.hpp>
#include <set>
#include <utils/common.hpp>
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<RlsUeContext>(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<int>(m_stiToUeId.size()));
return ueId;
}
}
void GnbRlsTask::onPeriodicLostControl()
{
int64_t current = utils::CurrentTimeMillis();
std::set<int> lostUeId{};
std::set<uint64_t> 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
//
// 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 <gnb/gtp/task.hpp>
#include <gnb/nts.hpp>
#include <gnb/rrc/task.hpp>
#include <utils/common.hpp>
#include <utils/constants.hpp>
#include <utils/libc_error.hpp>
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<NwGnbRrcToRls *>(msg);
switch (w->present)
{
case NwGnbRrcToRls::RRC_PDU_DELIVERY: {
handleDownlinkDelivery(w->ueId, rls::EPduType::RRC, std::move(w->pdu),
OctetString::FromOctet4(static_cast<int>(w->channel)));
break;
}
case NwGnbRrcToRls::RADIO_POWER_ON: {
m_powerOn = true;
break;
}
}
break;
}
case NtsMessageType::GNB_GTP_TO_RLS: {
auto *w = dynamic_cast<NwGnbGtpToRls *>(msg);
switch (w->present)
{
case NwGnbGtpToRls::DATA_PDU_DELIVERY: {
handleDownlinkDelivery(w->ueId, rls::EPduType::DATA, std::move(w->pdu),
OctetString::FromOctet4(static_cast<int>(w->psi)));
break;
}
}
break;
}
case NtsMessageType::UDP_SERVER_RECEIVE: {
auto *w = dynamic_cast<udp::NwUdpServerReceive *>(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<NwTimerExpired *>(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
......@@ -8,14 +8,13 @@
#pragma once
#include "rls.hpp"
#include <gnb/nts.hpp>
#include <gnb/types.hpp>
#include <memory>
#include <thread>
#include <udp/server_task.hpp>
#include <unordered_map>
#include <urs/rls/gnb_entity.hpp>
#include <urs/rls_pdu.hpp>
#include <utils/logger.hpp>
#include <utils/nts.hpp>
#include <vector>
......@@ -23,31 +22,42 @@
namespace nr::gnb
{
class GnbMrTask : public NtsTask
class GnbRlsTask : public NtsTask
{
private:
TaskBase *m_base;
std::unique_ptr<Logger> m_logger;
udp::UdpServerTask *m_udpTask;
GnbRls *m_rlsEntity;
std::unordered_map<int, MrUeContext> m_ueMap;
bool m_powerOn;
uint64_t m_sti;
std::unordered_map<int, std::unique_ptr<RlsUeContext>> m_ueCtx;
std::unordered_map<uint64_t, int> m_stiToUeId;
int m_ueIdCounter;
friend class GnbCmdHandler;
public:
explicit GnbMrTask(TaskBase *base);
~GnbMrTask() override = default;
explicit GnbRlsTask(TaskBase *base);
~GnbRlsTask() 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);
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
//
// 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<int>(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
......@@ -9,8 +9,8 @@
#include "task.hpp"
#include <asn/rrc/ASN_RRC_UL-CCCH-Message.h>
#include <asn/rrc/ASN_RRC_UL-DCCH-Message.h>
#include <gnb/rls/task.hpp>
#include <rrc/encode.hpp>
#include <gnb/mr/task.hpp>
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_RRC_BCCH_DL_SCH_Message>(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_RRC_DL_CCCH_Message>(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_RRC_DL_DCCH_Message>(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_RRC_PCCH_Message>(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_RRC_UL_CCCH_Message>(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)
......
......@@ -8,10 +8,10 @@
#include "task.hpp"
#include <gnb/mr/task.hpp>
#include <gnb/ngap/task.hpp>
#include <rrc/encode.hpp>
#include <asn/ngap/ASN_NGAP_FiveG-S-TMSI.h>
#include <asn/rrc/ASN_RRC_BCCH-BCH-Message.h>
#include <asn/rrc/ASN_RRC_BCCH-DL-SCH-Message.h>
#include <asn/rrc/ASN_RRC_CellGroupConfig.h>
......@@ -20,6 +20,9 @@
#include <asn/rrc/ASN_RRC_DLInformationTransfer-IEs.h>
#include <asn/rrc/ASN_RRC_DLInformationTransfer.h>
#include <asn/rrc/ASN_RRC_PCCH-Message.h>
#include <asn/rrc/ASN_RRC_Paging.h>
#include <asn/rrc/ASN_RRC_PagingRecord.h>
#include <asn/rrc/ASN_RRC_PagingRecordList.h>
#include <asn/rrc/ASN_RRC_RRCRelease-IEs.h>
#include <asn/rrc/ASN_RRC_RRCRelease.h>
#include <asn/rrc/ASN_RRC_RRCSetup-IEs.h>
......@@ -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<ASN_RRC_DL_DCCH_Message>();
......@@ -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<ASN_NGAP_FiveG_S_TMSI> &tmsi,
const asn::Unique<ASN_NGAP_TAIListForPaging> &taiList)
{
// Construct and send a Paging message
auto *pdu = asn::New<ASN_RRC_PCCH_Message>();
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<ASN_RRC_Paging>();
auto *record = asn::New<ASN_RRC_PagingRecord>();
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
......@@ -9,8 +9,8 @@
#include "task.hpp"
#include <asn/rrc/ASN_RRC_DLInformationTransfer-IEs.h>
#include <asn/rrc/ASN_RRC_DLInformationTransfer.h>
#include <gnb/mr/task.hpp>
#include <gnb/nts.hpp>
#include <gnb/rls/task.hpp>
#include <rrc/encode.hpp>
namespace nr::gnb
......@@ -38,15 +38,15 @@ void GnbRrcTask::onLoop()
switch (msg->msgType)
{
case NtsMessageType::GNB_MR_TO_RRC: {
auto *w = dynamic_cast<NwGnbMrToRrc *>(msg);
case NtsMessageType::GNB_RLS_TO_RRC: {
auto *w = dynamic_cast<NwGnbRlsToRrc *>(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<NwGnbNgapToRrc *>(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;
}
......
......@@ -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<ASN_NGAP_FiveG_S_TMSI> &tmsi,
const asn::Unique<ASN_NGAP_TAIListForPaging> &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);
......
......@@ -15,6 +15,7 @@
#include <string>
#include <utils/common_types.hpp>
#include <utils/logger.hpp>
#include <utils/network.hpp>
#include <utils/nts.hpp>
#include <utils/octet_string.hpp>
......@@ -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<PlmnSupport *> 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);
......
......@@ -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,
......
......@@ -8,6 +8,7 @@
#include "utils.hpp"
#include <algorithm>
#include <cstring>
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
......@@ -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);
......
......@@ -9,9 +9,9 @@
#include "cmd_handler.hpp"
#include <ue/app/task.hpp>
#include <ue/mr/task.hpp>
#include <ue/nas/task.hpp>
#include <ue/rrc/task.hpp>
#include <ue/rls/task.hpp>
#include <ue/tun/task.hpp>
#include <utils/common.hpp>
#include <utils/printer.hpp>
......@@ -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<Json> 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;
}
}
}
......
......@@ -9,7 +9,8 @@
#include "task.hpp"
#include "cmd_handler.hpp"
#include <nas/utils.hpp>
#include <ue/mr/task.hpp>
#include <ue/nas/task.hpp>
#include <ue/rls/task.hpp>
#include <ue/tun/tun.hpp>
#include <utils/common.hpp>
#include <utils/constants.hpp>
......@@ -50,17 +51,17 @@ void UeAppTask::onLoop()
switch (msg->msgType)
{
case NtsMessageType::UE_MR_TO_APP: {
auto *w = dynamic_cast<NwUeMrToApp *>(msg);
case NtsMessageType::UE_RLS_TO_APP: {
auto *w = dynamic_cast<NwUeRlsToApp *>(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
......@@ -29,6 +29,7 @@ class UeAppTask : public NtsTask
std::array<std::optional<UePduSessionInfo>, 16> m_pduSessions{};
std::array<TunTask *, 16> 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
//
// 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 <nas/utils.hpp>
#include <ue/app/task.hpp>
#include <ue/rrc/task.hpp>
#include <ue/sm/sm.hpp>
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
//
// 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
//
// 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 <ue/nts.hpp>
#include <urs/rls/ue_entity.hpp>
namespace nr::ue
{
UeRls::UeRls(const std::string &nodeName, const std::vector<InetAddress> &gnbSearchList, std::unique_ptr<Logger> 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
//
// 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 <nas/nas.hpp>
#include <ue/types.hpp>
#include <urs/rls/ue_entity.hpp>
namespace nr::ue
{
class UeRls : public rls::RlsUeEntity
{
private:
std::unique_ptr<Logger> m_logger;
NtsTask *m_targetTask;
public:
UeRls(const std::string &nodeName, const std::vector<InetAddress> &gnbSearchList, std::unique_ptr<Logger> 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
//
// 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 <ue/app/task.hpp>
#include <ue/nts.hpp>
#include <ue/rrc/task.hpp>
#include <utils/common.hpp>
#include <utils/constants.hpp>
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<InetAddress> 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<NwUeMrToMr *>(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<NwUeRrcToMr *>(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<int>(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<NwAppToMr *>(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<NwTimerExpired *>(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<udp::NwUdpServerReceive *>(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<rrc::RrcChannel>(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
......@@ -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<nas::SecuredMmMessage> 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<nas::SecuredMmMessage> 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<nas::SecuredMmMessage>();
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<nas::SecuredMmMessage> Encrypt(NasSecurityContext &ctx, const nas::PlainMmMessage &msg)
std::unique_ptr<nas::SecuredMmMessage> 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<nas::SecuredMmMessage> 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<nas::NasMessage> Decrypt(NasSecurityContext &ctx, const nas::SecuredMmMessage &msg)
......
......@@ -14,7 +14,7 @@
namespace nr::ue::nas_enc
{
std::unique_ptr<nas::SecuredMmMessage> Encrypt(NasSecurityContext &ctx, const nas::PlainMmMessage &msg);
std::unique_ptr<nas::SecuredMmMessage> Encrypt(NasSecurityContext &ctx, const nas::PlainMmMessage &msg, bool bypassCiphering);
std::unique_ptr<nas::NasMessage> Decrypt(NasSecurityContext &ctx, const nas::SecuredMmMessage &msg);
uint32_t ComputeMac(nas::ETypeOfIntegrityProtectionAlgorithm alg, NasCount count, bool is3gppAccess,
......
......@@ -9,7 +9,7 @@
#include "mm.hpp"
#include <nas/utils.hpp>
#include <ue/sm/sm.hpp>
#include <ue/nas/sm/sm.hpp>
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
......@@ -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(),
......
......@@ -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);
}
}
}
......
......@@ -9,7 +9,7 @@
#include "mm.hpp"
#include <nas/utils.hpp>
#include <ue/app/task.hpp>
#include <ue/sm/sm.hpp>
#include <ue/nas/sm/sm.hpp>
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."
......
......@@ -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 {};
......
......@@ -10,6 +10,8 @@
#include <utils/common.hpp>
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
......@@ -40,8 +40,12 @@ class NasMm
std::unique_ptr<nas::RegistrationRequest> m_lastRegistrationRequest{};
// Most recent de-registration request
std::unique_ptr<nas::DeRegistrationRequestUeOriginating> m_lastDeregistrationRequest{};
// Most recent service request
std::unique_ptr<nas::ServiceRequest> 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<UeCellMeasurement> &measures);
void handleRrcConnectionSetup();
void handleRrcConnectionRelease();
void handleServingCellChange(const UeCellInfo &servingCell);
void handleRadioLinkFailure();
void handlePaging(const std::vector<GutiMobileIdentity> &tmsiIds);
private: /* Access Control */
bool isHighPriority();
bool hasEmergency();
void setN1Capability(bool enabled);
bool isInNonAllowedArea();
private: /* eCall */
bool startECallInactivityIfNeeded();
......@@ -167,6 +174,7 @@ class NasMm
void deliverUlTransport(const nas::UlNasTransport &msg); // used by SM
bool isRegistered(); // used by SM
bool isRegisteredForEmergency(); // used by SM
void serviceNeededForUplinkData(); // used by SM
};
} // namespace nr::ue
\ No newline at end of file
//
// This file is a part of UERANSIM open source project.
// Copyright (c) 2021 ALİ GÜNGÖR.
//
// The software and all associated files are licensed under GPL-3.0
// and subject to the terms and conditions defined in LICENSE file.
//
#include "mm.hpp"
#include <algorithm>
#include <nas/utils.hpp>
#include <ue/app/task.hpp>
#include <ue/nas/sm/sm.hpp>
#include <ue/rrc/task.hpp>
namespace nr::ue
{
void NasMm::handlePlmnSearchResponse(const std::vector<UeCellMeasurement> &measures)
{
// TODO
// if (m_base->nodeListener)
// m_base->nodeListener->onConnected(app::NodeType::UE, m_base->config->getNodeName(), app::NodeType::GNB,
// gnbName);
if (m_mmSubState != EMmSubState::MM_REGISTERED_PLMN_SEARCH &&
m_mmSubState != EMmSubState::MM_REGISTERED_NO_CELL_AVAILABLE &&
m_mmSubState != EMmSubState::MM_DEREGISTERED_PLMN_SEARCH &&
m_mmSubState != EMmSubState::MM_DEREGISTERED_NO_CELL_AVAILABLE)
{
m_logger->warn("PLMN search response received without being requested");
return;
}
if (measures.empty())
{
if (m_mmSubState == EMmSubState::MM_REGISTERED_PLMN_SEARCH)
{
m_logger->err("PLMN selection failure, no cell available");
switchMmState(EMmState::MM_REGISTERED, EMmSubState::MM_REGISTERED_NO_CELL_AVAILABLE);
return;
}
else if (m_mmSubState == EMmSubState::MM_DEREGISTERED_PLMN_SEARCH)
{
m_logger->err("PLMN selection failure, no cell available");
switchMmState(EMmState::MM_DEREGISTERED, EMmSubState::MM_DEREGISTERED_NO_CELL_AVAILABLE);
return;
}
return; // otherwise it's one of the no-cell-available states, no need to print an error
}
int listedAsForbiddenPlmn = 0;
int listedAsForbiddenTai = 0;
int listedAsForbiddenServiceAreaPlmn = 0;
int listedAsForbiddenServiceAreaTai = 0;
int unlistedPlmn = 0;
std::vector<UeCellMeasurement> suitable{};
std::vector<UeCellMeasurement> acceptable{};
for (auto &item : measures)
{
acceptable.push_back(item);
if (nas::utils::PlmnListContains(m_usim->m_forbiddenPlmnList, item.cellId.plmn))
{
listedAsForbiddenPlmn++;
continue;
}
if (nas::utils::TaiListContains(
m_usim->m_forbiddenTaiListRps,
nas::VTrackingAreaIdentity{nas::utils::PlmnFrom(item.cellId.plmn), octet3{item.tac}}) ||
nas::utils::TaiListContains(
m_usim->m_forbiddenTaiListRoaming,
nas::VTrackingAreaIdentity{nas::utils::PlmnFrom(item.cellId.plmn), octet3{item.tac}}))
{
listedAsForbiddenTai++;
continue;
}
if (nas::utils::ServiceAreaListForbidsPlmn(m_usim->m_serviceAreaList, nas::utils::PlmnFrom(item.cellId.plmn)))
{
listedAsForbiddenServiceAreaPlmn++;
continue;
}
if (nas::utils::ServiceAreaListForbidsTai(
m_usim->m_serviceAreaList,
nas::VTrackingAreaIdentity{nas::utils::PlmnFrom(item.cellId.plmn), octet3{item.tac}}))
{
listedAsForbiddenServiceAreaTai++;
continue;
}
if (item.cellId.plmn == m_base->config->hplmn || item.cellId.plmn == m_usim->m_currentPlmn ||
nas::utils::PlmnListContains(m_usim->m_equivalentPlmnList, item.cellId.plmn))
{
suitable.push_back(item);
}
else
{
unlistedPlmn++;
continue;
}
}
int totalForbidden = listedAsForbiddenPlmn + listedAsForbiddenTai + listedAsForbiddenServiceAreaPlmn +
listedAsForbiddenServiceAreaTai;
auto logErrorSuitableAcceptable = [this, totalForbidden, unlistedPlmn]() {
m_logger->err("PLMN selection failure, no suitable or acceptable cell can be found");
if (totalForbidden > 0 || unlistedPlmn > 0)
m_logger->err("[%d] cell was in forbidden list, [%d] was in unknown PLMN", totalForbidden, unlistedPlmn);
};
auto logWarningAcceptable = [this, totalForbidden, unlistedPlmn]() {
m_logger->warn("PLMN selection failure, no suitable cell can be found, an acceptable cell is selected instead");
if (totalForbidden > 0 || unlistedPlmn > 0)
m_logger->warn("[%d] cell was in forbidden list, [%d] was in unknown PLMN", totalForbidden, unlistedPlmn);
};
if (!suitable.empty())
{
std::stable_sort(suitable.begin(), suitable.end(), [](auto &x, auto &y) { return x.dbm >= y.dbm; });
auto *w = new NwUeNasToRrc(NwUeNasToRrc::CELL_SELECTION_COMMAND);
w->cellId = suitable[0].cellId;
w->isSuitableCell = true;
m_base->rrcTask->push(w);
}
else if (!acceptable.empty())
{
std::stable_sort(acceptable.begin(), acceptable.end(), [](auto &x, auto &y) { return x.dbm >= y.dbm; });
logWarningAcceptable();
auto *w = new NwUeNasToRrc(NwUeNasToRrc::CELL_SELECTION_COMMAND);
w->cellId = acceptable[0].cellId;
w->isSuitableCell = false;
m_base->rrcTask->push(w);
}
else
{
if (m_mmSubState == EMmSubState::MM_REGISTERED_PLMN_SEARCH)
{
logErrorSuitableAcceptable();
switchMmState(EMmState::MM_REGISTERED, EMmSubState::MM_REGISTERED_NO_CELL_AVAILABLE);
return;
}
else if (m_mmSubState == EMmSubState::MM_DEREGISTERED_PLMN_SEARCH)
{
logErrorSuitableAcceptable();
switchMmState(EMmState::MM_DEREGISTERED, EMmSubState::MM_DEREGISTERED_NO_CELL_AVAILABLE);
return;
}
}
}
void NasMm::handleServingCellChange(const UeCellInfo &servingCell)
{
if (m_cmState == ECmState::CM_CONNECTED)
{
m_logger->err("Serving cell change in CM-CONNECTED");
return;
}
if (servingCell.cellCategory != ECellCategory::ACCEPTABLE_CELL &&
servingCell.cellCategory != ECellCategory::SUITABLE_CELL)
{
m_logger->err("Serving cell change with unhandled cell category");
return;
}
m_logger->info("Serving cell determined [%s]", servingCell.gnbName.c_str());
if (m_mmState == EMmState::MM_REGISTERED || m_mmState == EMmState::MM_DEREGISTERED)
{
bool isSuitable = servingCell.cellCategory == ECellCategory::SUITABLE_CELL;
if (m_mmState == EMmState::MM_REGISTERED)
switchMmState(EMmState::MM_REGISTERED, isSuitable ? EMmSubState::MM_REGISTERED_NORMAL_SERVICE
: EMmSubState::MM_REGISTERED_LIMITED_SERVICE);
else
switchMmState(EMmState::MM_DEREGISTERED, isSuitable ? EMmSubState::MM_DEREGISTERED_NORMAL_SERVICE
: EMmSubState::MM_DEREGISTERED_LIMITED_SERVICE);
}
// todo: else, other states abnormal case
resetRegAttemptCounter();
m_usim->m_servingCell = servingCell;
m_usim->m_currentPlmn = servingCell.cellId.plmn;
m_usim->m_currentTai =
nas::VTrackingAreaIdentity{nas::utils::PlmnFrom(servingCell.cellId.plmn), octet3{servingCell.tac}};
}
void NasMm::handleRrcConnectionSetup()
{
switchCmState(ECmState::CM_CONNECTED);
}
void NasMm::handleRrcConnectionRelease()
{
switchCmState(ECmState::CM_IDLE);
}
void NasMm::handleRadioLinkFailure()
{
if (m_cmState == ECmState::CM_CONNECTED)
{
m_logger->err("Radio link failure detected");
}
m_usim->m_servingCell = std::nullopt;
m_usim->m_currentPlmn = std::nullopt;
m_usim->m_currentTai = std::nullopt;
handleRrcConnectionRelease();
if (m_mmState == EMmState::MM_REGISTERED)
switchMmState(EMmState::MM_REGISTERED, EMmSubState::MM_REGISTERED_NA);
else if (m_mmState == EMmState::MM_DEREGISTERED)
switchMmState(EMmState::MM_DEREGISTERED, EMmSubState::MM_DEREGISTERED_NA);
}
void NasMm::localReleaseConnection()
{
m_logger->info("Performing local release of NAS connection");
m_base->rrcTask->push(new NwUeNasToRrc(NwUeNasToRrc::LOCAL_RELEASE_CONNECTION));
}
void NasMm::handlePaging(const std::vector<GutiMobileIdentity> &tmsiIds)
{
if (m_usim->m_storedGuti.type == nas::EIdentityType::NO_IDENTITY)
return;
bool tmsiMatches = false;
for (auto &tmsi : tmsiIds)
{
if (tmsi.amfSetId == m_usim->m_storedGuti.gutiOrTmsi.amfSetId &&
tmsi.amfPointer == m_usim->m_storedGuti.gutiOrTmsi.amfPointer &&
tmsi.tmsi == m_usim->m_storedGuti.gutiOrTmsi.tmsi)
{
tmsiMatches = true;
break;
}
}
if (!tmsiMatches)
return;
m_timers->t3346.stop();
if (m_mmState == EMmState::MM_REGISTERED_INITIATED || m_mmState == EMmState::MM_DEREGISTERED_INITIATED ||
m_mmState == EMmState::MM_SERVICE_REQUEST_INITIATED)
{
m_logger->debug("Ignoring received Paging, another procedure already initiated");
return;
}
m_logger->debug("Responding to received Paging");
if (m_cmState == ECmState::CM_CONNECTED)
sendMobilityRegistration(ERegUpdateCause::PAGING_OR_NOTIFICATION);
else
sendServiceRequest(EServiceReqCause::IDLE_PAGING);
}
} // namespace nr::ue
\ No newline at end of file
......@@ -16,7 +16,7 @@
namespace nr::ue
{
void NasMm::sendInitialRegistration(bool isEmergencyReg, bool dueToDereg)
void NasMm::sendInitialRegistration(EInitialRegCause regCause)
{
if (m_rmState != ERmState::RM_DEREGISTERED)
{
......@@ -24,6 +24,8 @@ void NasMm::sendInitialRegistration(bool isEmergencyReg, bool dueToDereg)
return;
}
bool isEmergencyReg = regCause == EInitialRegCause::EMERGENCY_SERVICES;
// 5.5.1.2.7 Abnormal cases in the UE
// a) Timer T3346 is running.
if (m_timers->t3346.isRunning() && !isEmergencyReg && !hasEmergency())
......@@ -33,7 +35,7 @@ void NasMm::sendInitialRegistration(bool isEmergencyReg, bool dueToDereg)
bool highPriority = isHighPriority();
// The UE shall not start the registration procedure for initial registration in the following case
if (!highPriority && !dueToDereg)
if (!highPriority && regCause != EInitialRegCause::DUE_TO_DEREGISTRATION)
{
m_logger->debug("Initial registration canceled, T3346 is running");
return;
......@@ -123,6 +125,13 @@ void NasMm::sendMobilityRegistration(ERegUpdateCause updateCause)
}
}
// 5.6.1.7 Abnormal cases in the UE
// d) Registration procedure for mobility and periodic registration update is triggered
if (m_mmState == EMmState::MM_SERVICE_REQUEST_INITIATED)
{
m_timers->t3517.stop();
}
m_logger->debug("Sending %s with update cause [%s]",
nas::utils::EnumToString(updateCause == ERegUpdateCause::T3512_EXPIRY
? nas::ERegistrationType::PERIODIC_REGISTRATION_UPDATING
......@@ -230,13 +239,12 @@ void NasMm::receiveInitialRegistrationAccept(const nas::RegistrationAccept &msg)
});
}
// .. in addition, the UE shall add to the stored list the PLMN code of the registered PLMN that sent the list
nas::utils::AddToPlmnList(m_usim->m_equivalentPlmnList, nas::utils::PlmnFrom(m_usim->m_currentPlmn));
nas::utils::AddToPlmnList(m_usim->m_equivalentPlmnList, nas::utils::PlmnFrom(*m_usim->m_currentPlmn));
// Upon receipt of the REGISTRATION ACCEPT message, the UE shall reset the registration attempt counter, enter state
// 5GMM-REGISTERED and set the 5GS update status to 5U1 UPDATED.
resetRegAttemptCounter();
switchMmState(EMmState::MM_REGISTERED, EMmSubState::MM_REGISTERED_NORMAL_SERVICE);
switchRmState(ERmState::RM_REGISTERED);
switchUState(E5UState::U1_UPDATED);
// If the REGISTRATION ACCEPT message included a T3512 value IE, the UE shall use the value in the T3512 value IE as
......@@ -337,7 +345,7 @@ void NasMm::receiveMobilityRegistrationAccept(const nas::RegistrationAccept &msg
});
}
// .. in addition, the UE shall add to the stored list the PLMN code of the registered PLMN that sent the list
nas::utils::AddToPlmnList(m_usim->m_equivalentPlmnList, nas::utils::PlmnFrom(m_usim->m_currentPlmn));
nas::utils::AddToPlmnList(m_usim->m_equivalentPlmnList, nas::utils::PlmnFrom(*m_usim->m_currentPlmn));
// Store the service area list
m_usim->m_serviceAreaList = msg.serviceAreaList.value_or(nas::IEServiceAreaList{});
......@@ -347,7 +355,6 @@ void NasMm::receiveMobilityRegistrationAccept(const nas::RegistrationAccept &msg
resetRegAttemptCounter();
m_serCounter = 0;
switchMmState(EMmState::MM_REGISTERED, EMmSubState::MM_REGISTERED_NORMAL_SERVICE);
switchRmState(ERmState::RM_REGISTERED);
switchUState(E5UState::U1_UPDATED);
// "If the ACCEPT message included a T3512 value IE, the UE shall use the value in T3512 value IE as
......@@ -412,6 +419,10 @@ void NasMm::receiveMobilityRegistrationAccept(const nas::RegistrationAccept &msg
if (msg.networkFeatureSupport.has_value())
m_nwFeatureSupport = *msg.networkFeatureSupport;
// The service request attempt counter shall be reset when registration procedure for mobility and periodic
// registration update is successfully completed
m_serCounter = 0;
if (sendComplete)
sendNasMessage(nas::RegistrationComplete{});
......@@ -454,8 +465,6 @@ void NasMm::receiveInitialRegistrationReject(const nas::RegistrationReject &msg)
nas::utils::EnumToString(msg.eapMessage->eap->code));
}
switchRmState(ERmState::RM_DEREGISTERED);
auto handleAbnormalCase = [this, regType, cause]() {
m_logger->debug("Handling Registration Reject abnormal case");
......@@ -542,12 +551,12 @@ void NasMm::receiveInitialRegistrationReject(const nas::RegistrationReject &msg)
if (cause == nas::EMmCause::ROAMING_NOT_ALLOWED_IN_TA || cause == nas::EMmCause::NO_SUITIBLE_CELLS_IN_TA)
{
// TODO add to forbidden tai
nas::utils::AddToTaiList(m_usim->m_forbiddenTaiListRoaming, *m_usim->m_currentTai);
}
if (cause == nas::EMmCause::PLMN_NOT_ALLOWED || cause == nas::EMmCause::SERVING_NETWORK_NOT_AUTHORIZED)
{
nas::utils::AddToPlmnList(m_usim->m_forbiddenPlmnList, nas::utils::PlmnFrom(m_usim->m_currentPlmn));
nas::utils::AddToPlmnList(m_usim->m_forbiddenPlmnList, nas::utils::PlmnFrom(*m_usim->m_currentPlmn));
}
if (cause == nas::EMmCause::CONGESTION)
......@@ -605,8 +614,6 @@ void NasMm::receiveMobilityRegistrationReject(const nas::RegistrationReject &msg
nas::utils::EnumToString(msg.eapMessage->eap->code));
}
switchRmState(ERmState::RM_DEREGISTERED);
auto handleAbnormalCase = [this, regType, cause]() {
m_logger->debug("Handling Registration Reject abnormal case");
......@@ -702,12 +709,12 @@ void NasMm::receiveMobilityRegistrationReject(const nas::RegistrationReject &msg
if (cause == nas::EMmCause::ROAMING_NOT_ALLOWED_IN_TA || cause == nas::EMmCause::NO_SUITIBLE_CELLS_IN_TA)
{
// TODO add to forbidden tai
nas::utils::AddToTaiList(m_usim->m_forbiddenTaiListRoaming, *m_usim->m_currentTai);
}
if (cause == nas::EMmCause::PLMN_NOT_ALLOWED || cause == nas::EMmCause::SERVING_NETWORK_NOT_AUTHORIZED)
{
nas::utils::AddToPlmnList(m_usim->m_forbiddenPlmnList, nas::utils::PlmnFrom(m_usim->m_currentPlmn));
nas::utils::AddToPlmnList(m_usim->m_forbiddenPlmnList, nas::utils::PlmnFrom(*m_usim->m_currentPlmn));
}
if (cause == nas::EMmCause::CONGESTION)
......@@ -807,7 +814,9 @@ void NasMm::handleAbnormalMobilityRegFailure(nas::ERegistrationType regType)
// "If the registration attempt counter is less than 5:"
if (m_regCounter < 5)
{
bool includedInTaiList = false; // TODO
bool includedInTaiList = nas::utils::TaiListContains(
m_usim->m_taiList, nas::VTrackingAreaIdentity{nas::utils::PlmnFrom(m_usim->m_servingCell->cellId.plmn),
octet3{m_usim->m_servingCell->tac}});
// "If the TAI of the current serving cell is not included in the TAI list or the 5GS update status is different
// to 5U1 UPDATED"
......
......@@ -109,8 +109,8 @@ void NasMm::receiveSecurityModeCommand(const nas::SecurityModeCommand &msg)
nsCtx->ciphering = msg.selectedNasSecurityAlgorithms.ciphering;
keys::DeriveNasKeys(*nsCtx);
m_logger->debug("Derived NAS keys integrity[%s] ciphering[%s]", nsCtx->keys.kNasInt.toHexString().c_str(),
nsCtx->keys.kNasEnc.toHexString().c_str());
// m_logger->debug("Derived NAS keys integrity[%s] ciphering[%s]", nsCtx->keys.kNasInt.toHexString().c_str(),
// nsCtx->keys.kNasEnc.toHexString().c_str());
m_logger->debug("Selected integrity[%d] ciphering[%d]", (int)nsCtx->integrity, (int)nsCtx->ciphering);
// The UE shall in addition reset the uplink NAS COUNT counter if a) the SECURITY MODE COMMAND message is received
......
This diff is collapsed.
......@@ -9,7 +9,7 @@
#include "mm.hpp"
#include <nas/utils.hpp>
#include <ue/app/task.hpp>
#include <ue/sm/sm.hpp>
#include <ue/nas/sm/sm.hpp>
namespace nr::ue
{
......
......@@ -29,7 +29,7 @@ void NasMm::onTimerExpire(nas::NasTimer &timer)
if (m_mmSubState == EMmSubState::MM_DEREGISTERED_NORMAL_SERVICE)
{
logExpired();
sendInitialRegistration(false, false);
sendInitialRegistration(EInitialRegCause::T3346_EXPIRY);
}
break;
}
......@@ -54,7 +54,6 @@ void NasMm::onTimerExpire(nas::NasTimer &timer)
// The UE shall abort the registration procedure for initial registration and the NAS signalling
// connection, if any, shall be released locally if the initial registration request is not for
// emergency services..
switchRmState(ERmState::RM_DEREGISTERED);
switchMmState(EMmState::MM_DEREGISTERED, EMmSubState::MM_DEREGISTERED_NA);
switchUState(E5UState::U2_NOT_UPDATED);
......@@ -87,6 +86,26 @@ void NasMm::onTimerExpire(nas::NasTimer &timer)
}
break;
}
case 3517: {
if (m_mmState == EMmState::MM_SERVICE_REQUEST_INITIATED)
{
logExpired();
switchMmState(EMmState::MM_REGISTERED, EMmSubState::MM_REGISTERED_NA);
if (m_cmState == ECmState::CM_IDLE && m_lastServiceReqCause != EServiceReqCause::EMERGENCY_FALLBACK)
{
if (!hasEmergency() && !isHighPriority() && m_lastServiceReqCause != EServiceReqCause::IDLE_PAGING &&
m_lastServiceReqCause != EServiceReqCause::IDLE_3GPP_NOTIFICATION_N3GPP &&
m_lastServiceReqCause != EServiceReqCause::CONNECTED_3GPP_NOTIFICATION_N3GPP)
m_serCounter++;
if (m_serCounter >= 5)
m_timers->t3525.start();
}
}
break;
}
case 3519: {
m_usim->m_storedSuci = {};
break;
......@@ -112,7 +131,7 @@ void NasMm::onTimerExpire(nas::NasTimer &timer)
if (m_mmState == EMmState::MM_DEREGISTERED_INITIATED && m_lastDeregistrationRequest != nullptr)
{
logExpired();
m_logger->debug("Retrying de-registration request");
m_logger->debug("Retransmitting De-registration Request due to T3521 expiry");
sendNasMessage(*m_lastDeregistrationRequest);
m_timers->t3521.start(false);
......
......@@ -10,22 +10,50 @@
#include <asn/rrc/ASN_RRC_EstablishmentCause.h>
#include <nas/utils.hpp>
#include <ue/nas/enc.hpp>
#include <ue/nas/sm/sm.hpp>
#include <ue/rrc/task.hpp>
#include <ue/sm/sm.hpp>
namespace nr::ue
{
static bool IsInitialNasMessage(const nas::PlainMmMessage &msg)
{
auto msgType = msg.messageType;
return msgType == nas::EMessageType::REGISTRATION_REQUEST ||
msgType == nas::EMessageType::DEREGISTRATION_REQUEST_UE_ORIGINATING ||
msgType == nas::EMessageType::SERVICE_REQUEST;
}
static bool IsAcceptedWithoutIntegrity(const nas::PlainMmMessage &msg)
{
auto msgType = msg.messageType;
return msgType == nas::EMessageType::IDENTITY_REQUEST || msgType == nas::EMessageType::AUTHENTICATION_REQUEST ||
msgType == nas::EMessageType::AUTHENTICATION_RESULT || msgType == nas::EMessageType::AUTHENTICATION_REJECT ||
msgType == nas::EMessageType::REGISTRATION_REJECT ||
msgType == nas::EMessageType::DEREGISTRATION_ACCEPT_UE_TERMINATED ||
msgType == nas::EMessageType::SERVICE_REJECT;
}
static bool BypassCiphering(const nas::PlainMmMessage &msg)
{
return IsInitialNasMessage(msg);
}
void NasMm::sendNasMessage(const nas::PlainMmMessage &msg)
{
// TODO trigger on send
if (m_cmState == ECmState::CM_IDLE && !IsInitialNasMessage(msg))
{
m_logger->warn("NAS Transport aborted, Service Request is needed for uplink signalling");
if (m_mmState != EMmState::MM_SERVICE_REQUEST_INITIATED)
sendServiceRequest(EServiceReqCause::IDLE_UPLINK_SIGNAL_PENDING);
return;
}
OctetString pdu{};
if (m_usim->m_currentNsCtx &&
(m_usim->m_currentNsCtx->integrity != nas::ETypeOfIntegrityProtectionAlgorithm::IA0 ||
m_usim->m_currentNsCtx->ciphering != nas::ETypeOfCipheringAlgorithm::EA0))
if (m_usim->m_currentNsCtx && (m_usim->m_currentNsCtx->integrity != nas::ETypeOfIntegrityProtectionAlgorithm::IA0 ||
m_usim->m_currentNsCtx->ciphering != nas::ETypeOfCipheringAlgorithm::EA0))
{
auto secured = nas_enc::Encrypt(*m_usim->m_currentNsCtx, msg);
auto secured = nas_enc::Encrypt(*m_usim->m_currentNsCtx, msg, BypassCiphering(msg));
nas::EncodeNasMessage(*secured, pdu);
}
else
......@@ -61,9 +89,9 @@ void NasMm::receiveNasMessage(const nas::NasMessage &msg)
if (mmMsg.sht == nas::ESecurityHeaderType::NOT_PROTECTED)
{
// If any NAS signalling message is received as not integrity protected even though the secure exchange of NAS
// messages has been established by the network, then the NAS shall discard this message
if (m_usim->m_currentNsCtx)
// If any NAS signalling message is received as not integrity protected even though the secure exchange of NAS
// messages has been established by the network, then the NAS shall discard this message
if (m_usim->m_currentNsCtx && !IsAcceptedWithoutIntegrity((const nas::PlainMmMessage &)mmMsg))
{
m_logger->err(
"Not integrity protected NAS message received after security establishment. Ignoring received "
......@@ -103,7 +131,7 @@ void NasMm::receiveNasMessage(const nas::NasMessage &msg)
if (!m_usim->m_currentNsCtx)
{
m_logger->warn("Secured NAS message received while no security context");
m_logger->err("Secured NAS message received while no security context");
sendMmStatus(nas::EMmCause::MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE);
return;
}
......@@ -191,7 +219,7 @@ void NasMm::receiveMmMessage(const nas::PlainMmMessage &msg)
receiveDlNasTransport((const nas::DlNasTransport &)msg);
break;
default:
m_logger->err("Unhandled NAS MM message received: %d", (int)msg.messageType);
m_logger->err("Unhandled NAS MM message received [%d]", (int)msg.messageType);
break;
}
}
......@@ -200,7 +228,7 @@ void NasMm::receiveDlNasTransport(const nas::DlNasTransport &msg)
{
if (msg.payloadContainerType.payloadContainerType != nas::EPayloadContainerType::N1_SM_INFORMATION)
{
m_logger->err("Unhandled DL NAS Transport type: %d", (int)msg.payloadContainerType.payloadContainerType);
m_logger->err("Unhandled DL NAS Transport type [%d]", (int)msg.payloadContainerType.payloadContainerType);
return;
}
......@@ -217,7 +245,7 @@ void NasMm::receiveDlNasTransport(const nas::DlNasTransport &msg)
void NasMm::sendMmStatus(nas::EMmCause cause)
{
m_logger->warn("Sending MM Status with cause %s", nas::utils::EnumToString(cause));
m_logger->warn("Sending MM Status with cause [%s]", nas::utils::EnumToString(cause));
nas::FiveGMmStatus m;
m.mmCause.value = cause;
......@@ -226,12 +254,7 @@ void NasMm::sendMmStatus(nas::EMmCause cause)
void NasMm::receiveMmStatus(const nas::FiveGMmStatus &msg)
{
receiveMmCause(msg.mmCause);
}
void NasMm::receiveMmCause(const nas::IE5gMmCause &msg)
{
m_logger->err("MM cause received: %s", nas::utils::EnumToString(msg.value));
m_logger->err("MM status received with cause [%s]", nas::utils::EnumToString(msg.mmCause.value));
}
} // namespace nr::ue
......@@ -11,7 +11,7 @@
#include <nas/proto_conf.hpp>
#include <nas/utils.hpp>
#include <ue/app/task.hpp>
#include <ue/mm/mm.hpp>
#include <ue/nas/mm/mm.hpp>
namespace nr::ue
{
......@@ -89,6 +89,7 @@ void NasSm::sendEstablishmentRequest(const SessionConfig &config)
ps->sessionAmbr = {};
ps->authorizedQoSFlowDescriptions = {};
ps->pduAddress = {};
ps->uplinkPending = false;
/* Make PCO */
nas::ProtocolConfigurationOptions opt{};
......
......@@ -11,7 +11,7 @@
#include <nas/proto_conf.hpp>
#include <nas/utils.hpp>
#include <ue/app/task.hpp>
#include <ue/mm/mm.hpp>
#include <ue/nas/mm/mm.hpp>
namespace nr::ue
{
......
......@@ -10,7 +10,7 @@
#include <nas/utils.hpp>
#include <set>
#include <ue/app/task.hpp>
#include <ue/mm/mm.hpp>
#include <ue/nas/mm/mm.hpp>
namespace nr::ue
{
......
......@@ -11,7 +11,7 @@
#include <nas/utils.hpp>
#include <optional>
#include <ue/app/task.hpp>
#include <ue/mm/mm.hpp>
#include <ue/nas/mm/mm.hpp>
namespace nr::ue
{
......
......@@ -11,6 +11,7 @@
#include <nas/proto_conf.hpp>
#include <nas/utils.hpp>
#include <ue/app/task.hpp>
#include <ue/nas/mm/mm.hpp>
namespace nr::ue
{
......@@ -58,4 +59,50 @@ bool NasSm::anyEmergencySession()
[](auto &ps) { return ps->psState != EPsState::INACTIVE && ps->isEmergency; });
}
void NasSm::handleUplinkStatusChange(int psi, bool isPending)
{
m_logger->debug("Uplink data status changed PSI[%d] pending[%s]", psi, isPending ? "true" : "false");
m_pduSessions[psi]->uplinkPending = isPending;
if (isPending)
m_mm->serviceNeededForUplinkData();
}
bool NasSm::anyUplinkDataPending()
{
auto status = getUplinkDataStatus();
for (int i = 1; i < 16; i++)
if (status[i])
return true;
return false;
}
bool NasSm::anyEmergencyUplinkDataPending()
{
auto status = getUplinkDataStatus();
for (int i = 1; i < 16; i++)
if (status[i] && m_pduSessions[i]->isEmergency)
return true;
return false;
}
std::bitset<16> NasSm::getUplinkDataStatus()
{
std::bitset<16> res{};
for (int i = 1; i < 16; i++)
if (m_pduSessions[i]->psState == EPsState::ACTIVE && m_pduSessions[i]->uplinkPending)
res[i] = true;
return res;
}
std::bitset<16> NasSm::getPduSessionStatus()
{
std::bitset<16> res{};
for (int i = 1; i < 16; i++)
if (m_pduSessions[i]->psState == EPsState::ACTIVE)
res[i] = true;
return res;
}
} // namespace nr::ue
\ No newline at end of file
......@@ -9,6 +9,7 @@
#pragma once
#include <array>
#include <bitset>
#include <nas/nas.hpp>
#include <nas/timer.hpp>
#include <ue/nts.hpp>
......@@ -49,6 +50,11 @@ class NasSm
void localReleaseSession(int psi);
void localReleaseAllSessions();
bool anyEmergencySession();
void handleUplinkStatusChange(int psi, bool isPending);
bool anyUplinkDataPending();
bool anyEmergencyUplinkDataPending();
std::bitset<16> getUplinkDataStatus();
std::bitset<16> getPduSessionStatus();
/* Session Release */
void sendReleaseRequest(int psi);
......
......@@ -11,7 +11,7 @@
#include <nas/proto_conf.hpp>
#include <nas/utils.hpp>
#include <ue/app/task.hpp>
#include <ue/mm/mm.hpp>
#include <ue/nas/mm/mm.hpp>
namespace nr::ue
{
......
......@@ -8,7 +8,7 @@
#include "sm.hpp"
#include <nas/utils.hpp>
#include <ue/mm/mm.hpp>
#include <ue/nas/mm/mm.hpp>
namespace nr::ue
{
......@@ -70,7 +70,7 @@ void NasSm::receiveSmMessage(const nas::SmMessage &msg)
void NasSm::receiveSmStatus(const nas::FiveGSmStatus &msg)
{
m_logger->err("SM Status received: %s", nas::utils::EnumToString(msg.smCause.value));
m_logger->err("SM Status received with cause [%s]", nas::utils::EnumToString(msg.smCause.value));
if (msg.smCause.value == nas::ESmCause::INVALID_PTI_VALUE)
{
......
......@@ -28,10 +28,7 @@ NasTask::NasTask(TaskBase *base) : base{base}, timers{}
void NasTask::onStart()
{
logger->debug("NAS layer started");
usim->initialize(base->config->supi.has_value(), base->config->initials);
usim->m_currentPlmn = base->config->hplmn; // TODO: normally assigned after plmn search
sm->onStart(mm);
mm->onStart(sm, usim);
......@@ -67,18 +64,34 @@ void NasTask::onLoop()
auto *w = dynamic_cast<NwUeNasToNas *>(msg);
switch (w->present)
{
case NwUeNasToNas::PERFORM_MM_CYCLE:
case NwUeNasToNas::PERFORM_MM_CYCLE: {
mm->handleNasEvent(*w);
break;
case NwUeNasToNas::NAS_TIMER_EXPIRE:
}
case NwUeNasToNas::NAS_TIMER_EXPIRE: {
if (w->timer->isMmTimer())
mm->handleNasEvent(*w);
else
sm->handleNasEvent(*w);
break;
case NwUeNasToNas::ESTABLISH_INITIAL_SESSIONS:
}
case NwUeNasToNas::ESTABLISH_INITIAL_SESSIONS: {
sm->establishInitialSessions();
break;
}
default:
break;
}
break;
}
case NtsMessageType::UE_APP_TO_NAS: {
auto *w = dynamic_cast<NwUeAppToNas *>(msg);
switch (w->present)
{
case NwUeAppToNas::UPLINK_STATUS_CHANGE: {
sm->handleUplinkStatusChange(w->psi, w->isPending);
break;
}
default:
break;
}
......
......@@ -12,9 +12,9 @@
#include <crypt/milenage.hpp>
#include <nas/nas.hpp>
#include <nas/timer.hpp>
#include <ue/mm/mm.hpp>
#include <ue/nas/mm/mm.hpp>
#include <ue/nas/sm/sm.hpp>
#include <ue/nts.hpp>
#include <ue/sm/sm.hpp>
#include <ue/types.hpp>
#include <utils/nts.hpp>
......
......@@ -24,18 +24,21 @@ class Usim
bool m_isValid{};
public:
// Location related
nas::IE5gsMobileIdentity m_storedGuti{};
std::optional<nas::IE5gsTrackingAreaIdentity> m_lastVisitedRegisteredTai{};
// State related
E5UState m_uState{};
// Identity related
nas::IE5gsMobileIdentity m_storedSuci{};
nas::IE5gsMobileIdentity m_storedGuti{};
// Plmn related
Plmn m_currentPlmn{};
std::optional<UeCellInfo> m_servingCell{};
std::optional<Plmn> m_currentPlmn{};
std::optional<nas::VTrackingAreaIdentity> m_currentTai{};
std::optional<nas::IE5gsTrackingAreaIdentity> m_lastVisitedRegisteredTai{};
nas::IE5gsTrackingAreaIdentityList m_taiList{};
nas::IE5gsTrackingAreaIdentityList m_forbiddenTaiList{};
nas::IE5gsTrackingAreaIdentityList m_forbiddenTaiListRoaming{}; // 5GS TAs for roaming
nas::IE5gsTrackingAreaIdentityList m_forbiddenTaiListRps{}; // 5GS forbidden TAs for regional provision of service
nas::IEPlmnList m_equivalentPlmnList{};
nas::IEPlmnList m_forbiddenPlmnList{};
nas::IEServiceAreaList m_serviceAreaList{};
......
......@@ -13,7 +13,6 @@
#include <app/cli_base.hpp>
#include <nas/timer.hpp>
#include <rrc/rrc.hpp>
#include <urs/rls/rls.hpp>
#include <utility>
#include <utils/network.hpp>
#include <utils/nts.hpp>
......@@ -22,97 +21,6 @@
namespace nr::ue
{
struct NwUeMrToMr : NtsMessage
{
enum PR
{
RLS_CONNECTED,
RLS_RELEASED,
RLS_SEARCH_FAILURE,
RLS_START_WAITING_TIMER,
RLS_SEND_OVER_UDP,
RLS_RECEIVE_OVER_UDP
} present;
// RLS_CONNECTED
std::string gnbName{};
// RLS_RELEASED
// RLS_SEARCH_FAILURE
rls::ECause cause{};
// RLS_START_WAITING_TIMER
int period{};
// RLS_SEND_OVER_UDP
InetAddress address{};
// RLS_RECEIVE_OVER_UDP
rls::EPayloadType type{};
// RLS_SEND_OVER_UDP
// RLS_RECEIVE_OVER_UDP
OctetString pdu{};
explicit NwUeMrToMr(PR present) : NtsMessage(NtsMessageType::UE_MR_TO_MR), present(present)
{
}
};
struct NwUeMrToRrc : NtsMessage
{
enum PR
{
PLMN_SEARCH_RESPONSE,
PLMN_SEARCH_FAILURE,
RRC_PDU_DELIVERY,
RADIO_LINK_FAILURE
} present;
// PLMN_SEARCH_RESPONSE
std::string gnbName{};
// RRC_PDU_DELIVERY
rrc::RrcChannel channel{};
OctetString pdu{};
explicit NwUeMrToRrc(PR present) : NtsMessage(NtsMessageType::UE_MR_TO_RRC), present(present)
{
}
};
struct NwUeMrToApp : NtsMessage
{
enum PR
{
DATA_PDU_DELIVERY
} present;
// DATA_PDU_DELIVERY
int psi{};
OctetString data{};
explicit NwUeMrToApp(PR present) : NtsMessage(NtsMessageType::UE_MR_TO_APP), present(present)
{
}
};
struct NwAppToMr : NtsMessage
{
enum PR
{
DATA_PDU_DELIVERY
} present;
// DATA_PDU_DELIVERY
int psi{};
OctetString data{};
explicit NwAppToMr(PR present) : NtsMessage(NtsMessageType::UE_APP_TO_MR), present(present)
{
}
};
struct NwAppToTun : NtsMessage
{
enum PR
......@@ -155,17 +63,24 @@ struct NwUeRrcToNas : NtsMessage
{
NAS_DELIVERY,
PLMN_SEARCH_RESPONSE,
PLMN_SEARCH_FAILURE,
RRC_CONNECTION_SETUP,
RRC_CONNECTION_RELEASE,
RADIO_LINK_FAILURE,
SERVING_CELL_CHANGE,
PAGING,
} present;
// NAS_DELIVERY
OctetString nasPdu{};
// PLMN_SEARCH_RESPONSE
std::string gnbName{};
std::vector<UeCellMeasurement> measurements{};
// SERVING_CELL_CHANGE
UeCellInfo servingCell{};
// PAGING
std::vector<GutiMobileIdentity> pagingTmsi{};
explicit NwUeRrcToNas(PR present) : NtsMessage(NtsMessageType::UE_RRC_TO_NAS), present(present)
{
......@@ -179,7 +94,8 @@ struct NwUeNasToRrc : NtsMessage
PLMN_SEARCH_REQUEST,
LOCAL_RELEASE_CONNECTION,
INITIAL_NAS_DELIVERY,
UPLINK_NAS_DELIVERY
UPLINK_NAS_DELIVERY,
CELL_SELECTION_COMMAND,
} present;
// INITIAL_NAS_DELIVERY
......@@ -189,28 +105,59 @@ struct NwUeNasToRrc : NtsMessage
// INITIAL_NAS_DELIVERY
long rrcEstablishmentCause{};
// CELL_SELECTION_COMMAND
GlobalNci cellId{};
bool isSuitableCell{}; // otherwise 'acceptable'
explicit NwUeNasToRrc(PR present) : NtsMessage(NtsMessageType::UE_NAS_TO_RRC), present(present)
{
}
};
struct NwUeRrcToMr : NtsMessage
struct NwUeRrcToRls : NtsMessage
{
enum PR
{
PLMN_SEARCH_REQUEST,
CELL_SELECTION_COMMAND,
RRC_PDU_DELIVERY,
RRC_CONNECTION_RELEASE,
RESET_STI,
} present;
// CELL_SELECTION_COMMAND
GlobalNci cellId{};
bool isSuitableCell{}; // otherwise 'acceptable'
// RRC_PDU_DELIVERY
rrc::RrcChannel channel{};
OctetString pdu{};
// RRC_CONNECTION_RELEASE
rls::ECause cause{};
explicit NwUeRrcToRls(PR present) : NtsMessage(NtsMessageType::UE_RRC_TO_RLS), present(present)
{
}
};
explicit NwUeRrcToMr(PR present) : NtsMessage(NtsMessageType::UE_RRC_TO_MR), present(present)
struct NwUeRlsToRrc : NtsMessage
{
enum PR
{
PLMN_SEARCH_RESPONSE,
SERVING_CELL_CHANGE,
RRC_PDU_DELIVERY,
RADIO_LINK_FAILURE
} present;
// PLMN_SEARCH_RESPONSE
std::vector<UeCellMeasurement> measurements{};
// SERVING_CELL_CHANGE
UeCellInfo servingCell{};
// RRC_PDU_DELIVERY
rrc::RrcChannel channel{};
OctetString pdu{};
explicit NwUeRlsToRrc(PR present) : NtsMessage(NtsMessageType::UE_RLS_TO_RRC), present(present)
{
}
};
......@@ -244,10 +191,59 @@ struct NwUeNasToApp : NtsMessage
}
};
struct NwUeAppToNas : NtsMessage
{
enum PR
{
UPLINK_STATUS_CHANGE,
} present;
// UPLINK_STATUS_CHANGE
int psi{};
bool isPending{};
explicit NwUeAppToNas(PR present) : NtsMessage(NtsMessageType::UE_APP_TO_NAS), present(present)
{
}
};
struct NwUeAppToRls : NtsMessage
{
enum PR
{
DATA_PDU_DELIVERY
} present;
// DATA_PDU_DELIVERY
int psi{};
OctetString pdu{};
explicit NwUeAppToRls(PR present) : NtsMessage(NtsMessageType::UE_APP_TO_RLS), present(present)
{
}
};
struct NwUeRlsToApp : NtsMessage
{
enum PR
{
DATA_PDU_DELIVERY
} present;
// DATA_PDU_DELIVERY
int psi{};
OctetString pdu{};
explicit NwUeRlsToApp(PR present) : NtsMessage(NtsMessageType::UE_RLS_TO_APP), present(present)
{
}
};
struct NwUeStatusUpdate : NtsMessage
{
static constexpr const int SESSION_ESTABLISHMENT = 1;
static constexpr const int SESSION_RELEASE = 2;
static constexpr const int CM_STATE = 3;
const int what{};
......@@ -257,6 +253,9 @@ struct NwUeStatusUpdate : NtsMessage
// SESSION_RELEASE
int psi{};
// CM_STATE
ECmState cmState{};
explicit NwUeStatusUpdate(const int what) : NtsMessage(NtsMessageType::UE_STATUS_UPDATE), what(what)
{
}
......
//
// 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 <ue/nts.hpp>
#include <ue/rrc/task.hpp>
#include <utils/common.hpp>
namespace nr::ue
{
void UeRlsTask::onMeasurement()
{
std::vector<GlobalNci> entered{};
std::vector<GlobalNci> exited{};
// compare active and pending measurements
for (auto &m : m_activeMeasurements)
{
if (!m_pendingMeasurements.count(m.first))
exited.push_back(m.first);
}
for (auto &m : m_pendingMeasurements)
{
if (!m_activeMeasurements.count(m.first))
entered.push_back(m.first);
}
if (!entered.empty() || !exited.empty())
onCoverageChange(entered, exited);
// copy from pending to active measurements
m_activeMeasurements = m_pendingMeasurements;
// clear pending measurements
m_pendingMeasurements = {};
// Issue another cell info request for each address in the search space
for (auto &ip : m_cellSearchSpace)
{
rls::RlsCellInfoRequest req{m_sti};
sendRlsMessage(ip, req);
}
// Send PLMN search response to the RRC if it is requested
if (m_pendingPlmnResponse)
{
m_pendingPlmnResponse = false;
std::vector<UeCellMeasurement> measurements{};
for (auto &m : m_activeMeasurements)
measurements.push_back(m.second);
auto *w = new NwUeRlsToRrc(NwUeRlsToRrc::PLMN_SEARCH_RESPONSE);
w->measurements = std::move(measurements);
m_base->rrcTask->push(w);
}
}
void UeRlsTask::receiveCellInfoResponse(const rls::RlsCellInfoResponse &msg)
{
UeCellMeasurement meas{};
meas.sti = msg.sti;
meas.cellId = msg.cellId;
meas.tac = msg.tac;
meas.dbm = msg.dbm;
meas.gnbName = msg.gnbName;
meas.linkIp = msg.linkIp;
m_pendingMeasurements[meas.cellId] = meas;
}
void UeRlsTask::onCoverageChange(const std::vector<GlobalNci> &entered, const std::vector<GlobalNci> &exited)
{
m_logger->debug("Coverage change detected. [%d] cell entered, [%d] cell exited", static_cast<int>(entered.size()),
static_cast<int>(exited.size()));
bool campedCellLost = m_servingCell.has_value() && std::any_of(exited.begin(), exited.end(), [this](auto &i) {
return i == m_servingCell->cellId;
});
if (campedCellLost)
{
m_logger->warn("Signal lost from camped cell");
m_servingCell = std::nullopt;
m_base->rrcTask->push(new NwUeRlsToRrc(NwUeRlsToRrc::RADIO_LINK_FAILURE));
}
}
void UeRlsTask::plmnSearchRequested()
{
m_pendingPlmnResponse = true;
}
void UeRlsTask::handleCellSelectionCommand(const GlobalNci &cellId, bool isSuitable)
{
slowDownMeasurements();
if (!m_activeMeasurements.count(cellId))
{
m_logger->err("Selected cell is no longer available for camping");
return;
}
auto &measurement = m_activeMeasurements[cellId];
m_servingCell = UeCellInfo{};
m_servingCell->sti = measurement.sti;
m_servingCell->cellId = measurement.cellId;
m_servingCell->tac = measurement.tac;
m_servingCell->gnbName = measurement.gnbName;
m_servingCell->linkIp = measurement.linkIp;
m_servingCell->cellCategory = isSuitable ? ECellCategory::SUITABLE_CELL : ECellCategory::ACCEPTABLE_CELL;
auto *w = new NwUeRlsToRrc(NwUeRlsToRrc::SERVING_CELL_CHANGE);
w->servingCell = *m_servingCell;
m_base->rrcTask->push(w);
}
} // namespace nr::ue
//
// 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 <ue/nts.hpp>
#include <utils/common.hpp>
#include <utils/constants.hpp>
static constexpr const int TIMER_ID_MEASUREMENT = 1;
static constexpr const int TIMER_PERIOD_MEASUREMENT_MIN = 1;
static constexpr const int TIMER_PERIOD_MEASUREMENT_MAX = 2000;
static constexpr const int TIMER_ID_RAPID_LAUNCH = 2;
static constexpr const int TIMER_PERIOD_RAPID_LAUNCH = 750;
namespace nr::ue
{
UeRlsTask::UeRlsTask(TaskBase *base)
: m_base{base}, m_udpTask{}, m_cellSearchSpace{}, m_pendingMeasurements{}, m_activeMeasurements{},
m_pendingPlmnResponse{}, m_measurementPeriod{TIMER_PERIOD_MEASUREMENT_MIN}, m_servingCell{}
{
m_logger = m_base->logBase->makeUniqueLogger(m_base->config->getLoggerPrefix() + "rls");
for (auto &addr : m_base->config->gnbSearchList)
m_cellSearchSpace.emplace_back(addr, cons::PortalPort);
m_sti = utils::Random64();
}
void UeRlsTask::onStart()
{
m_udpTask = new udp::UdpServerTask(this);
std::vector<InetAddress> gnbSearchList{};
for (auto &ip : m_base->config->gnbSearchList)
gnbSearchList.emplace_back(ip, cons::PortalPort);
m_udpTask->start();
setTimer(TIMER_ID_MEASUREMENT, m_measurementPeriod);
setTimer(TIMER_ID_RAPID_LAUNCH, TIMER_PERIOD_RAPID_LAUNCH);
onMeasurement();
}
void UeRlsTask::onLoop()
{
NtsMessage *msg = take();
if (!msg)
return;
switch (msg->msgType)
{
case NtsMessageType::UE_RRC_TO_RLS: {
auto *w = dynamic_cast<NwUeRrcToRls *>(msg);
switch (w->present)
{
case NwUeRrcToRls::PLMN_SEARCH_REQUEST:
plmnSearchRequested();
break;
case NwUeRrcToRls::CELL_SELECTION_COMMAND:
handleCellSelectionCommand(w->cellId, w->isSuitableCell);
break;
case NwUeRrcToRls::RRC_PDU_DELIVERY:
deliverUplinkPdu(rls::EPduType::RRC, std::move(w->pdu),
OctetString::FromOctet4(static_cast<int>(w->channel)));
break;
case NwUeRrcToRls::RESET_STI:
m_sti = utils::Random64();
break;
}
break;
}
case NtsMessageType::UE_APP_TO_RLS: {
auto *w = dynamic_cast<NwUeAppToRls *>(msg);
switch (w->present)
{
case NwUeAppToRls::DATA_PDU_DELIVERY: {
deliverUplinkPdu(rls::EPduType::DATA, std::move(w->pdu), OctetString::FromOctet4(static_cast<int>(w->psi)));
break;
}
}
break;
}
case NtsMessageType::TIMER_EXPIRED: {
auto *w = dynamic_cast<NwTimerExpired *>(msg);
if (w->timerId == TIMER_ID_MEASUREMENT)
{
setTimer(TIMER_ID_MEASUREMENT, m_measurementPeriod);
onMeasurement();
}
else if (w->timerId == TIMER_ID_RAPID_LAUNCH)
{
slowDownMeasurements();
}
break;
}
case NtsMessageType::UDP_SERVER_RECEIVE: {
auto *w = dynamic_cast<udp::NwUdpServerReceive *>(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;
}
default:
m_logger->unhandledNts(msg);
break;
}
delete msg;
}
void UeRlsTask::onQuit()
{
m_udpTask->quit();
delete m_udpTask;
}
void UeRlsTask::slowDownMeasurements()
{
m_measurementPeriod = TIMER_PERIOD_MEASUREMENT_MAX;
}
} // namespace nr::ue
......@@ -8,12 +8,15 @@
#pragma once
#include "rls.hpp"
#include <memory>
#include <optional>
#include <rrc/rrc.hpp>
#include <thread>
#include <udp/server_task.hpp>
#include <ue/types.hpp>
#include <unordered_map>
#include <urs/rls_pdu.hpp>
#include <utils/common_types.hpp>
#include <utils/logger.hpp>
#include <utils/nts.hpp>
#include <vector>
......@@ -21,30 +24,48 @@
namespace nr::ue
{
class UeMrTask : public NtsTask
class UeRlsTask : public NtsTask
{
private:
TaskBase *m_base;
std::unique_ptr<Logger> m_logger;
udp::UdpServerTask *m_udpTask;
UeRls *m_rlsEntity;
long m_lastPlmnSearchFailurePrinted;
std::vector<InetAddress> m_cellSearchSpace;
std::unordered_map<GlobalNci, UeCellMeasurement> m_pendingMeasurements;
std::unordered_map<GlobalNci, UeCellMeasurement> m_activeMeasurements;
bool m_pendingPlmnResponse;
int64_t m_measurementPeriod;
uint64_t m_sti;
std::optional<UeCellInfo> m_servingCell;
friend class UeCmdHandler;
public:
explicit UeMrTask(TaskBase *base);
~UeMrTask() override = default;
explicit UeRlsTask(TaskBase *base);
~UeRlsTask() override = default;
protected:
void onStart() override;
void onLoop() override;
void onQuit() override;
private:
void receiveDownlinkPayload(rls::EPayloadType type, OctetString &&payload);
private: /* Base */
void slowDownMeasurements();
private: /* Transport */
void receiveRlsMessage(const InetAddress &address, rls::RlsMessage &msg);
void sendRlsMessage(const InetAddress &address, const rls::RlsMessage &msg);
void deliverUplinkPdu(rls::EPduType pduType, OctetString &&pdu, OctetString &&payload);
void deliverDownlinkPdu(rls::RlsPduDelivery &msg);
private: /* Measurement */
void onMeasurement();
void receiveCellInfoResponse(const rls::RlsCellInfoResponse &msg);
void onCoverageChange(const std::vector<GlobalNci> &entered, const std::vector<GlobalNci> &exited);
void plmnSearchRequested();
void handleCellSelectionCommand(const GlobalNci &cellId, bool isSuitable);
};
} // namespace nr::ue
\ No newline at end of file
//
// This file is a part of UERANSIM open source project.
// Copyright (c) 2021 ALİ GÜNGÖR.
//
// The software and all associated files are licensed under GPL-3.0
// and subject to the terms and conditions defined in LICENSE file.
//
#include "task.hpp"
#include <ue/app/task.hpp>
#include <ue/nts.hpp>
#include <ue/rrc/task.hpp>
#include <utils/constants.hpp>
namespace nr::ue
{
void UeRlsTask::receiveRlsMessage(const InetAddress &address, rls::RlsMessage &msg)
{
switch (msg.msgType)
{
case rls::EMessageType::CELL_INFO_RESPONSE: {
receiveCellInfoResponse((const rls::RlsCellInfoResponse &)msg);
break;
case rls::EMessageType::PDU_DELIVERY: {
deliverDownlinkPdu((rls::RlsPduDelivery &)msg);
break;
}
default:
m_logger->err("Unhandled RLS message type[%d]", static_cast<int>(msg.msgType));
break;
}
}
}
void UeRlsTask::sendRlsMessage(const InetAddress &address, const rls::RlsMessage &msg)
{
OctetString stream{};
rls::EncodeRlsMessage(msg, stream);
m_udpTask->send(address, stream);
}
void UeRlsTask::deliverUplinkPdu(rls::EPduType pduType, OctetString &&pdu, OctetString &&payload)
{
if (!m_servingCell.has_value())
{
m_logger->warn("RLS uplink delivery requested without a serving cell");
return;
}
rls::RlsPduDelivery msg{m_sti};
msg.pduType = pduType;
msg.pdu = std::move(pdu);
msg.payload = std::move(payload);
sendRlsMessage(InetAddress{m_servingCell->linkIp, cons::PortalPort}, msg);
}
void UeRlsTask::deliverDownlinkPdu(rls::RlsPduDelivery &msg)
{
if (msg.pduType == rls::EPduType::RRC)
{
auto *nw = new NwUeRlsToRrc(NwUeRlsToRrc::RRC_PDU_DELIVERY);
nw->channel = static_cast<rrc::RrcChannel>(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 NwUeRlsToApp(NwUeRlsToApp::DATA_PDU_DELIVERY);
nw->psi = msg.payload.get4I(0);
nw->pdu = std::move(msg.pdu);
m_base->appTask->push(nw);
}
}
} // namespace nr::ue
This diff is collapsed.
......@@ -9,13 +9,15 @@
#include "task.hpp"
#include <asn/utils/utils.hpp>
#include <rrc/encode.hpp>
#include <ue/mr/task.hpp>
#include <ue/nas/task.hpp>
#include <ue/nts.hpp>
#include <utils/common.hpp>
#include <asn/rrc/ASN_RRC_DLInformationTransfer-IEs.h>
#include <asn/rrc/ASN_RRC_DLInformationTransfer.h>
#include <asn/rrc/ASN_RRC_Paging.h>
#include <asn/rrc/ASN_RRC_PagingRecord.h>
#include <asn/rrc/ASN_RRC_PagingRecordList.h>
#include <asn/rrc/ASN_RRC_RRCSetup-IEs.h>
#include <asn/rrc/ASN_RRC_RRCSetup.h>
#include <asn/rrc/ASN_RRC_RRCSetupComplete-IEs.h>
......@@ -129,12 +131,31 @@ void UeRrcTask::receiveRrcRelease(const ASN_RRC_RRCRelease &msg)
{
m_logger->debug("RRC Release received");
m_state = ERrcState::RRC_IDLE;
auto *wr = new NwUeRrcToMr(NwUeRrcToMr::RRC_CONNECTION_RELEASE);
wr->cause = rls::ECause::RRC_NORMAL_RELEASE;
m_base->mrTask->push(wr);
m_base->nasTask->push(new NwUeRrcToNas(NwUeRrcToNas::RRC_CONNECTION_RELEASE));
}
void UeRrcTask::receivePaging(const ASN_RRC_Paging &msg)
{
std::vector<GutiMobileIdentity> tmsiIds{};
asn::ForeachItem(*msg.pagingRecordList, [&tmsiIds](auto &pagingRecord) {
if (pagingRecord.ue_Identity.present == ASN_RRC_PagingUE_Identity_PR_ng_5G_S_TMSI)
{
auto recordTmsi = asn::GetOctetString(pagingRecord.ue_Identity.choice.ng_5G_S_TMSI);
auto tmsiOs = BitBuffer{recordTmsi.data()};
GutiMobileIdentity tmsi{};
tmsi.amfSetId = tmsiOs.readBits(10);
tmsi.amfPointer = tmsiOs.readBits(6);
tmsi.tmsi = octet4{static_cast<uint32_t>(tmsiOs.readBitsLong(32) & 0xFFFFFFFFu)};
tmsiIds.push_back(tmsi);
}
});
auto *w = new NwUeRrcToNas(NwUeRrcToNas::PAGING);
w->pagingTmsi = std::move(tmsiIds);
m_base->nasTask->push(w);
}
} // namespace nr::ue
\ No newline at end of file
This diff is collapsed.
......@@ -35,6 +35,7 @@ extern "C"
struct ASN_RRC_RRCSetup;
struct ASN_RRC_RRCReject;
struct ASN_RRC_RRCRelease;
struct ASN_RRC_Paging;
}
namespace nr::ue
......@@ -73,15 +74,12 @@ class UeRrcTask : public NtsTask
void receiveRrcReject(const ASN_RRC_RRCReject &msg);
void receiveRrcRelease(const ASN_RRC_RRCRelease &msg);
void receiveDownlinkInformationTransfer(const ASN_RRC_DLInformationTransfer &msg);
void receivePaging(const ASN_RRC_Paging &msg);
void handleRadioLinkFailure();
/* RRC channel send message */
void sendRrcMessage(ASN_RRC_BCCH_BCH_Message *msg);
void sendRrcMessage(ASN_RRC_BCCH_DL_SCH_Message *msg);
void sendRrcMessage(ASN_RRC_DL_CCCH_Message *msg);
void sendRrcMessage(ASN_RRC_DL_DCCH_Message *msg);
void sendRrcMessage(ASN_RRC_PCCH_Message *msg);
void sendRrcMessage(ASN_RRC_UL_CCCH_Message *msg);
void sendRrcMessage(ASN_RRC_UL_CCCH1_Message *msg);
void sendRrcMessage(ASN_RRC_UL_DCCH_Message *msg);
......@@ -92,9 +90,6 @@ class UeRrcTask : public NtsTask
void receiveRrcMessage(ASN_RRC_DL_CCCH_Message *msg);
void receiveRrcMessage(ASN_RRC_DL_DCCH_Message *msg);
void receiveRrcMessage(ASN_RRC_PCCH_Message *msg);
void receiveRrcMessage(ASN_RRC_UL_CCCH_Message *msg);
void receiveRrcMessage(ASN_RRC_UL_CCCH1_Message *msg);
void receiveRrcMessage(ASN_RRC_UL_DCCH_Message *msg);
};
} // namespace nr::ue
......@@ -185,6 +185,8 @@ Json ToJson(const ERegUpdateCause &v)
return "INTER_SYSTEM_CHANGE_S1_TO_N1";
case ERegUpdateCause::CONNECTION_RECOVERY:
return "CONNECTION_RECOVERY";
case ERegUpdateCause::FALLBACK_INDICATION:
return "FALLBACK_INDICATION";
case ERegUpdateCause::MM_OR_S1_CAPABILITY_CHANGE:
return "MM_OR_S1_CAPABILITY_CHANGE";
case ERegUpdateCause::USAGE_SETTING_CHANGE:
......@@ -243,4 +245,33 @@ Json ToJson(const UePduSessionInfo &v)
});
}
Json ToJson(const EServiceReqCause &v)
{
switch (v)
{
case EServiceReqCause::UNSPECIFIED:
return "UNSPECIFIED";
case EServiceReqCause::IDLE_PAGING:
return "IDLE_PAGING";
case EServiceReqCause::CONNECTED_3GPP_NOTIFICATION_N3GPP:
return "CONNECTED_3GPP_NOTIFICATION_N3GPP";
case EServiceReqCause::IDLE_UPLINK_SIGNAL_PENDING:
return "IDLE_UPLINK_SIGNAL_PENDING";
case EServiceReqCause::IDLE_UPLINK_DATA_PENDING:
return "IDLE_UPLINK_DATA_PENDING";
case EServiceReqCause::CONNECTED_UPLINK_DATA_PENDING:
return "CONNECTED_UPLINK_DATA_PENDING";
case EServiceReqCause::NON_3GPP_AS_ESTABLISHED:
return "NON_3GPP_AS_ESTABLISHED";
case EServiceReqCause::IDLE_3GPP_NOTIFICATION_N3GPP:
return "IDLE_3GPP_NOTIFICATION_N3GPP";
case EServiceReqCause::EMERGENCY_FALLBACK:
return "EMERGENCY_FALLBACK";
case EServiceReqCause::FALLBACK_INDICATION:
return "FALLBACK_INDICATION";
default:
return "?";
}
}
} // namespace nr::ue
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -40,6 +40,7 @@ class OctetString
void appendOctet(int v);
void appendOctet(int bigHalf, int littleHalf);
void appendOctet2(octet2 v);
void appendOctet2(uint16_t v);
void appendOctet2(int v);
void appendOctet3(octet3 v);
void appendOctet3(int v);
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment