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

Merge pull request #278 from aligungr/dev

v3.1.6
parents 8cceeae6 cf9d1ebb
......@@ -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.5-blue" />
<img src="https://img.shields.io/badge/UERANSIM-v3.1.6-blue" />
<img src="https://img.shields.io/badge/3GPP-R15-orange" />
<img src="https://img.shields.io/badge/License-GPL--3.0-green"/>
</p>
......
This diff is collapsed.
......@@ -8,6 +8,7 @@
#pragma once
#include <array>
#include <memory>
#include <string>
#include <utils/common_types.hpp>
......@@ -43,12 +44,24 @@ struct UeCliCommand
INFO,
STATUS,
TIMERS,
PS_ESTABLISH,
PS_RELEASE,
PS_RELEASE_ALL,
DE_REGISTER,
} present;
// DE_REGISTER
EDeregCause deregCause{};
// PS_RELEASE
std::array<int8_t, 16> psIds{};
int psCount{};
// PS_ESTABLISH
std::optional<SingleSlice> sNssai{};
std::optional<std::string> apn{};
bool isEmergency{};
explicit UeCliCommand(PR present) : present(present)
{
}
......
......@@ -134,7 +134,7 @@ static void ReadOptions(int argc, char **argv)
{
opt::OptionsDescription desc{"UERANSIM", cons::Tag, "Command Line Interface",
cons::Owner, "nr-cli", {"<node-name> [option...]", "--dump"},
true, false};
{}, true, false};
opt::OptionItem itemDump = {'d', "dump", "List all UE and gNBs in the environment", std::nullopt};
opt::OptionItem itemExec = {'e', "exec", "Execute the given command directly without an interactive shell",
......
......@@ -78,9 +78,15 @@ static nr::gnb::GnbConfig *ReadConfigYaml()
static void ReadOptions(int argc, char **argv)
{
opt::OptionsDescription desc{cons::Project, cons::Tag, "5G-SA gNB implementation",
cons::Owner, "nr-gnb", {"-c <config-file> [option...]"},
true, false};
opt::OptionsDescription desc{cons::Project,
cons::Tag,
"5G-SA gNB implementation",
cons::Owner,
"nr-gnb",
{"-c <config-file> [option...]"},
{},
true,
false};
opt::OptionItem itemConfigFile = {'c', "config", "Use specified configuration file for gNB", "config-file"};
opt::OptionItem itemDisableCmd = {'l', "disable-cmd", "Disable command line functionality for this instance",
......
......@@ -9,8 +9,8 @@
#include "task.hpp"
#include <asn/ngap/ASN_NGAP_QosFlowSetupRequestItem.h>
#include <gnb/mr/task.hpp>
#include <gnb/gtp/proto.hpp>
#include <gnb/mr/task.hpp>
#include <utils/constants.hpp>
#include <utils/libc_error.hpp>
......@@ -61,12 +61,16 @@ void GtpTask::onLoop()
handleUeContextUpdate(*w->update);
break;
}
case NwGnbNgapToGtp::UE_CONTEXT_RELEASE: {
handleUeContextDelete(w->ueId);
break;
}
case NwGnbNgapToGtp::SESSION_CREATE: {
handleSessionCreate(w->resource);
break;
}
case NwGnbNgapToGtp::UE_CONTEXT_RELEASE: {
handleUeContextDelete(w->ueId);
case NwGnbNgapToGtp::SESSION_RELEASE: {
handleSessionRelease(w->ueId, w->psi);
break;
}
}
......@@ -122,6 +126,28 @@ void GtpTask::handleSessionCreate(PduSessionResource *session)
updateAmbrForSession(sessionInd);
}
void GtpTask::handleSessionRelease(int ueId, int psi)
{
if (!m_ueContexts.count(ueId))
{
m_logger->err("PDU session resource could not be released, UE context with ID[%d] not found", ueId);
return;
}
uint64_t sessionInd = MakeSessionResInd(ueId, psi);
// Remove all session information from rate limiter
m_rateLimiter->updateSessionUplinkLimit(sessionInd, 0);
m_rateLimiter->updateUeDownlinkLimit(sessionInd, 0);
// And remove from PDU session table
int teid = m_pduSessions[sessionInd]->downTunnel.teid;
m_pduSessions.erase(sessionInd);
// And remove from the tree
m_sessionTree.remove(sessionInd, teid);
}
void GtpTask::handleUeContextDelete(int ueId)
{
// Find PDU sessions of the UE
......@@ -162,7 +188,7 @@ void GtpTask::handleUplinkData(int ueId, int psi, OctetString &&pdu)
if (!m_pduSessions.count(sessionInd))
{
m_logger->err("Uplink data failure, PDU session not found. UE: %d PSI: %d", ueId, psi);
m_logger->err("Uplink data failure, PDU session not found. UE[%d] PSI[%d]", ueId, psi);
return;
}
......
......@@ -48,6 +48,7 @@ class GtpTask : public NtsTask
void handleUdpReceive(const udp::NwUdpServerReceive &msg);
void handleUeContextUpdate(const GtpUeContextUpdate &msg);
void handleSessionCreate(PduSessionResource *session);
void handleSessionRelease(int ueId, int psi);
void handleUeContextDelete(int ueId);
void handleUplinkData(int ueId, int psi, OctetString &&data);
......
......@@ -25,7 +25,7 @@ namespace nr::gnb
void NgapTask::handleInitialNasTransport(int ueId, const OctetString &nasPdu, long rrcEstablishmentCause)
{
m_logger->debug("Initial NAS message received from UE %d", ueId);
m_logger->debug("Initial NAS message received from UE[%d]", ueId);
if (m_ueCtx.count(ueId))
{
......
......@@ -14,6 +14,10 @@
#include <asn/ngap/ASN_NGAP_AssociatedQosFlowList.h>
#include <asn/ngap/ASN_NGAP_GTPTunnel.h>
#include <asn/ngap/ASN_NGAP_PDUSessionResourceFailedToSetupItemSURes.h>
#include <asn/ngap/ASN_NGAP_PDUSessionResourceReleaseCommand.h>
#include <asn/ngap/ASN_NGAP_PDUSessionResourceReleaseResponse.h>
#include <asn/ngap/ASN_NGAP_PDUSessionResourceReleaseResponseTransfer.h>
#include <asn/ngap/ASN_NGAP_PDUSessionResourceReleasedItemRelRes.h>
#include <asn/ngap/ASN_NGAP_PDUSessionResourceSetupItemSUReq.h>
#include <asn/ngap/ASN_NGAP_PDUSessionResourceSetupItemSURes.h>
#include <asn/ngap/ASN_NGAP_PDUSessionResourceSetupRequest.h>
......@@ -21,12 +25,14 @@
#include <asn/ngap/ASN_NGAP_PDUSessionResourceSetupResponse.h>
#include <asn/ngap/ASN_NGAP_PDUSessionResourceSetupResponseTransfer.h>
#include <asn/ngap/ASN_NGAP_PDUSessionResourceSetupUnsuccessfulTransfer.h>
#include <asn/ngap/ASN_NGAP_PDUSessionResourceToReleaseItemRelCmd.h>
#include <asn/ngap/ASN_NGAP_ProtocolIE-Field.h>
#include <asn/ngap/ASN_NGAP_QosFlowPerTNLInformationItem.h>
#include <asn/ngap/ASN_NGAP_QosFlowPerTNLInformationList.h>
#include <asn/ngap/ASN_NGAP_QosFlowSetupRequestItem.h>
#include <asn/ngap/ASN_NGAP_QosFlowSetupRequestList.h>
#include <gnb/gtp/task.hpp>
#include <set>
#include <stdexcept>
namespace nr::gnb
......@@ -193,12 +199,14 @@ void NgapTask::receiveSessionResourceSetupRequest(int amfId, ASN_NGAP_PDUSession
sendNgapUeAssociated(ue->ctxId, respPdu);
if (failedList.empty())
m_logger->info("PDU session resource is setup for UE[%d] count[%d]", ue->ctxId, successList.size());
m_logger->info("PDU session resource(s) setup for UE[%d] count[%d]", ue->ctxId,
static_cast<int>(successList.size()));
else if (successList.empty())
m_logger->err("PDU session resource setup was failed for UE[%d] count[%d]", ue->ctxId, failedList.size());
m_logger->err("PDU session resource(s) setup was failed for UE[%d] count[%d]", ue->ctxId,
static_cast<int>(failedList.size()));
else
m_logger->err("PDU session establishment is partially successful for UE[%d], success[%d], failed[%d]",
successList.size(), failedList.size());
static_cast<int>(successList.size()), static_cast<int>(failedList.size()));
}
std::optional<NgapCause> NgapTask::setupPduSessionResource(PduSessionResource *resource)
......@@ -231,4 +239,68 @@ std::optional<NgapCause> NgapTask::setupPduSessionResource(PduSessionResource *r
return {};
}
void NgapTask::receiveSessionResourceReleaseCommand(int amfId, ASN_NGAP_PDUSessionResourceReleaseCommand *msg)
{
auto *ue = findUeByNgapIdPair(amfId, ngap_utils::FindNgapIdPair(msg));
if (ue == nullptr)
return;
std::set<int> psIds{};
auto *ieReq = asn::ngap::GetProtocolIe(msg, ASN_NGAP_ProtocolIE_ID_id_PDUSessionResourceToReleaseListRelCmd);
if (ieReq)
{
auto &list = ieReq->PDUSessionResourceToReleaseListRelCmd.list;
for (int i = 0; i < list.count; i++)
{
auto &item = list.array[i];
if (item)
psIds.insert(static_cast<int>(item->pDUSessionID));
}
}
ieReq = asn::ngap::GetProtocolIe(msg, ASN_NGAP_ProtocolIE_ID_id_NAS_PDU);
if (ieReq)
deliverDownlinkNas(ue->ctxId, asn::GetOctetString(ieReq->NAS_PDU));
auto *ieResp = asn::New<ASN_NGAP_PDUSessionResourceReleaseResponseIEs>();
ieResp->id = ASN_NGAP_ProtocolIE_ID_id_PDUSessionResourceReleasedListRelRes;
ieResp->criticality = ASN_NGAP_Criticality_ignore;
ieResp->value.present =
ASN_NGAP_PDUSessionResourceReleaseResponseIEs__value_PR_PDUSessionResourceReleasedListRelRes;
// Perform release
for (auto &psi : psIds)
{
auto *w = new NwGnbNgapToGtp(NwGnbNgapToGtp::SESSION_RELEASE);
w->ueId = ue->ctxId;
w->psi = psi;
m_base->gtpTask->push(w);
}
for (auto &psi : psIds)
{
auto *tr = asn::New<ASN_NGAP_PDUSessionResourceReleaseResponseTransfer>();
OctetString encodedTr = ngap_encode::EncodeS(asn_DEF_ASN_NGAP_PDUSessionResourceReleaseResponseTransfer, tr);
if (encodedTr.length() == 0)
throw std::runtime_error("PDUSessionResourceReleaseResponseTransfer encoding failed");
asn::Free(asn_DEF_ASN_NGAP_PDUSessionResourceReleaseResponseTransfer, tr);
auto *item = asn::New<ASN_NGAP_PDUSessionResourceReleasedItemRelRes>();
item->pDUSessionID = static_cast<ASN_NGAP_PDUSessionID_t>(psi);
asn::SetOctetString(item->pDUSessionResourceReleaseResponseTransfer, encodedTr);
asn::SequenceAdd(ieResp->value.choice.PDUSessionResourceReleasedListRelRes, item);
}
auto *respPdu = asn::ngap::NewMessagePdu<ASN_NGAP_PDUSessionResourceReleaseResponse>({ieResp});
sendNgapUeAssociated(ue->ctxId, respPdu);
m_logger->info("PDU session resource(s) released for UE[%d] count[%d]", ue->ctxId, static_cast<int>(psIds.size()));
}
} // namespace nr::gnb
\ No newline at end of file
......@@ -30,6 +30,7 @@ extern "C" struct ASN_NGAP_UEContextModificationRequest;
extern "C" struct ASN_NGAP_AMFConfigurationUpdate;
extern "C" struct ASN_NGAP_OverloadStart;
extern "C" struct ASN_NGAP_OverloadStop;
extern "C" struct ASN_NGAP_PDUSessionResourceReleaseCommand;
namespace nr::gnb
{
......@@ -102,6 +103,7 @@ class NgapTask : public NtsTask
/* PDU session management */
void receiveSessionResourceSetupRequest(int amfId, ASN_NGAP_PDUSessionResourceSetupRequest *msg);
void receiveSessionResourceReleaseCommand(int amfId, ASN_NGAP_PDUSessionResourceReleaseCommand *msg);
std::optional<NgapCause> setupPduSessionResource(PduSessionResource *resource);
/* UE context management */
......
......@@ -230,6 +230,9 @@ void NgapTask::handleSctpMessage(int amfId, uint16_t stream, const UniqueBuffer
case ASN_NGAP_InitiatingMessage__value_PR_OverloadStop:
receiveOverloadStop(amf->ctxId, &value.choice.OverloadStop);
break;
case ASN_NGAP_InitiatingMessage__value_PR_PDUSessionResourceReleaseCommand:
receiveSessionResourceReleaseCommand(amf->ctxId, &value.choice.PDUSessionResourceReleaseCommand);
break;
default:
m_logger->err("Unhandled NGAP initiating-message received (%d)", value.present);
break;
......
......@@ -119,8 +119,9 @@ struct NwGnbNgapToGtp : NtsMessage
enum PR
{
UE_CONTEXT_UPDATE,
SESSION_CREATE,
UE_CONTEXT_RELEASE,
SESSION_CREATE,
SESSION_RELEASE,
} present;
// UE_CONTEXT_UPDATE
......@@ -130,8 +131,12 @@ struct NwGnbNgapToGtp : NtsMessage
PduSessionResource *resource{};
// UE_CONTEXT_RELEASE
// SESSION_RELEASE
int ueId{};
// SESSION_RELEASE
int psi{};
explicit NwGnbNgapToGtp(PR present) : NtsMessage(NtsMessageType::GNB_NGAP_TO_GTP), present(present)
{
}
......
......@@ -220,9 +220,9 @@ static nr::ue::UeConfig *ReadConfigYaml()
static void ReadOptions(int argc, char **argv)
{
opt::OptionsDescription desc{cons::Project, cons::Tag, "5G-SA UE implementation",
cons::Owner, "nr-ue", {"-c <config-file> [option...]"},
true, false};
opt::OptionsDescription desc{
cons::Project, cons::Tag, "5G-SA UE implementation", cons::Owner, "nr-ue", {"-c <config-file> [option...]"}, {},
true, false};
opt::OptionItem itemConfigFile = {'c', "config", "Use specified configuration file for UE", "config-file"};
opt::OptionItem itemImsi = {'i', "imsi", "Use specified IMSI number instead of provided one", "imsi"};
......
......@@ -129,6 +129,27 @@ void UeCmdHandler::handleCmdImpl(NwUeCliCommand &msg)
sendResult(msg.address, "De-registration procedure triggered. UE device will be switched off.");
break;
}
case app::UeCliCommand::PS_RELEASE: {
for (int i = 0; i < msg.cmd->psCount; i++)
m_base->nasTask->sm->sendReleaseRequest(static_cast<int>(msg.cmd->psIds[i]) % 16);
sendResult(msg.address, "PDU session release procedure(s) triggered");
break;
}
case app::UeCliCommand::PS_RELEASE_ALL: {
m_base->nasTask->sm->sendReleaseRequestForAll();
sendResult(msg.address, "PDU session release procedure(s) triggered");
break;
}
case app::UeCliCommand::PS_ESTABLISH: {
SessionConfig config{};
config.type = nas::EPduSessionType::IPV4;
config.isEmergency = msg.cmd->isEmergency;
config.apn = msg.cmd->apn;
config.sNssai = msg.cmd->sNssai;
m_base->nasTask->sm->sendEstablishmentRequest(config);
sendResult(msg.address, "PDU session establishment procedure triggered");
break;
}
}
}
......
......@@ -83,9 +83,11 @@ class NasMm
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 sendMobilityRegistration(ERegUpdateCause updateCause);
void receiveRegistrationAccept(const nas::RegistrationAccept &msg);
void receiveInitialRegistrationAccept(const nas::RegistrationAccept &msg);
void receiveMobilityRegistrationAccept(const nas::RegistrationAccept &msg);
......
......@@ -38,12 +38,12 @@ static nas::IEIntegrityProtectionMaximumDataRate MakeIntegrityMaxRate(const Inte
void NasSm::sendEstablishmentRequest(const SessionConfig &config)
{
m_logger->debug("Sending PDU session establishment request");
m_logger->debug("Sending PDU Session Establishment Request");
/* Control the protocol state */
if (!m_mm->isRegistered())
{
m_logger->err("UE is not registered");
m_logger->err("PDU session establishment could not be triggered, UE is not registered");
return;
}
......@@ -125,42 +125,29 @@ void NasSm::sendEstablishmentRequest(const SessionConfig &config)
sendSmMessage(psi, *pt.message);
}
void NasSm::receivePduSessionEstablishmentAccept(const nas::PduSessionEstablishmentAccept &msg)
void NasSm::receiveEstablishmentAccept(const nas::PduSessionEstablishmentAccept &msg)
{
m_logger->debug("PDU Session Establishment Accept received");
if (msg.smCause.has_value())
{
m_logger->warn("SM cause received in PduSessionEstablishmentAccept [%s]",
nas::utils::EnumToString(msg.smCause->value));
}
if (msg.pti < ProcedureTransaction::MIN_ID || msg.pti > ProcedureTransaction::MAX_ID)
{
// PTI is required for PDU session establishment request
m_logger->err("Received PTI [%d] value is invalid", msg.pti);
sendSmCause(nas::ESmCause::INVALID_PTI_VALUE, msg.pduSessionId);
return;
}
if (m_procedureTransactions[msg.pti].psi != msg.pduSessionId)
{
m_logger->err("Received PSI value [%d] is invalid, expected was [%d]", msg.pduSessionId,
m_procedureTransactions[msg.pti].psi);
sendSmCause(nas::ESmCause::INVALID_PTI_VALUE, msg.pduSessionId);
if (!checkPtiAndPsi(msg))
return;
}
freeProcedureTransactionId(msg.pti);
auto& pduSession = m_pduSessions[msg.pduSessionId];
auto &pduSession = m_pduSessions[msg.pduSessionId];
if (pduSession->psState != EPsState::ACTIVE_PENDING)
{
m_logger->err("PS establishment accept received without requested");
sendSmCause(nas::ESmCause::MESSAGE_TYPE_NOT_COMPATIBLE_WITH_THE_PROTOCOL_STATE, pduSession->psi);
m_logger->err("PS establishment accept received without being requested");
sendSmCause(nas::ESmCause::MESSAGE_TYPE_NOT_COMPATIBLE_WITH_THE_PROTOCOL_STATE, msg.pti, msg.pduSessionId);
return;
}
if (msg.smCause.has_value())
{
m_logger->warn("SM cause received in PduSessionEstablishmentAccept [%s]",
nas::utils::EnumToString(msg.smCause->value));
}
pduSession->psState = EPsState::ACTIVE;
pduSession->authorizedQoSRules = nas::utils::DeepCopyIe(msg.authorizedQoSRules);
pduSession->sessionAmbr = nas::utils::DeepCopyIe(msg.sessionAmbr);
......@@ -183,20 +170,41 @@ void NasSm::receivePduSessionEstablishmentAccept(const nas::PduSessionEstablishm
m_logger->info("PDU Session establishment is successful PSI[%d]", pduSession->psi);
}
void NasSm::receivePduSessionEstablishmentReject(const nas::PduSessionEstablishmentReject &msg)
void NasSm::receiveEstablishmentRoutingFailure(const nas::PduSessionEstablishmentRequest &msg)
{
m_logger->err("PDU Session Establishment Reject received [%s]", nas::utils::EnumToString(msg.smCause.value));
// TODO
m_logger->err("PDU Session Establishment Request received due to a routing failure");
if (!checkPtiAndPsi(msg))
return;
abortProcedureByPti(msg.pti);
}
void NasSm::abortEstablishmentRequest(int pti)
void NasSm::receiveEstablishmentReject(const nas::PduSessionEstablishmentReject &msg)
{
int psi = m_procedureTransactions[pti].psi;
m_logger->err("PDU Session Establishment Reject received [%s]", nas::utils::EnumToString(msg.smCause.value));
if (!checkPtiAndPsi(msg))
return;
freeProcedureTransactionId(msg.pti);
m_logger->debug("PDU Session Establishment Procedure aborted for PTI[%d], PSI[%d]", pti, psi);
auto &pduSession = m_pduSessions[msg.pduSessionId];
freeProcedureTransactionId(pti);
freePduSessionId(psi);
if (pduSession->psState != EPsState::ACTIVE_PENDING)
{
m_logger->err("PS establishment reject received without being requested");
sendSmCause(nas::ESmCause::MESSAGE_TYPE_NOT_COMPATIBLE_WITH_THE_PROTOCOL_STATE, msg.pti, msg.pduSessionId);
return;
}
pduSession->psState = EPsState::INACTIVE;
if (pduSession->isEmergency)
{
// This not much important and no need for now
// TODO: inform the upper layers of the failure of the procedure
}
}
} // 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 "sm.hpp"
#include <nas/utils.hpp>
#include <set>
#include <ue/app/task.hpp>
#include <ue/mm/mm.hpp>
namespace nr::ue
{
bool NasSm::checkPtiAndPsi(const nas::SmMessage &msg)
{
if (msg.pti < ProcedureTransaction::MIN_ID || msg.pti > ProcedureTransaction::MAX_ID)
{
m_logger->err("Received PTI [%d] value is invalid", msg.pti);
sendSmCause(nas::ESmCause::INVALID_PTI_VALUE, msg.pti, msg.pduSessionId);
return false;
}
if (m_procedureTransactions[msg.pti].psi != msg.pduSessionId)
{
m_logger->err("Received PSI value [%d] is invalid, expected was [%d]", msg.pduSessionId,
m_procedureTransactions[msg.pti].psi);
sendSmCause(nas::ESmCause::INVALID_PTI_VALUE, msg.pti, msg.pduSessionId);
return false;
}
return true;
}
void NasSm::abortProcedureByPti(int pti)
{
auto &pt = m_procedureTransactions[pti];
if (pt.state != EPtState::PENDING)
return;
if (pt.message == nullptr)
{
m_logger->err("Procedure abortion failure, stored SM message is null");
return;
}
auto msgType = pt.message->messageType;
int psi = m_procedureTransactions[pti].psi;
m_logger->debug("Aborting SM procedure for PTI[%d], PSI[%d]", pti, psi);
if (msgType == nas::EMessageType::PDU_SESSION_ESTABLISHMENT_REQUEST)
{
freeProcedureTransactionId(pti);
freePduSessionId(psi);
}
else if (msgType == nas::EMessageType::PDU_SESSION_RELEASE_REQUEST)
{
freeProcedureTransactionId(pti);
localReleaseSession(psi);
}
// todo: others
}
void NasSm::abortProcedureByPtiOrPsi(int pti, int psi)
{
std::set<int> ptiToAbort{};
int i = 0;
for (auto &pt : m_procedureTransactions)
{
if (pt.state == EPtState::PENDING && pt.psi == psi)
ptiToAbort.insert(i);
i++;
}
ptiToAbort.insert(pti);
for (int id : ptiToAbort)
abortProcedureByPti(id);
}
} // 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 "sm.hpp"
#include <nas/proto_conf.hpp>
#include <nas/utils.hpp>
#include <optional>
#include <ue/app/task.hpp>
#include <ue/mm/mm.hpp>
namespace nr::ue
{
void NasSm::sendReleaseRequest(int psi)
{
/* Control the PDU session state */
auto &ps = m_pduSessions[psi];
if (ps->psState != EPsState::ACTIVE)
{
m_logger->warn("PDU session release procedure could not start: PS[%d] is not active already", psi);
return;
}
m_logger->debug("Sending PDU Session Release Request for PSI[%d]", psi);
/* Allocate PTI */
int pti = allocateProcedureTransactionId();
if (pti == 0)
return;
/* Construct the message */
auto req = std::make_unique<nas::PduSessionReleaseRequest>();
req->pti = pti;
req->pduSessionId = psi;
req->smCause = nas::IE5gSmCause{};
req->smCause->value = nas::ESmCause::REGULAR_DEACTIVATION;
/* Set relevant fields of the PT, and start T3582 */
auto &pt = m_procedureTransactions[pti];
pt.state = EPtState::PENDING;
pt.timer = newTransactionTimer(3582);
pt.message = std::move(req);
pt.psi = psi;
/* Send SM message */
sendSmMessage(psi, *pt.message);
}
void NasSm::sendReleaseRequestForAll()
{
for (auto &ps : m_pduSessions)
if (ps->psState == EPsState::ACTIVE)
sendReleaseRequest(ps->psi);
}
void NasSm::receiveReleaseReject(const nas::PduSessionReleaseReject &msg)
{
auto cause = msg.smCause.value;
m_logger->err("PDU Session Release Reject received [%s]", nas::utils::EnumToString(cause));
if (!checkPtiAndPsi(msg))
return;
freeProcedureTransactionId(msg.pti);
if (cause != nas::ESmCause::PTI_ALREADY_IN_USE && cause != nas::ESmCause::INVALID_PDU_SESSION_IDENTITY)
{
auto &pduSession = m_pduSessions[msg.pduSessionId];
if (pduSession->psState != EPsState::INACTIVE_PENDING)
{
m_logger->err("PS release reject received without being requested");
sendSmCause(nas::ESmCause::MESSAGE_TYPE_NOT_COMPATIBLE_WITH_THE_PROTOCOL_STATE, msg.pti, msg.pduSessionId);
return;
}
pduSession->psState = EPsState::ACTIVE;
}
else
{
localReleaseSession(msg.pduSessionId);
}
}
void NasSm::receiveReleaseCommand(const nas::PduSessionReleaseCommand &msg)
{
m_logger->debug("PDU Session Release Command received");
int psi = msg.pduSessionId;
int pti = msg.pti;
/* Abnormal case handling 6.3.3.6/a */
if (m_pduSessions[msg.pduSessionId]->psState == EPsState::INACTIVE)
{
m_logger->err("PS[%d] is already in inactive state, ignoring release command", msg.pduSessionId);
sendSmCause(nas::ESmCause::INVALID_PDU_SESSION_IDENTITY, msg.pti, msg.pduSessionId);
return;
}
/* Abnormal case handling 6.4.1.6/c */
if (m_pduSessions[psi]->psState == EPsState::ACTIVE_PENDING)
{
m_logger->warn("PDU Session Release Command ignored for PSI[%d] due to collision with establishment procedure.",
psi);
return;
}
/* Handle reactivation case (take the fields before releasing the PS) */
std::optional<SessionConfig> reactivation{};
if (msg.smCause.value == nas::ESmCause::REACTIVATION_REQUESTED)
{
reactivation = SessionConfig{};
reactivation->type = m_pduSessions[psi]->sessionType;
reactivation->apn = m_pduSessions[psi]->apn;
reactivation->sNssai = m_pduSessions[psi]->sNssai;
}
/* Release PS and handle PT */
localReleaseSession(psi);
if (pti != 0)
{
auto &pt = m_procedureTransactions[pti];
if (pt.message == nullptr || pt.message->messageType != nas::EMessageType::PDU_SESSION_RELEASE_REQUEST)
{
m_logger->err("PTI mismatch occurred, received PTI[%d] has no PDU session release request", pti);
sendSmCause(nas::ESmCause::PTI_MISMATCH, msg.pti, msg.pduSessionId);
return;
}
// Warning: PS has been released, and the spec says that (6.3.3.3) this PTI should not be released immediately
// Therefore the PT does not hold a valid PSI after this point
pt.psi = 0;
// TODO: Let's immediately release the PT for now. See 6.3.3.3
freeProcedureTransactionId(pti);
}
/* Construct Release Complete message */
nas::PduSessionReleaseComplete resp{};
resp.pduSessionId = psi;
resp.pti = pti;
/* Send SM message */
sendSmMessage(psi, resp);
/* Handle reactivation */
if (reactivation.has_value())
{
m_logger->debug("Re-initiating a PDU session establishment procedure due to reactivation request");
sendEstablishmentRequest(*reactivation);
}
}
} // namespace nr::ue
......@@ -50,12 +50,15 @@ class NasSm
void localReleaseAllSessions();
bool anyEmergencySession();
/* Session Release */
void sendReleaseRequest(int psi);
void sendReleaseRequestForAll();
private:
/* Transport */
void sendSmMessage(int psi, const nas::SmMessage &msg);
void receiveSmStatus(const nas::FiveGSmStatus &msg);
void receiveSmCause(const nas::IE5gSmCause &msg);
void sendSmCause(const nas::ESmCause &cause, int psi);
void sendSmCause(const nas::ESmCause &cause, int pti, int psi);
/* Allocation */
int allocatePduSessionId(const SessionConfig &config);
......@@ -65,15 +68,24 @@ class NasSm
/* Session Establishment */
void sendEstablishmentRequest(const SessionConfig &config);
void receivePduSessionEstablishmentAccept(const nas::PduSessionEstablishmentAccept &msg);
void receivePduSessionEstablishmentReject(const nas::PduSessionEstablishmentReject &msg);
void abortEstablishmentRequest(int pti);
void receiveEstablishmentAccept(const nas::PduSessionEstablishmentAccept &msg);
void receiveEstablishmentReject(const nas::PduSessionEstablishmentReject &msg);
void receiveEstablishmentRoutingFailure(const nas::PduSessionEstablishmentRequest &msg);
/* Session Release */
void receiveReleaseReject(const nas::PduSessionReleaseReject &msg);
void receiveReleaseCommand(const nas::PduSessionReleaseCommand &msg);
/* Timer */
std::unique_ptr<nas::NasTimer> newTransactionTimer(int code);
void onTimerExpire(nas::NasTimer &timer);
void onTransactionTimerExpire(int pti);
/* Procedure */
bool checkPtiAndPsi(const nas::SmMessage &msg);
void abortProcedureByPti(int pti);
void abortProcedureByPtiOrPsi(int pti, int psi);
public:
/* Interface */
void handleNasEvent(const NwUeNasToNas &msg); // used by NAS
......
......@@ -62,8 +62,24 @@ void NasSm::onTransactionTimerExpire(int pti)
}
else
{
m_logger->err("PDU Session Establishment Procedure failure, no response from the network after 5 attempts");
abortEstablishmentRequest(pti);
m_logger->err("PDU Session Establishment procedure failure, no response from the network after 5 attempts");
abortProcedureByPti(pti);
}
break;
}
case 3582: {
if (pt.timer->getExpiryCount() < 5)
{
m_logger->warn("Retransmitting PDU Session Release Request due to T3582 expiry");
sendSmMessage(pt.psi, *pt.message);
pt.timer->start(false);
}
else
{
m_logger->err("PDU Session Release procedure failure, no response from the network after 5 attempts");
abortProcedureByPti(pti);
m_mm->sendMobilityRegistration(ERegUpdateCause::PS_STATUS_INFORM);
}
break;
}
......
......@@ -45,10 +45,19 @@ void NasSm::receiveSmMessage(const nas::SmMessage &msg)
switch (msg.messageType)
{
case nas::EMessageType::PDU_SESSION_ESTABLISHMENT_ACCEPT:
receivePduSessionEstablishmentAccept((const nas::PduSessionEstablishmentAccept &)msg);
receiveEstablishmentAccept((const nas::PduSessionEstablishmentAccept &)msg);
break;
case nas::EMessageType::PDU_SESSION_ESTABLISHMENT_REJECT:
receivePduSessionEstablishmentReject((const nas::PduSessionEstablishmentReject &)msg);
receiveEstablishmentReject((const nas::PduSessionEstablishmentReject &)msg);
break;
case nas::EMessageType::PDU_SESSION_ESTABLISHMENT_REQUEST:
receiveEstablishmentRoutingFailure((const nas::PduSessionEstablishmentRequest &)msg);
break;
case nas::EMessageType::PDU_SESSION_RELEASE_REJECT:
receiveReleaseReject((const nas::PduSessionReleaseReject &)msg);
break;
case nas::EMessageType::PDU_SESSION_RELEASE_COMMAND:
receiveReleaseCommand((const nas::PduSessionReleaseCommand &)msg);
break;
case nas::EMessageType::FIVEG_SM_STATUS:
receiveSmStatus((const nas::FiveGSmStatus &)msg);
......@@ -61,18 +70,37 @@ void NasSm::receiveSmMessage(const nas::SmMessage &msg)
void NasSm::receiveSmStatus(const nas::FiveGSmStatus &msg)
{
receiveSmCause(msg.smCause);
}
m_logger->err("SM Status received: %s", nas::utils::EnumToString(msg.smCause.value));
void NasSm::receiveSmCause(const nas::IE5gSmCause &msg)
{
m_logger->err("SM cause received: %s", nas::utils::EnumToString(msg.value));
if (msg.smCause.value == nas::ESmCause::INVALID_PTI_VALUE)
{
// "The UE shall abort any ongoing 5GSM procedure related to the received PTI value and stop any related timer."
abortProcedureByPti(msg.pti);
}
else if (msg.smCause.value == nas::ESmCause::MESSAGE_TYPE_NON_EXISTENT_OR_NOT_IMPLEMENTED)
{
// "The UE shall abort any ongoing 5GSM procedure related to the PTI or PDU session Id and stop any related
// timer."
abortProcedureByPtiOrPsi(msg.pti, msg.pduSessionId);
}
}
void NasSm::sendSmCause(const nas::ESmCause &cause, int psi)
void NasSm::sendSmCause(const nas::ESmCause &cause, int pti, int psi)
{
m_logger->warn("Sending SM Cause[%s] for PSI[%d]", nas::utils::EnumToString(cause), psi);
// TODO
nas::FiveGSmStatus smStatus{};
smStatus.smCause.value = cause;
smStatus.pti = pti;
smStatus.pduSessionId = psi;
nas::UlNasTransport ulTransport{};
ulTransport.payloadContainerType.payloadContainerType = nas::EPayloadContainerType::N1_SM_INFORMATION;
nas::EncodeNasMessage(smStatus, ulTransport.payloadContainer.data);
ulTransport.pduSessionId = nas::IEPduSessionIdentity2{};
ulTransport.pduSessionId->value = psi;
m_mm->deliverUlTransport(ulTransport);
}
} // namespace nr::ue
\ No newline at end of file
......@@ -221,6 +221,33 @@ std::string utils::VectorToHexString(const std::vector<uint8_t> &hex)
return str;
}
bool utils::TryParseInt(const std::string &str, int &output)
{
return TryParseInt(str.c_str(), output);
}
bool utils::TryParseInt(const char *str, int &output)
{
int base = 10;
if (strlen(str) > 2)
{
if (str[0] == '0' && str[1] == 'x')
base = 16;
else if (str[0] == '0' && str[1] == 'b')
base = 2;
}
try
{
output = std::stoi(str, nullptr, base);
return true;
}
catch (...)
{
return false;
}
}
int utils::ParseInt(const std::string &str)
{
return ParseInt(str.c_str());
......@@ -228,11 +255,9 @@ int utils::ParseInt(const std::string &str)
int utils::ParseInt(const char *str)
{
std::stringstream ss("");
ss << str;
int i;
ss >> i;
return i;
int n = 0;
TryParseInt(str, n);
return n;
}
uint64_t utils::Random64()
......@@ -306,6 +331,8 @@ bool utils::IsNumeric(const std::string &str)
void utils::Trim(std::string &s)
{
if (s.length() == 0)
return;
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { return !std::isspace(ch); }));
s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { return !std::isspace(ch); }).base(), s.end());
}
......
......@@ -31,6 +31,8 @@ TimeStamp CurrentTimeStamp();
int NextId();
int ParseInt(const std::string &str);
int ParseInt(const char *str);
bool TryParseInt(const std::string &str, int &output);
bool TryParseInt(const char *str, int &output);
uint64_t Random64();
void Sleep(int ms);
bool IsRoot();
......
......@@ -15,10 +15,10 @@ struct cons
// Version information
static constexpr const uint8_t Major = 3;
static constexpr const uint8_t Minor = 1;
static constexpr const uint8_t Patch = 5;
static constexpr const uint8_t Patch = 6;
static constexpr const char *Project = "UERANSIM";
static constexpr const char *Tag = "v3.1.5";
static constexpr const char *Name = "UERANSIM v3.1.5";
static constexpr const char *Tag = "v3.1.6";
static constexpr const char *Name = "UERANSIM v3.1.6";
static constexpr const char *Owner = "ALİ GÜNGÖR";
// Some port values
......
......@@ -222,9 +222,14 @@ opt::OptionsResult::OptionsResult(int argc, char **argv, const opt::OptionsDescr
bool opt::OptionsResult::hasFlag(const opt::OptionItem &item) const
{
if (item.shortName.has_value() && m_options.count(std::string(1, *item.shortName)) > 0)
return hasFlag(item.shortName, item.longName);
}
bool opt::OptionsResult::hasFlag(const std::optional<char> &shortName, const std::optional<std::string> &longName) const
{
if (shortName.has_value() && m_options.count(std::string(1, *shortName)) > 0)
return true;
if (item.longName.has_value() && m_options.count(*item.longName) > 0)
if (longName.has_value() && m_options.count(*longName) > 0)
return true;
return false;
}
......@@ -264,10 +269,21 @@ void opt::OptionsResult::showHelp() const
ostream << std::endl;
ostream << "Usage:" << std::endl;
for (auto &usage : m_description.usages)
ostream << " " << m_description.programName << " " << usage << std::endl;
ostream << std::endl;
if (!m_description.usages.empty())
{
ostream << "Usage:" << std::endl;
for (auto &usage : m_description.usages)
ostream << " " << m_description.programName << " " << usage << std::endl;
ostream << std::endl;
}
if (!m_description.examples.empty())
{
ostream << (m_description.examples.size() > 1 ? "Examples:" : "Example:") << std::endl;
for (auto &example : m_description.examples)
ostream << " " << m_description.programName << " " << example << std::endl;
ostream << std::endl;
}
std::vector<OptionItem> items = m_description.items;
if (!m_description.hideDefaultOptionsInUsage)
......@@ -333,10 +349,16 @@ std::string opt::OptionsResult::getPositional(int index) const
std::string opt::OptionsResult::getOption(const OptionItem &item) const
{
if (item.shortName.has_value() && m_options.count(std::string(1, *item.shortName)) > 0)
return m_options.at(std::string(1, *item.shortName));
if (item.longName.has_value() && m_options.count(*item.longName))
return m_options.at(*item.longName);
return getOption(item.shortName, item.longName);
}
std::string opt::OptionsResult::getOption(const std::optional<char> &shortName,
const std::optional<std::string> &longName) const
{
if (shortName.has_value() && m_options.count(std::string(1, *shortName)) > 0)
return m_options.at(std::string(1, *shortName));
if (longName.has_value() && m_options.count(*longName))
return m_options.at(*longName);
return {};
}
......
......@@ -72,15 +72,16 @@ struct OptionsDescription
std::string programName{};
std::vector<OptionItem> items{};
std::vector<std::string> usages{};
std::vector<std::string> examples{};
bool helpIfEmpty{};
bool hideDefaultOptionsInUsage{};
OptionsDescription(std::string projectName, std::string version, std::string appDescription, std::string copyright,
std::string programName, std::vector<std::string> usages, bool helpIfEmpty,
bool hideDefaultOptionsInUsage)
std::string programName, std::vector<std::string> usages, std::vector<std::string> examples,
bool helpIfEmpty, bool hideDefaultOptionsInUsage)
: projectName(std::move(projectName)), version(std::move(version)), appDescription(std::move(appDescription)),
copyright(std::move(copyright)), programName(std::move(programName)), usages(std::move(usages)),
helpIfEmpty(helpIfEmpty), hideDefaultOptionsInUsage(hideDefaultOptionsInUsage)
examples(std::move(examples)), helpIfEmpty(helpIfEmpty), hideDefaultOptionsInUsage(hideDefaultOptionsInUsage)
{
}
};
......@@ -106,10 +107,12 @@ class OptionsResult
public:
bool hasFlag(const OptionItem &item) const;
bool hasFlag(const std::optional<char> &shortName, const std::optional<std::string>& longName) const;
int positionalCount() const;
int count() const;
std::string getPositional(int index) const;
std::string getOption(const OptionItem &item) const;
std::string getOption(const std::optional<char> &shortName, const std::optional<std::string>& longName) const;
public:
void showHelp() const;
......
//
// 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 "ordered_map.hpp"
//
// 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 <initializer_list>
#include <unordered_map>
#include <utility>
#include <vector>
template <typename TKey, typename TValue>
class OrderedMap
{
private:
std::unordered_map<TKey, TValue> m_map{};
std::vector<TKey> m_keyOrder{};
public:
OrderedMap() = default;
OrderedMap(std::initializer_list<std::pair<TKey, TValue>> initList)
{
for (auto &pair : initList)
{
if (!m_map.count(pair.first))
m_keyOrder.push_back(pair.first);
m_map.insert(pair);
}
}
inline auto count(const TKey &key) const
{
return m_map.count(key);
}
inline TValue &operator[](const TKey &key)
{
return m_map.at(key);
}
inline const TValue &operator[](const TKey &key) const
{
return m_map.at(key);
}
inline typename std::vector<TKey>::iterator begin()
{
return m_keyOrder.begin();
}
inline typename std::vector<TKey>::const_iterator begin() const
{
return m_keyOrder.begin();
}
inline typename std::vector<TKey>::iterator end()
{
return m_keyOrder.end();
}
inline typename std::vector<TKey>::const_iterator end() const
{
return m_keyOrder.end();
}
};
\ No newline at end of file
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