init-version-3.27

This commit is contained in:
lai
2025-08-12 14:15:27 +08:00
parent 85f22d2098
commit a2d7a353d8
4264 changed files with 625612 additions and 1 deletions

324
src/gnb/ngap/context.cpp Normal file
View File

@@ -0,0 +1,324 @@
//
// This file is a part of UERANSIM project.
// Copyright (c) 2023 ALİ GÜNGÖR.
//
// https://github.com/aligungr/UERANSIM/
// See README, LICENSE, and CONTRIBUTING files for licensing details.
//
#include "encode.hpp"
#include "task.hpp"
#include "utils.hpp"
#include <gnb/gtp/task.hpp>
#include <gnb/rrc/task.hpp>
#include <asn/ngap/ASN_NGAP_AMF-UE-NGAP-ID.h>
#include <asn/ngap/ASN_NGAP_AssociatedQosFlowItem.h>
#include <asn/ngap/ASN_NGAP_AssociatedQosFlowList.h>
#include <asn/ngap/ASN_NGAP_GTPTunnel.h>
#include <asn/ngap/ASN_NGAP_InitialContextSetupRequest.h>
#include <asn/ngap/ASN_NGAP_InitialContextSetupResponse.h>
#include <asn/ngap/ASN_NGAP_NGAP-PDU.h>
#include <asn/ngap/ASN_NGAP_PDUSessionResourceFailedToSetupItemCxtRes.h>
#include <asn/ngap/ASN_NGAP_PDUSessionResourceFailedToSetupItemSURes.h>
#include <asn/ngap/ASN_NGAP_PDUSessionResourceItemCxtRelReq.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_PDUSessionResourceSetupItemCxtReq.h>
#include <asn/ngap/ASN_NGAP_PDUSessionResourceSetupItemCxtRes.h>
#include <asn/ngap/ASN_NGAP_PDUSessionResourceSetupItemSUReq.h>
#include <asn/ngap/ASN_NGAP_PDUSessionResourceSetupItemSURes.h>
#include <asn/ngap/ASN_NGAP_PDUSessionResourceSetupListCxtReq.h>
#include <asn/ngap/ASN_NGAP_PDUSessionResourceSetupRequest.h>
#include <asn/ngap/ASN_NGAP_PDUSessionResourceSetupRequestTransfer.h>
#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 <asn/ngap/ASN_NGAP_SuccessfulOutcome.h>
#include <asn/ngap/ASN_NGAP_UE-NGAP-ID-pair.h>
#include <asn/ngap/ASN_NGAP_UE-NGAP-IDs.h>
#include <asn/ngap/ASN_NGAP_UEAggregateMaximumBitRate.h>
#include <asn/ngap/ASN_NGAP_UEContextModificationRequest.h>
#include <asn/ngap/ASN_NGAP_UEContextModificationResponse.h>
#include <asn/ngap/ASN_NGAP_UEContextReleaseCommand.h>
#include <asn/ngap/ASN_NGAP_UEContextReleaseComplete.h>
#include <asn/ngap/ASN_NGAP_UEContextReleaseRequest.h>
namespace nr::gnb
{
void NgapTask::receiveInitialContextSetup(int amfId, ASN_NGAP_InitialContextSetupRequest *msg)
{
m_logger->debug("Initial Context Setup Request received");
auto *ue = findUeByNgapIdPair(amfId, ngap_utils::FindNgapIdPair(msg));
if (ue == nullptr)
return;
auto w = std::make_unique<NmGnbNgapToGtp>(NmGnbNgapToGtp::UE_CONTEXT_UPDATE);
w->update = std::make_unique<GtpUeContextUpdate>(true, ue->ctxId, ue->ueAmbr);
m_base->gtpTask->push(std::move(w));
auto *reqIe = asn::ngap::GetProtocolIe(msg, ASN_NGAP_ProtocolIE_ID_id_UEAggregateMaximumBitRate);
if (reqIe)
{
ue->ueAmbr.dlAmbr = asn::GetUnsigned64(reqIe->UEAggregateMaximumBitRate.uEAggregateMaximumBitRateDL) / 8ull;
ue->ueAmbr.ulAmbr = asn::GetUnsigned64(reqIe->UEAggregateMaximumBitRate.uEAggregateMaximumBitRateUL) / 8ull;
}
std::vector<ASN_NGAP_PDUSessionResourceSetupItemCxtRes *> successList;
std::vector<ASN_NGAP_PDUSessionResourceFailedToSetupItemCxtRes *> failedList;
reqIe = asn::ngap::GetProtocolIe(msg, ASN_NGAP_ProtocolIE_ID_id_PDUSessionResourceSetupListCxtReq);
if (reqIe)
{
auto &list = reqIe->PDUSessionResourceSetupListCxtReq.list;
for (int i = 0; i < list.count; i++)
{
auto &item = list.array[i];
auto *transfer = ngap_encode::Decode<ASN_NGAP_PDUSessionResourceSetupRequestTransfer>(
asn_DEF_ASN_NGAP_PDUSessionResourceSetupRequestTransfer, item->pDUSessionResourceSetupRequestTransfer);
if (transfer == nullptr)
{
m_logger->err(
"Unable to decode a PDU session resource setup request transfer. Ignoring the relevant item");
asn::Free(asn_DEF_ASN_NGAP_PDUSessionResourceSetupRequestTransfer, transfer);
continue;
}
auto *resource = new PduSessionResource(ue->ctxId, static_cast<int>(item->pDUSessionID));
auto *ie = asn::ngap::GetProtocolIe(transfer, ASN_NGAP_ProtocolIE_ID_id_PDUSessionAggregateMaximumBitRate);
if (ie)
{
resource->sessionAmbr.dlAmbr =
asn::GetUnsigned64(ie->PDUSessionAggregateMaximumBitRate.pDUSessionAggregateMaximumBitRateDL) /
8ull;
resource->sessionAmbr.ulAmbr =
asn::GetUnsigned64(ie->PDUSessionAggregateMaximumBitRate.pDUSessionAggregateMaximumBitRateUL) /
8ull;
}
ie = asn::ngap::GetProtocolIe(transfer, ASN_NGAP_ProtocolIE_ID_id_DataForwardingNotPossible);
if (ie)
resource->dataForwardingNotPossible = true;
ie = asn::ngap::GetProtocolIe(transfer, ASN_NGAP_ProtocolIE_ID_id_PDUSessionType);
if (ie)
resource->sessionType = ngap_utils::PduSessionTypeFromAsn(ie->PDUSessionType);
ie = asn::ngap::GetProtocolIe(transfer, ASN_NGAP_ProtocolIE_ID_id_UL_NGU_UP_TNLInformation);
if (ie)
{
resource->upTunnel.teid =
(uint32_t)asn::GetOctet4(ie->UPTransportLayerInformation.choice.gTPTunnel->gTP_TEID);
resource->upTunnel.address =
asn::GetOctetString(ie->UPTransportLayerInformation.choice.gTPTunnel->transportLayerAddress);
}
ie = asn::ngap::GetProtocolIe(transfer, ASN_NGAP_ProtocolIE_ID_id_QosFlowSetupRequestList);
if (ie)
{
auto *ptr = asn::New<ASN_NGAP_QosFlowSetupRequestList>();
asn::DeepCopy(asn_DEF_ASN_NGAP_QosFlowSetupRequestList, ie->QosFlowSetupRequestList, ptr);
resource->qosFlows = asn::WrapUnique(ptr, asn_DEF_ASN_NGAP_QosFlowSetupRequestList);
}
auto error = setupPduSessionResource(ue, resource);
if (error.has_value())
{
auto *tr = asn::New<ASN_NGAP_PDUSessionResourceSetupUnsuccessfulTransfer>();
ngap_utils::ToCauseAsn_Ref(error.value(), tr->cause);
OctetString encodedTr =
ngap_encode::EncodeS(asn_DEF_ASN_NGAP_PDUSessionResourceSetupUnsuccessfulTransfer, tr);
if (encodedTr.length() == 0)
throw std::runtime_error("PDUSessionResourceSetupUnsuccessfulTransfer encoding failed");
asn::Free(asn_DEF_ASN_NGAP_PDUSessionResourceSetupUnsuccessfulTransfer, tr);
auto *res = asn::New<ASN_NGAP_PDUSessionResourceFailedToSetupItemCxtRes>();
res->pDUSessionID = resource->psi;
asn::SetOctetString(res->pDUSessionResourceSetupUnsuccessfulTransfer, encodedTr);
failedList.push_back(res);
}
else
{
auto *tr = asn::New<ASN_NGAP_PDUSessionResourceSetupResponseTransfer>();
auto &qosList = resource->qosFlows->list;
for (int iQos = 0; iQos < qosList.count; iQos++)
{
auto *associatedQosFlowItem = asn::New<ASN_NGAP_AssociatedQosFlowItem>();
associatedQosFlowItem->qosFlowIdentifier = qosList.array[iQos]->qosFlowIdentifier;
asn::SequenceAdd(tr->dLQosFlowPerTNLInformation.associatedQosFlowList, associatedQosFlowItem);
}
auto &upInfo = tr->dLQosFlowPerTNLInformation.uPTransportLayerInformation;
upInfo.present = ASN_NGAP_UPTransportLayerInformation_PR_gTPTunnel;
upInfo.choice.gTPTunnel = asn::New<ASN_NGAP_GTPTunnel>();
asn::SetBitString(upInfo.choice.gTPTunnel->transportLayerAddress, resource->downTunnel.address);
asn::SetOctetString4(upInfo.choice.gTPTunnel->gTP_TEID, (octet4)resource->downTunnel.teid);
OctetString encodedTr =
ngap_encode::EncodeS(asn_DEF_ASN_NGAP_PDUSessionResourceSetupResponseTransfer, tr);
if (encodedTr.length() == 0)
throw std::runtime_error("PDUSessionResourceSetupResponseTransfer encoding failed");
asn::Free(asn_DEF_ASN_NGAP_PDUSessionResourceSetupResponseTransfer, tr);
auto *res = asn::New<ASN_NGAP_PDUSessionResourceSetupItemCxtRes>();
res->pDUSessionID = resource->psi;
asn::SetOctetString(res->pDUSessionResourceSetupResponseTransfer, encodedTr);
successList.push_back(res);
}
asn::Free(asn_DEF_ASN_NGAP_PDUSessionResourceSetupRequestTransfer, transfer);
}
}
reqIe = asn::ngap::GetProtocolIe(msg, ASN_NGAP_ProtocolIE_ID_id_NAS_PDU);
if (reqIe)
deliverDownlinkNas(ue->ctxId, asn::GetOctetString(reqIe->NAS_PDU));
std::vector<ASN_NGAP_InitialContextSetupResponseIEs *> responseIes;
if (!successList.empty())
{
auto *ie = asn::New<ASN_NGAP_InitialContextSetupResponseIEs>();
ie->id = ASN_NGAP_ProtocolIE_ID_id_PDUSessionResourceSetupListCxtRes;
ie->criticality = ASN_NGAP_Criticality_ignore;
ie->value.present = ASN_NGAP_InitialContextSetupResponseIEs__value_PR_PDUSessionResourceSetupListCxtRes;
for (auto &item : successList)
asn::SequenceAdd(ie->value.choice.PDUSessionResourceSetupListCxtRes, item);
responseIes.push_back(ie);
}
if (!failedList.empty())
{
auto *ie = asn::New<ASN_NGAP_InitialContextSetupResponseIEs>();
ie->id = ASN_NGAP_ProtocolIE_ID_id_PDUSessionResourceFailedToSetupListCxtRes;
ie->criticality = ASN_NGAP_Criticality_ignore;
ie->value.present = ASN_NGAP_InitialContextSetupResponseIEs__value_PR_PDUSessionResourceFailedToSetupListCxtRes;
for (auto &item : failedList)
asn::SequenceAdd(ie->value.choice.PDUSessionResourceFailedToSetupListCxtRes, item);
responseIes.push_back(ie);
}
auto *response = asn::ngap::NewMessagePdu<ASN_NGAP_InitialContextSetupResponse>(responseIes);
sendNgapUeAssociated(ue->ctxId, response);
}
void NgapTask::receiveContextRelease(int amfId, ASN_NGAP_UEContextReleaseCommand *msg)
{
m_logger->debug("UE Context Release Command received");
auto *ue = findUeByNgapIdPair(amfId, ngap_utils::FindNgapIdPairFromUeNgapIds(msg));
if (ue == nullptr)
return;
// Notify RRC task
auto w1 = std::make_unique<NmGnbNgapToRrc>(NmGnbNgapToRrc::AN_RELEASE);
w1->ueId = ue->ctxId;
m_base->rrcTask->push(std::move(w1));
// Notify GTP task
auto w2 = std::make_unique<NmGnbNgapToGtp>(NmGnbNgapToGtp::UE_CONTEXT_RELEASE);
w2->ueId = ue->ctxId;
m_base->gtpTask->push(std::move(w2));
auto *response = asn::ngap::NewMessagePdu<ASN_NGAP_UEContextReleaseComplete>({});
sendNgapUeAssociated(ue->ctxId, response);
deleteUeContext(ue->ctxId);
}
void NgapTask::receiveContextModification(int amfId, ASN_NGAP_UEContextModificationRequest *msg)
{
m_logger->debug("UE Context Modification Request received");
auto *ue = findUeByNgapIdPair(amfId, ngap_utils::FindNgapIdPair(msg));
if (ue == nullptr)
return;
auto *ie = asn::ngap::GetProtocolIe(msg, ASN_NGAP_ProtocolIE_ID_id_UEAggregateMaximumBitRate);
if (ie)
{
ue->ueAmbr.dlAmbr = asn::GetUnsigned64(ie->UEAggregateMaximumBitRate.uEAggregateMaximumBitRateDL);
ue->ueAmbr.ulAmbr = asn::GetUnsigned64(ie->UEAggregateMaximumBitRate.uEAggregateMaximumBitRateUL);
}
ie = asn::ngap::GetProtocolIe(msg, ASN_NGAP_ProtocolIE_ID_id_NewAMF_UE_NGAP_ID);
if (ie)
{
int64_t old = ue->amfUeNgapId;
ue->amfUeNgapId = asn::GetSigned64(ie->AMF_UE_NGAP_ID_1);
m_logger->debug("AMF-UE-NGAP-ID changed from %ld to %ld", old, ue->amfUeNgapId);
}
auto *response = asn::ngap::NewMessagePdu<ASN_NGAP_UEContextModificationResponse>({});
sendNgapUeAssociated(ue->ctxId, response);
auto w = std::make_unique<NmGnbNgapToGtp>(NmGnbNgapToGtp::UE_CONTEXT_UPDATE);
w->update = std::make_unique<GtpUeContextUpdate>(false, ue->ctxId, ue->ueAmbr);
m_base->gtpTask->push(std::move(w));
}
void NgapTask::sendContextRelease(int ueId, NgapCause cause)
{
m_logger->debug("Sending UE Context release request (NG-RAN node initiated)");
auto *ue = findUeContext(ueId);
if (ue == nullptr)
return;
std::vector<ASN_NGAP_UEContextReleaseRequest_IEs *> ies;
if (!ue->pduSessions.empty())
{
auto *ieSessionList = asn::New<ASN_NGAP_UEContextReleaseRequest_IEs>();
ieSessionList->id = ASN_NGAP_ProtocolIE_ID_id_PDUSessionResourceListCxtRelReq;
ieSessionList->criticality = ASN_NGAP_Criticality_reject;
ieSessionList->value.present = ASN_NGAP_UEContextReleaseRequest_IEs__value_PR_PDUSessionResourceListCxtRelReq;
for (int psi : ue->pduSessions)
{
auto *sessionItem = asn::New<ASN_NGAP_PDUSessionResourceItemCxtRelReq>();
sessionItem->pDUSessionID = static_cast<ASN_NGAP_PDUSessionID_t>(psi);
asn::SequenceAdd(ieSessionList->value.choice.PDUSessionResourceListCxtRelReq, sessionItem);
}
ies.push_back(ieSessionList);
}
auto *ieCause = asn::New<ASN_NGAP_UEContextReleaseRequest_IEs>();
ieCause->id = ASN_NGAP_ProtocolIE_ID_id_Cause;
ieCause->criticality = ASN_NGAP_Criticality_ignore;
ieCause->value.present = ASN_NGAP_UEContextReleaseRequest_IEs__value_PR_Cause;
ngap_utils::ToCauseAsn_Ref(cause, ieCause->value.choice.Cause);
ies.push_back(ieCause);
auto *pdu = asn::ngap::NewMessagePdu<ASN_NGAP_UEContextReleaseRequest>(ies);
sendNgapUeAssociated(ueId, pdu);
}
} // namespace nr::gnb

14
src/gnb/ngap/encode.cpp Normal file
View File

@@ -0,0 +1,14 @@
//
// This file is a part of UERANSIM project.
// Copyright (c) 2023 ALİ GÜNGÖR.
//
// https://github.com/aligungr/UERANSIM/
// See README, LICENSE, and CONTRIBUTING files for licensing details.
//
#include "encode.hpp"
namespace nr::gnb::ngap_encode
{
}

96
src/gnb/ngap/encode.hpp vendored Normal file
View File

@@ -0,0 +1,96 @@
//
// This file is a part of UERANSIM project.
// Copyright (c) 2023 ALİ GÜNGÖR.
//
// https://github.com/aligungr/UERANSIM/
// See README, LICENSE, and CONTRIBUTING files for licensing details.
//
#pragma once
#include <asn_application.h>
#include <lib/asn/utils.hpp>
#include <utils/octet_string.hpp>
namespace nr::gnb::ngap_encode
{
template <typename T>
inline std::string EncodeXer(const asn_TYPE_descriptor_t &desc, T *pdu)
{
auto res = asn_encode_to_new_buffer(nullptr, ATS_CANONICAL_XER, &desc, pdu);
if (res.buffer == nullptr || res.result.encoded < 0)
return {};
std::string s{reinterpret_cast<char *>(res.buffer), reinterpret_cast<char *>(res.buffer) + res.result.encoded};
free(res.buffer);
return s;
}
template <typename T>
inline bool Encode(const asn_TYPE_descriptor_t &desc, T *pdu, ssize_t &encoded, uint8_t *&buffer)
{
auto res = asn_encode_to_new_buffer(nullptr, ATS_ALIGNED_CANONICAL_PER, &desc, pdu);
if (res.buffer == nullptr || res.result.encoded < 0)
return false;
encoded = res.result.encoded;
buffer = new uint8_t[encoded];
std::memcpy(buffer, res.buffer, encoded);
free(res.buffer);
return true;
}
template <typename T>
inline OctetString EncodeS(const asn_TYPE_descriptor_t &desc, T *pdu)
{
uint8_t *buffer;
ssize_t encoded;
if (Encode(desc, pdu, encoded, buffer))
{
std::vector<uint8_t> v(encoded);
memcpy(v.data(), buffer, encoded);
delete[] buffer;
return OctetString{std::move(v)};
}
return OctetString{};
}
template <typename T>
inline T *Decode(asn_TYPE_descriptor_t &desc, const uint8_t *buffer, size_t size)
{
auto *pdu = asn::New<T>();
auto res = aper_decode(nullptr, &desc, reinterpret_cast<void **>(&pdu), buffer, size, 0, 0);
if (res.code != RC_OK)
{
asn::Free(desc, pdu);
return nullptr;
}
return pdu;
}
template <typename T>
inline bool DecodeInPlace(asn_TYPE_descriptor_t &desc, uint8_t *buffer, size_t size, T **outPdu)
{
auto res = aper_decode(nullptr, &desc, reinterpret_cast<void **>(outPdu), buffer, size, 0, 0);
return res.code == RC_OK;
}
template <typename T>
inline bool DecodeInPlace(asn_TYPE_descriptor_t &desc, const OCTET_STRING_t &octetString, T **outPdu)
{
return DecodeInPlace(desc, octetString.buf, octetString.size, outPdu);
}
template <typename T>
inline T *Decode(asn_TYPE_descriptor_t &desc, const OCTET_STRING_t &octetString)
{
return Decode<T>(desc, octetString.buf, octetString.size);
}
} // namespace nr::gnb::ngap_encode

362
src/gnb/ngap/interface.cpp Normal file
View File

@@ -0,0 +1,362 @@
//
// This file is a part of UERANSIM project.
// Copyright (c) 2023 ALİ GÜNGÖR.
//
// https://github.com/aligungr/UERANSIM/
// See README, LICENSE, and CONTRIBUTING files for licensing details.
//
#include "task.hpp"
#include "utils.hpp"
#include <algorithm>
#include <gnb/app/task.hpp>
#include <gnb/rrc/task.hpp>
#include <gnb/sctp/task.hpp>
#include <asn/ngap/ASN_NGAP_AMFConfigurationUpdate.h>
#include <asn/ngap/ASN_NGAP_AMFConfigurationUpdateFailure.h>
#include <asn/ngap/ASN_NGAP_AMFName.h>
#include <asn/ngap/ASN_NGAP_BroadcastPLMNItem.h>
#include <asn/ngap/ASN_NGAP_ErrorIndication.h>
#include <asn/ngap/ASN_NGAP_GlobalGNB-ID.h>
#include <asn/ngap/ASN_NGAP_InitiatingMessage.h>
#include <asn/ngap/ASN_NGAP_NGAP-PDU.h>
#include <asn/ngap/ASN_NGAP_NGSetupRequest.h>
#include <asn/ngap/ASN_NGAP_OverloadStartNSSAIItem.h>
#include <asn/ngap/ASN_NGAP_PLMNSupportItem.h>
#include <asn/ngap/ASN_NGAP_ProtocolIE-Field.h>
#include <asn/ngap/ASN_NGAP_ServedGUAMIItem.h>
#include <asn/ngap/ASN_NGAP_SliceSupportItem.h>
#include <asn/ngap/ASN_NGAP_SupportedTAItem.h>
namespace nr::gnb
{
template <typename T>
static void AssignDefaultAmfConfigs(NgapAmfContext *amf, T *msg)
{
auto *ie = asn::ngap::GetProtocolIe(msg, ASN_NGAP_ProtocolIE_ID_id_AMFName);
if (ie)
amf->amfName = asn::GetPrintableString(ie->AMFName);
ie = asn::ngap::GetProtocolIe(msg, ASN_NGAP_ProtocolIE_ID_id_RelativeAMFCapacity);
if (ie)
amf->relativeCapacity = ie->RelativeAMFCapacity;
ie = asn::ngap::GetProtocolIe(msg, ASN_NGAP_ProtocolIE_ID_id_ServedGUAMIList);
if (ie)
{
utils::ClearAndDelete(amf->servedGuamiList);
asn::ForeachItem(ie->ServedGUAMIList, [amf](ASN_NGAP_ServedGUAMIItem &item) {
auto servedGuami = new ServedGuami();
if (item.backupAMFName)
servedGuami->backupAmfName = asn::GetPrintableString(*item.backupAMFName);
ngap_utils::GuamiFromAsn_Ref(item.gUAMI, servedGuami->guami);
amf->servedGuamiList.push_back(servedGuami);
});
}
ie = asn::ngap::GetProtocolIe(msg, ASN_NGAP_ProtocolIE_ID_id_PLMNSupportList);
if (ie)
{
utils::ClearAndDelete(amf->plmnSupportList);
asn::ForeachItem(ie->PLMNSupportList, [amf](ASN_NGAP_PLMNSupportItem &item) {
auto plmnSupport = new PlmnSupport();
ngap_utils::PlmnFromAsn_Ref(item.pLMNIdentity, plmnSupport->plmn);
asn::ForeachItem(item.sliceSupportList, [plmnSupport](ASN_NGAP_SliceSupportItem &ssItem) {
plmnSupport->sliceSupportList.slices.push_back(ngap_utils::SliceSupportFromAsn(ssItem));
});
amf->plmnSupportList.push_back(plmnSupport);
});
}
}
void NgapTask::handleAssociationSetup(int amfId, int ascId, int inCount, int outCount)
{
auto *amf = findAmfContext(amfId);
if (amf != nullptr)
{
amf->association.associationId = amfId;
amf->association.inStreams = inCount;
amf->association.outStreams = outCount;
sendNgSetupRequest(amf->ctxId);
}
}
void NgapTask::handleAssociationShutdown(int amfId)
{
auto *amf = findAmfContext(amfId);
if (amf == nullptr)
return;
m_logger->err("Association terminated for AMF[%d]", amfId);
m_logger->debug("Removing AMF context[%d]", amfId);
amf->state = EAmfState::NOT_CONNECTED;
auto w = std::make_unique<NmGnbSctp>(NmGnbSctp::CONNECTION_CLOSE);
w->clientId = amfId;
m_base->sctpTask->push(std::move(w));
deleteAmfContext(amfId);
}
void NgapTask::sendNgSetupRequest(int amfId)
{
m_logger->debug("Sending NG Setup Request");
auto *amf = findAmfContext(amfId);
if (amf == nullptr)
return;
amf->state = EAmfState::WAITING_NG_SETUP;
// TODO: this procedure also re-initialises the NGAP UE-related contexts (if any)
// and erases all related signalling connections in the two nodes like an NG Reset procedure would do.
// More on 38.413 8.7.1.1
auto *globalGnbId = asn::New<ASN_NGAP_GlobalGNB_ID>();
globalGnbId->gNB_ID.present = ASN_NGAP_GNB_ID_PR_gNB_ID;
auto gnbIdLength = m_base->config->gnbIdLength;
auto bitsToShift = 32 - gnbIdLength;
asn::SetBitString(globalGnbId->gNB_ID.choice.gNB_ID, octet4{m_base->config->getGnbId() << bitsToShift},
static_cast<size_t>(gnbIdLength));
asn::SetOctetString3(globalGnbId->pLMNIdentity, ngap_utils::PlmnToOctet3(m_base->config->plmn));
auto *ieGlobalGnbId = asn::New<ASN_NGAP_NGSetupRequestIEs>();
ieGlobalGnbId->id = ASN_NGAP_ProtocolIE_ID_id_GlobalRANNodeID;
ieGlobalGnbId->criticality = ASN_NGAP_Criticality_reject;
ieGlobalGnbId->value.present = ASN_NGAP_NGSetupRequestIEs__value_PR_GlobalRANNodeID;
ieGlobalGnbId->value.choice.GlobalRANNodeID.present = ASN_NGAP_GlobalRANNodeID_PR_globalGNB_ID;
ieGlobalGnbId->value.choice.GlobalRANNodeID.choice.globalGNB_ID = globalGnbId;
auto *ieRanNodeName = asn::New<ASN_NGAP_NGSetupRequestIEs>();
ieRanNodeName->id = ASN_NGAP_ProtocolIE_ID_id_RANNodeName;
ieRanNodeName->criticality = ASN_NGAP_Criticality_ignore;
ieRanNodeName->value.present = ASN_NGAP_NGSetupRequestIEs__value_PR_RANNodeName;
asn::SetPrintableString(ieRanNodeName->value.choice.RANNodeName, m_base->config->name);
auto *broadcastPlmn = asn::New<ASN_NGAP_BroadcastPLMNItem>();
asn::SetOctetString3(broadcastPlmn->pLMNIdentity, ngap_utils::PlmnToOctet3(m_base->config->plmn));
for (auto &nssai : m_base->config->nssai.slices)
{
auto *item = asn::New<ASN_NGAP_SliceSupportItem>();
asn::SetOctetString1(item->s_NSSAI.sST, static_cast<uint8_t>(nssai.sst));
if (nssai.sd.has_value())
{
item->s_NSSAI.sD = asn::New<ASN_NGAP_SD_t>();
asn::SetOctetString3(*item->s_NSSAI.sD, octet3{nssai.sd.value()});
}
asn::SequenceAdd(broadcastPlmn->tAISliceSupportList, item);
}
auto *supportedTa = asn::New<ASN_NGAP_SupportedTAItem>();
asn::SetOctetString3(supportedTa->tAC, octet3{m_base->config->tac});
asn::SequenceAdd(supportedTa->broadcastPLMNList, broadcastPlmn);
auto *ieSupportedTaList = asn::New<ASN_NGAP_NGSetupRequestIEs>();
ieSupportedTaList->id = ASN_NGAP_ProtocolIE_ID_id_SupportedTAList;
ieSupportedTaList->criticality = ASN_NGAP_Criticality_reject;
ieSupportedTaList->value.present = ASN_NGAP_NGSetupRequestIEs__value_PR_SupportedTAList;
asn::SequenceAdd(ieSupportedTaList->value.choice.SupportedTAList, supportedTa);
auto *iePagingDrx = asn::New<ASN_NGAP_NGSetupRequestIEs>();
iePagingDrx->id = ASN_NGAP_ProtocolIE_ID_id_DefaultPagingDRX;
iePagingDrx->criticality = ASN_NGAP_Criticality_ignore;
iePagingDrx->value.present = ASN_NGAP_NGSetupRequestIEs__value_PR_PagingDRX;
iePagingDrx->value.choice.PagingDRX = ngap_utils::PagingDrxToAsn(m_base->config->pagingDrx);
auto *pdu = asn::ngap::NewMessagePdu<ASN_NGAP_NGSetupRequest>(
{ieGlobalGnbId, ieRanNodeName, ieSupportedTaList, iePagingDrx});
sendNgapNonUe(amfId, pdu);
}
void NgapTask::receiveNgSetupResponse(int amfId, ASN_NGAP_NGSetupResponse *msg)
{
m_logger->debug("NG Setup Response received");
auto *amf = findAmfContext(amfId);
if (amf == nullptr)
return;
AssignDefaultAmfConfigs(amf, msg);
amf->state = EAmfState::CONNECTED;
m_logger->info("NG Setup procedure is successful");
if (!m_isInitialized && std::all_of(m_amfCtx.begin(), m_amfCtx.end(),
[](auto &amfCtx) { return amfCtx.second->state == EAmfState::CONNECTED; }))
{
m_isInitialized = true;
auto update = std::make_unique<NmGnbStatusUpdate>(NmGnbStatusUpdate::NGAP_IS_UP);
update->isNgapUp = true;
m_base->appTask->push(std::move(update));
m_base->rrcTask->push(std::make_unique<NmGnbNgapToRrc>(NmGnbNgapToRrc::RADIO_POWER_ON));
}
}
void NgapTask::receiveNgSetupFailure(int amfId, ASN_NGAP_NGSetupFailure *msg)
{
auto *amf = findAmfContext(amfId);
if (amf == nullptr)
return;
amf->state = EAmfState::WAITING_NG_SETUP;
auto *ie = asn::ngap::GetProtocolIe(msg, ASN_NGAP_ProtocolIE_ID_id_Cause);
if (ie)
m_logger->err("NG Setup procedure is failed. Cause: %s", ngap_utils::CauseToString(ie->Cause).c_str());
else
m_logger->err("NG Setup procedure is failed.");
}
void NgapTask::receiveErrorIndication(int amfId, ASN_NGAP_ErrorIndication *msg)
{
auto *amf = findAmfContext(amfId);
if (amf == nullptr)
{
m_logger->err("Error indication received with not found AMF context");
return;
}
auto *ie = asn::ngap::GetProtocolIe(msg, ASN_NGAP_ProtocolIE_ID_id_Cause);
if (ie)
m_logger->err("Error indication received. Cause: %s", ngap_utils::CauseToString(ie->Cause).c_str());
else
m_logger->err("Error indication received.");
}
void NgapTask::sendErrorIndication(int amfId, NgapCause cause, int ueId)
{
auto ieCause = asn::New<ASN_NGAP_ErrorIndicationIEs>();
ieCause->id = ASN_NGAP_ProtocolIE_ID_id_Cause;
ieCause->criticality = ASN_NGAP_Criticality_ignore;
ieCause->value.present = ASN_NGAP_ErrorIndicationIEs__value_PR_Cause;
ngap_utils::ToCauseAsn_Ref(cause, ieCause->value.choice.Cause);
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});
if (ueId > 0)
sendNgapUeAssociated(ueId, pdu);
else
sendNgapNonUe(amfId, pdu);
}
void NgapTask::receiveAmfConfigurationUpdate(int amfId, ASN_NGAP_AMFConfigurationUpdate *msg)
{
m_logger->debug("AMF configuration update received");
auto *amf = findAmfContext(amfId);
if (amf == nullptr)
return;
bool tnlModified = false;
auto *ie = asn::ngap::GetProtocolIe(msg, ASN_NGAP_ProtocolIE_ID_id_AMF_TNLAssociationToAddList);
if (ie && ie->AMF_TNLAssociationToAddList.list.count > 0)
tnlModified = true;
ie = asn::ngap::GetProtocolIe(msg, ASN_NGAP_ProtocolIE_ID_id_AMF_TNLAssociationToRemoveList);
if (ie && ie->AMF_TNLAssociationToRemoveList.list.count > 0)
tnlModified = true;
ie = asn::ngap::GetProtocolIe(msg, ASN_NGAP_ProtocolIE_ID_id_AMF_TNLAssociationToUpdateList);
if (ie && ie->AMF_TNLAssociationToUpdateList.list.count > 0)
tnlModified = true;
// TODO: AMF TNL modification is not supported
if (tnlModified)
{
m_logger->err("TNL modification is not supported, rejecting AMF configuration update");
auto *ieCause = asn::New<ASN_NGAP_AMFConfigurationUpdateFailureIEs>();
ieCause->id = ASN_NGAP_ProtocolIE_ID_id_Cause;
ieCause->criticality = ASN_NGAP_Criticality_ignore;
ieCause->value.present = ASN_NGAP_AMFConfigurationUpdateFailureIEs__value_PR_Cause;
ngap_utils::ToCauseAsn_Ref(NgapCause::Transport_unspecified, ieCause->value.choice.Cause);
auto *pdu = asn::ngap::NewMessagePdu<ASN_NGAP_AMFConfigurationUpdateFailure>({ieCause});
sendNgapNonUe(amfId, pdu);
}
else
{
AssignDefaultAmfConfigs(amf, msg);
auto *ieList = asn::New<ASN_NGAP_AMFConfigurationUpdateAcknowledgeIEs>();
ieList->id = ASN_NGAP_ProtocolIE_ID_id_AMF_TNLAssociationSetupList;
ieList->criticality = ASN_NGAP_Criticality_ignore;
ieList->value.present = ASN_NGAP_AMFConfigurationUpdateAcknowledgeIEs__value_PR_AMF_TNLAssociationSetupList;
auto *pdu = asn::ngap::NewMessagePdu<ASN_NGAP_AMFConfigurationUpdateAcknowledge>({ieList});
sendNgapNonUe(amfId, pdu);
}
}
void NgapTask::receiveOverloadStart(int amfId, ASN_NGAP_OverloadStart *msg)
{
m_logger->debug("AMF overload start received");
auto *amf = findAmfContext(amfId);
if (amf == nullptr)
return;
amf->overloadInfo = {};
amf->overloadInfo.status = EOverloadStatus::OVERLOADED;
auto *ie = asn::ngap::GetProtocolIe(msg, ASN_NGAP_ProtocolIE_ID_id_AMFOverloadResponse);
if (ie && ie->OverloadResponse.present == ASN_NGAP_OverloadResponse_PR_overloadAction)
{
switch (ie->OverloadResponse.choice.overloadAction)
{
case ASN_NGAP_OverloadAction_reject_non_emergency_mo_dt:
amf->overloadInfo.indication.action = EOverloadAction::REJECT_NON_EMERGENCY_MO_DATA;
break;
case ASN_NGAP_OverloadAction_reject_rrc_cr_signalling:
amf->overloadInfo.indication.action = EOverloadAction::REJECT_SIGNALLING;
break;
case ASN_NGAP_OverloadAction_permit_emergency_sessions_and_mobile_terminated_services_only:
amf->overloadInfo.indication.action = EOverloadAction::ONLY_EMERGENCY_AND_MT;
break;
case ASN_NGAP_OverloadAction_permit_high_priority_sessions_and_mobile_terminated_services_only:
amf->overloadInfo.indication.action = EOverloadAction::ONLY_HIGH_PRI_AND_MT;
break;
default:
m_logger->warn("AMF overload action [%d] could not understand",
(int)ie->OverloadResponse.choice.overloadAction);
break;
}
}
ie = asn::ngap::GetProtocolIe(msg, ASN_NGAP_ProtocolIE_ID_id_AMFTrafficLoadReductionIndication);
if (ie)
amf->overloadInfo.indication.loadReductionPerc = static_cast<int>(ie->TrafficLoadReductionIndication);
ie = asn::ngap::GetProtocolIe(msg, ASN_NGAP_ProtocolIE_ID_id_OverloadStartNSSAIList);
if (ie)
{
// TODO
/*asn::ForeachItem(ie->OverloadStartNSSAIList, [](auto &item) {
item.sliceOverloadList;
});*/
}
}
void NgapTask::receiveOverloadStop(int amfId, ASN_NGAP_OverloadStop *msg)
{
m_logger->debug("AMF overload stop received");
// TODO
}
} // namespace nr::gnb

157
src/gnb/ngap/management.cpp Normal file
View File

@@ -0,0 +1,157 @@
//
// This file is a part of UERANSIM project.
// Copyright (c) 2023 ALİ GÜNGÖR.
//
// https://github.com/aligungr/UERANSIM/
// See README, LICENSE, and CONTRIBUTING files for licensing details.
//
#include "task.hpp"
#include <utils/common.hpp>
namespace nr::gnb
{
NgapAmfContext *NgapTask::findAmfContext(int ctxId)
{
NgapAmfContext *ctx = nullptr;
if (m_amfCtx.count(ctxId))
ctx = m_amfCtx[ctxId];
if (ctx == nullptr)
m_logger->err("AMF context not found with id: %d", ctxId);
return ctx;
}
void NgapTask::createAmfContext(const GnbAmfConfig &conf)
{
auto *ctx = new NgapAmfContext();
ctx->ctxId = utils::NextId();
ctx->state = EAmfState::NOT_CONNECTED;
ctx->address = conf.address;
ctx->port = conf.port;
m_amfCtx[ctx->ctxId] = ctx;
}
void NgapTask::createUeContext(int ueId, int32_t &requestedSliceType)
{
auto *ctx = new NgapUeContext(ueId);
ctx->amfUeNgapId = -1;
ctx->ranUeNgapId = ++m_ueNgapIdCounter;
m_ueCtx[ctx->ctxId] = ctx;
// Perform AMF selection
auto *amf = selectAmf(ueId, requestedSliceType);
if (amf == nullptr)
m_logger->err("AMF selection for UE[%d] failed. Could not find a suitable AMF.", ueId);
else
ctx->associatedAmfId = amf->ctxId;
}
NgapUeContext *NgapTask::findUeContext(int ctxId)
{
NgapUeContext *ctx = nullptr;
if (m_ueCtx.count(ctxId))
ctx = m_ueCtx[ctxId];
if (ctx == nullptr)
m_logger->err("UE context not found with id: %d", ctxId);
return ctx;
}
NgapUeContext *NgapTask::findUeByRanId(int64_t ranUeNgapId)
{
if (ranUeNgapId <= 0)
return nullptr;
// TODO: optimize
for (auto &ue : m_ueCtx)
if (ue.second->ranUeNgapId == ranUeNgapId)
return ue.second;
return nullptr;
}
NgapUeContext *NgapTask::findUeByAmfId(int64_t amfUeNgapId)
{
if (amfUeNgapId <= 0)
return nullptr;
// TODO: optimize
for (auto &ue : m_ueCtx)
if (ue.second->amfUeNgapId == amfUeNgapId)
return ue.second;
return nullptr;
}
NgapUeContext *NgapTask::findUeByNgapIdPair(int amfCtxId, const NgapIdPair &idPair)
{
auto &amfId = idPair.amfUeNgapId;
auto &ranId = idPair.ranUeNgapId;
if (!amfId.has_value() && !ranId.has_value())
{
sendErrorIndication(amfCtxId, NgapCause::Protocol_abstract_syntax_error_falsely_constructed_message);
return nullptr;
}
if (!amfId.has_value())
{
auto ue = findUeByRanId(ranId.value());
if (ue == nullptr)
{
sendErrorIndication(amfCtxId, NgapCause::RadioNetwork_unknown_local_UE_NGAP_ID);
return nullptr;
}
return ue;
}
if (!ranId.has_value())
{
auto ue = findUeByAmfId(amfId.value());
if (ue == nullptr)
{
sendErrorIndication(amfCtxId, NgapCause::RadioNetwork_inconsistent_remote_UE_NGAP_ID);
return nullptr;
}
return ue;
}
auto ue = findUeByRanId(ranId.value());
if (ue == nullptr)
{
sendErrorIndication(amfCtxId, NgapCause::RadioNetwork_unknown_local_UE_NGAP_ID);
return nullptr;
}
if (ue->amfUeNgapId == -1)
ue->amfUeNgapId = amfId.value();
else if (ue->amfUeNgapId != amfId.value())
{
sendErrorIndication(amfCtxId, NgapCause::RadioNetwork_inconsistent_remote_UE_NGAP_ID);
return nullptr;
}
return ue;
}
void NgapTask::deleteUeContext(int ueId)
{
auto *ue = m_ueCtx[ueId];
if (ue)
{
delete ue;
m_ueCtx.erase(ueId);
}
}
void NgapTask::deleteAmfContext(int amfId)
{
auto *amf = m_amfCtx[amfId];
if (amf)
{
delete amf;
m_amfCtx.erase(amfId);
}
}
} // namespace nr::gnb

248
src/gnb/ngap/nas.cpp Normal file
View File

@@ -0,0 +1,248 @@
//
// This file is a part of UERANSIM project.
// Copyright (c) 2023 ALİ GÜNGÖR.
//
// https://github.com/aligungr/UERANSIM/
// See README, LICENSE, and CONTRIBUTING files for licensing details.
//
#include "encode.hpp"
#include "task.hpp"
#include "utils.hpp"
#include <gnb/rrc/task.hpp>
#include <asn/ngap/ASN_NGAP_DownlinkNASTransport.h>
#include <asn/ngap/ASN_NGAP_InitialUEMessage.h>
#include <asn/ngap/ASN_NGAP_InitiatingMessage.h>
#include <asn/ngap/ASN_NGAP_NASNonDeliveryIndication.h>
#include <asn/ngap/ASN_NGAP_NGAP-PDU.h>
#include <asn/ngap/ASN_NGAP_ProtocolIE-Field.h>
#include <asn/ngap/ASN_NGAP_RerouteNASRequest.h>
#include <asn/ngap/ASN_NGAP_UplinkNASTransport.h>
#include <ue/nas/enc.hpp>
#include "encode.hpp"
#include <stdexcept>
namespace nr::gnb
{
int32_t extractSliceInfoAndModifyPdu(OctetString &nasPdu) {
nas::RegistrationRequest *regRequest = nullptr;
int32_t requestedSliceType = -1;
const uint8_t *m_data = nasPdu.data();
size_t m_dataLength = nasPdu.length();
OctetView octetView(m_data, m_dataLength);
auto nasMessage = nas::DecodeNasMessage(octetView);
if (nasMessage->epd == nas::EExtendedProtocolDiscriminator::MOBILITY_MANAGEMENT_MESSAGES)
{
nas::MmMessage *mmMessage = dynamic_cast<nas::MmMessage *>(nasMessage.get());
if (mmMessage)
{
nas::PlainMmMessage *plainMmMessage = dynamic_cast<nas::PlainMmMessage *>(mmMessage);
if (plainMmMessage)
{
regRequest = dynamic_cast<nas::RegistrationRequest *>(plainMmMessage);
if (regRequest)
{
auto sz = regRequest->requestedNSSAI->sNssais.size();
if (sz > 0) {
requestedSliceType = static_cast<uint8_t>(regRequest->requestedNSSAI->sNssais[0].sst);
}
}
}
}
}
if (regRequest && regRequest->requestedNSSAI)
regRequest->requestedNSSAI = std::nullopt;
OctetString modifiedNasPdu;
nas::EncodeNasMessage(*nasMessage, modifiedNasPdu);
nasPdu = std::move(modifiedNasPdu);
return requestedSliceType;
}
void NgapTask::handleInitialNasTransport(int ueId, OctetString &nasPdu, int64_t rrcEstablishmentCause,
const std::optional<GutiMobileIdentity> &sTmsi)
{
int32_t requestedSliceType = extractSliceInfoAndModifyPdu(nasPdu);
m_logger->debug("Initial NAS message received from UE[%d]", ueId);
if (m_ueCtx.count(ueId))
{
m_logger->err("UE context[%d] already exists", ueId);
return;
}
createUeContext(ueId, requestedSliceType);
auto *ueCtx = findUeContext(ueId);
if (ueCtx == nullptr)
return;
auto *amfCtx = findAmfContext(ueCtx->associatedAmfId);
if (amfCtx == nullptr)
return;
if (amfCtx->state != EAmfState::CONNECTED)
{
m_logger->err("Initial NAS transport failure. AMF is not in connected state.");
return;
}
amfCtx->nextStream = (amfCtx->nextStream + 1) % amfCtx->association.outStreams;
if ((amfCtx->nextStream == 0) && (amfCtx->association.outStreams > 1))
amfCtx->nextStream += 1;
ueCtx->uplinkStream = amfCtx->nextStream;
std::vector<ASN_NGAP_InitialUEMessage_IEs *> ies;
auto *ieEstablishmentCause = asn::New<ASN_NGAP_InitialUEMessage_IEs>();
ieEstablishmentCause->id = ASN_NGAP_ProtocolIE_ID_id_RRCEstablishmentCause;
ieEstablishmentCause->criticality = ASN_NGAP_Criticality_ignore;
ieEstablishmentCause->value.present = ASN_NGAP_InitialUEMessage_IEs__value_PR_RRCEstablishmentCause;
ieEstablishmentCause->value.choice.RRCEstablishmentCause = rrcEstablishmentCause;
ies.push_back(ieEstablishmentCause);
auto *ieCtxRequest = asn::New<ASN_NGAP_InitialUEMessage_IEs>();
ieCtxRequest->id = ASN_NGAP_ProtocolIE_ID_id_UEContextRequest;
ieCtxRequest->criticality = ASN_NGAP_Criticality_ignore;
ieCtxRequest->value.present = ASN_NGAP_InitialUEMessage_IEs__value_PR_UEContextRequest;
ieCtxRequest->value.choice.UEContextRequest = ASN_NGAP_UEContextRequest_requested;
ies.push_back(ieCtxRequest);
auto *ieNasPdu = asn::New<ASN_NGAP_InitialUEMessage_IEs>();
ieNasPdu->id = ASN_NGAP_ProtocolIE_ID_id_NAS_PDU;
ieNasPdu->criticality = ASN_NGAP_Criticality_reject;
ieNasPdu->value.present = ASN_NGAP_InitialUEMessage_IEs__value_PR_NAS_PDU;
asn::SetOctetString(ieNasPdu->value.choice.NAS_PDU, nasPdu);
ies.push_back(ieNasPdu);
if (sTmsi)
{
auto *ieTmsi = asn::New<ASN_NGAP_InitialUEMessage_IEs>();
ieTmsi->id = ASN_NGAP_ProtocolIE_ID_id_FiveG_S_TMSI;
ieTmsi->criticality = ASN_NGAP_Criticality_reject;
ieTmsi->value.present = ASN_NGAP_InitialUEMessage_IEs__value_PR_FiveG_S_TMSI;
asn::SetBitStringInt<10>(sTmsi->amfSetId, ieTmsi->value.choice.FiveG_S_TMSI.aMFSetID);
asn::SetBitStringInt<6>(sTmsi->amfPointer, ieTmsi->value.choice.FiveG_S_TMSI.aMFPointer);
asn::SetOctetString4(ieTmsi->value.choice.FiveG_S_TMSI.fiveG_TMSI, sTmsi->tmsi);
ies.push_back(ieTmsi);
}
auto *pdu = asn::ngap::NewMessagePdu<ASN_NGAP_InitialUEMessage>(ies);
sendNgapUeAssociated(ueId, pdu);
}
void NgapTask::deliverDownlinkNas(int ueId, OctetString &&nasPdu)
{
auto w = std::make_unique<NmGnbNgapToRrc>(NmGnbNgapToRrc::NAS_DELIVERY);
w->ueId = ueId;
w->pdu = std::move(nasPdu);
m_base->rrcTask->push(std::move(w));
}
void NgapTask::handleUplinkNasTransport(int ueId, const OctetString &nasPdu)
{
auto *ue = findUeContext(ueId);
if (ue == nullptr)
return;
auto *ieNasPdu = asn::New<ASN_NGAP_UplinkNASTransport_IEs>();
ieNasPdu->id = ASN_NGAP_ProtocolIE_ID_id_NAS_PDU;
ieNasPdu->criticality = ASN_NGAP_Criticality_reject;
ieNasPdu->value.present = ASN_NGAP_UplinkNASTransport_IEs__value_PR_NAS_PDU;
asn::SetOctetString(ieNasPdu->value.choice.NAS_PDU, nasPdu);
auto *pdu = asn::ngap::NewMessagePdu<ASN_NGAP_UplinkNASTransport>({ieNasPdu});
sendNgapUeAssociated(ueId, pdu);
}
void NgapTask::sendNasNonDeliveryIndication(int ueId, const OctetString &nasPdu, NgapCause cause)
{
m_logger->debug("Sending non-delivery indication for UE[%d]", ueId);
auto *ieNasPdu = asn::New<ASN_NGAP_NASNonDeliveryIndication_IEs>();
ieNasPdu->id = ASN_NGAP_ProtocolIE_ID_id_NAS_PDU;
ieNasPdu->criticality = ASN_NGAP_Criticality_ignore;
ieNasPdu->value.present = ASN_NGAP_NASNonDeliveryIndication_IEs__value_PR_NAS_PDU;
asn::SetOctetString(ieNasPdu->value.choice.NAS_PDU, nasPdu);
auto *ieCause = asn::New<ASN_NGAP_NASNonDeliveryIndication_IEs>();
ieCause->id = ASN_NGAP_ProtocolIE_ID_id_Cause;
ieCause->criticality = ASN_NGAP_Criticality_ignore;
ieCause->value.present = ASN_NGAP_NASNonDeliveryIndication_IEs__value_PR_Cause;
ngap_utils::ToCauseAsn_Ref(cause, ieCause->value.choice.Cause);
auto *pdu = asn::ngap::NewMessagePdu<ASN_NGAP_NASNonDeliveryIndication>({ieNasPdu, ieCause});
sendNgapUeAssociated(ueId, pdu);
}
void NgapTask::receiveDownlinkNasTransport(int amfId, ASN_NGAP_DownlinkNASTransport *msg)
{
auto *ue = findUeByNgapIdPair(amfId, ngap_utils::FindNgapIdPair(msg));
if (ue == nullptr)
return;
auto *ieNasPdu = asn::ngap::GetProtocolIe(msg, ASN_NGAP_ProtocolIE_ID_id_NAS_PDU);
if (ieNasPdu)
deliverDownlinkNas(ue->ctxId, asn::GetOctetString(ieNasPdu->NAS_PDU));
}
void NgapTask::receiveRerouteNasRequest(int amfId, ASN_NGAP_RerouteNASRequest *msg)
{
m_logger->debug("Reroute NAS request received");
auto *ue = findUeByNgapIdPair(amfId, ngap_utils::FindNgapIdPair(msg));
if (ue == nullptr)
return;
auto *ieNgapMessage = asn::ngap::GetProtocolIe(msg, ASN_NGAP_ProtocolIE_ID_id_NGAP_Message);
auto *ieAmfSetId = asn::ngap::GetProtocolIe(msg, ASN_NGAP_ProtocolIE_ID_id_AMFSetID);
auto *ieAllowedNssai = asn::ngap::GetProtocolIe(msg, ASN_NGAP_ProtocolIE_ID_id_AllowedNSSAI);
auto ngapPdu = asn::New<ASN_NGAP_NGAP_PDU>();
ngapPdu->present = ASN_NGAP_NGAP_PDU_PR_initiatingMessage;
ngapPdu->choice.initiatingMessage = asn::New<ASN_NGAP_InitiatingMessage>();
ngapPdu->choice.initiatingMessage->procedureCode = ASN_NGAP_ProcedureCode_id_InitialUEMessage;
ngapPdu->choice.initiatingMessage->criticality = ASN_NGAP_Criticality_ignore;
ngapPdu->choice.initiatingMessage->value.present = ASN_NGAP_InitiatingMessage__value_PR_InitialUEMessage;
auto *initialUeMessage = &ngapPdu->choice.initiatingMessage->value.choice.InitialUEMessage;
if (!ngap_encode::DecodeInPlace(asn_DEF_ASN_NGAP_InitialUEMessage, ieNgapMessage->OCTET_STRING, &initialUeMessage))
{
m_logger->err("APER decoding failed in Reroute NAS Request");
asn::Free(asn_DEF_ASN_NGAP_NGAP_PDU, ngapPdu);
sendErrorIndication(amfId, NgapCause::Protocol_transfer_syntax_error);
return;
}
if (ieAllowedNssai)
{
auto *oldAllowedNssai = asn::ngap::GetProtocolIe(initialUeMessage, ASN_NGAP_ProtocolIE_ID_id_AllowedNSSAI);
if (oldAllowedNssai)
asn::DeepCopy(asn_DEF_ASN_NGAP_AllowedNSSAI, ieAllowedNssai->AllowedNSSAI, &oldAllowedNssai->AllowedNSSAI);
else
{
auto *newAllowedNssai = asn::New<ASN_NGAP_InitialUEMessage_IEs>();
newAllowedNssai->id = ASN_NGAP_ProtocolIE_ID_id_AllowedNSSAI;
newAllowedNssai->criticality = ASN_NGAP_Criticality_reject;
newAllowedNssai->value.present = ASN_NGAP_InitialUEMessage_IEs__value_PR_AllowedNSSAI;
asn::ngap::AddProtocolIe(*initialUeMessage, newAllowedNssai);
}
}
auto *newAmf = selectNewAmfForReAllocation(ue->ctxId, amfId, asn::GetBitStringInt<10>(ieAmfSetId->AMFSetID));
if (newAmf == nullptr)
{
m_logger->err("AMF selection for re-allocation failed. Could not find a suitable AMF.");
return;
}
sendNgapUeAssociated(ue->ctxId, ngapPdu);
}
} // namespace nr::gnb

35
src/gnb/ngap/nnsf.cpp Normal file
View File

@@ -0,0 +1,35 @@
//
// This file is a part of UERANSIM project.
// Copyright (c) 2023 ALİ GÜNGÖR.
//
// https://github.com/aligungr/UERANSIM/
// See README, LICENSE, and CONTRIBUTING files for licensing details.
//
#include "task.hpp"
namespace nr::gnb
{
NgapAmfContext *NgapTask::selectAmf(int ueId, int32_t &requestedSliceType)
{
for (auto &amf : m_amfCtx) {
for (const auto &plmnSupport : amf.second->plmnSupportList) {
for (const auto &singleSlice : plmnSupport->sliceSupportList.slices) {
int32_t supportedSliceType = static_cast<int32_t>(singleSlice.sst);
if (supportedSliceType == requestedSliceType) {
return amf.second;
}
}
}
}
return nullptr;
}
NgapAmfContext *NgapTask::selectNewAmfForReAllocation(int ueId, int initiatedAmfId, int amfSetId)
{
// TODO an arbitrary AMF is selected for now
return findAmfContext(initiatedAmfId);
}
} // namespace nr::gnb

59
src/gnb/ngap/radio.cpp Normal file
View File

@@ -0,0 +1,59 @@
//
// This file is a part of UERANSIM project.
// Copyright (c) 2023 ALİ GÜNGÖR.
//
// https://github.com/aligungr/UERANSIM/
// See README, LICENSE, and CONTRIBUTING files for licensing details.
//
#include "encode.hpp"
#include "task.hpp"
#include "utils.hpp"
#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
{
void NgapTask::handleRadioLinkFailure(int ueId)
{
// Notify GTP task
auto w = std::make_unique<NmGnbNgapToGtp>(NmGnbNgapToGtp::UE_CONTEXT_RELEASE);
w->ueId = ueId;
m_base->gtpTask->push(std::move(w));
// Notify AMF
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 = std::make_unique<NmGnbNgapToRrc>(NmGnbNgapToRrc::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(std::move(w));
}
} // namespace nr::gnb

316
src/gnb/ngap/session.cpp Normal file
View File

@@ -0,0 +1,316 @@
//
// This file is a part of UERANSIM project.
// Copyright (c) 2023 ALİ GÜNGÖR.
//
// https://github.com/aligungr/UERANSIM/
// See README, LICENSE, and CONTRIBUTING files for licensing details.
//
#include "encode.hpp"
#include "task.hpp"
#include "utils.hpp"
#include <set>
#include <stdexcept>
#include <gnb/gtp/task.hpp>
#include <asn/ngap/ASN_NGAP_AssociatedQosFlowItem.h>
#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>
#include <asn/ngap/ASN_NGAP_PDUSessionResourceSetupRequestTransfer.h>
#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>
namespace nr::gnb
{
void NgapTask::receiveSessionResourceSetupRequest(int amfId, ASN_NGAP_PDUSessionResourceSetupRequest *msg)
{
std::vector<ASN_NGAP_PDUSessionResourceSetupItemSURes *> successList;
std::vector<ASN_NGAP_PDUSessionResourceFailedToSetupItemSURes *> failedList;
auto *ue = findUeByNgapIdPair(amfId, ngap_utils::FindNgapIdPair(msg));
if (ue == nullptr)
return;
auto *ieList = asn::ngap::GetProtocolIe(msg, ASN_NGAP_ProtocolIE_ID_id_PDUSessionResourceSetupListSUReq);
if (ieList)
{
auto &list = ieList->PDUSessionResourceSetupListSUReq.list;
for (int i = 0; i < list.count; i++)
{
auto &item = list.array[i];
auto *transfer = ngap_encode::Decode<ASN_NGAP_PDUSessionResourceSetupRequestTransfer>(
asn_DEF_ASN_NGAP_PDUSessionResourceSetupRequestTransfer, item->pDUSessionResourceSetupRequestTransfer);
if (transfer == nullptr)
{
m_logger->err(
"Unable to decode a PDU session resource setup request transfer. Ignoring the relevant item");
asn::Free(asn_DEF_ASN_NGAP_PDUSessionResourceSetupRequestTransfer, transfer);
continue;
}
auto *resource = new PduSessionResource(ue->ctxId, static_cast<int>(item->pDUSessionID));
auto *ie = asn::ngap::GetProtocolIe(transfer, ASN_NGAP_ProtocolIE_ID_id_PDUSessionAggregateMaximumBitRate);
if (ie)
{
resource->sessionAmbr.dlAmbr =
asn::GetUnsigned64(ie->PDUSessionAggregateMaximumBitRate.pDUSessionAggregateMaximumBitRateDL) /
8ull;
resource->sessionAmbr.ulAmbr =
asn::GetUnsigned64(ie->PDUSessionAggregateMaximumBitRate.pDUSessionAggregateMaximumBitRateUL) /
8ull;
}
ie = asn::ngap::GetProtocolIe(transfer, ASN_NGAP_ProtocolIE_ID_id_DataForwardingNotPossible);
if (ie)
resource->dataForwardingNotPossible = true;
ie = asn::ngap::GetProtocolIe(transfer, ASN_NGAP_ProtocolIE_ID_id_PDUSessionType);
if (ie)
resource->sessionType = ngap_utils::PduSessionTypeFromAsn(ie->PDUSessionType);
ie = asn::ngap::GetProtocolIe(transfer, ASN_NGAP_ProtocolIE_ID_id_UL_NGU_UP_TNLInformation);
if (ie)
{
resource->upTunnel.teid =
(uint32_t)asn::GetOctet4(ie->UPTransportLayerInformation.choice.gTPTunnel->gTP_TEID);
resource->upTunnel.address =
asn::GetOctetString(ie->UPTransportLayerInformation.choice.gTPTunnel->transportLayerAddress);
}
ie = asn::ngap::GetProtocolIe(transfer, ASN_NGAP_ProtocolIE_ID_id_QosFlowSetupRequestList);
if (ie)
{
auto *ptr = asn::New<ASN_NGAP_QosFlowSetupRequestList>();
asn::DeepCopy(asn_DEF_ASN_NGAP_QosFlowSetupRequestList, ie->QosFlowSetupRequestList, ptr);
resource->qosFlows = asn::WrapUnique(ptr, asn_DEF_ASN_NGAP_QosFlowSetupRequestList);
}
auto error = setupPduSessionResource(ue, resource);
if (error.has_value())
{
auto *tr = asn::New<ASN_NGAP_PDUSessionResourceSetupUnsuccessfulTransfer>();
ngap_utils::ToCauseAsn_Ref(error.value(), tr->cause);
OctetString encodedTr =
ngap_encode::EncodeS(asn_DEF_ASN_NGAP_PDUSessionResourceSetupUnsuccessfulTransfer, tr);
if (encodedTr.length() == 0)
throw std::runtime_error("PDUSessionResourceSetupUnsuccessfulTransfer encoding failed");
asn::Free(asn_DEF_ASN_NGAP_PDUSessionResourceSetupUnsuccessfulTransfer, tr);
auto *res = asn::New<ASN_NGAP_PDUSessionResourceFailedToSetupItemSURes>();
res->pDUSessionID = resource->psi;
asn::SetOctetString(res->pDUSessionResourceSetupUnsuccessfulTransfer, encodedTr);
failedList.push_back(res);
}
else
{
if (item->pDUSessionNAS_PDU)
deliverDownlinkNas(ue->ctxId, asn::GetOctetString(*item->pDUSessionNAS_PDU));
auto *tr = asn::New<ASN_NGAP_PDUSessionResourceSetupResponseTransfer>();
auto &qosList = resource->qosFlows->list;
for (int iQos = 0; iQos < qosList.count; iQos++)
{
auto *associatedQosFlowItem = asn::New<ASN_NGAP_AssociatedQosFlowItem>();
associatedQosFlowItem->qosFlowIdentifier = qosList.array[iQos]->qosFlowIdentifier;
asn::SequenceAdd(tr->dLQosFlowPerTNLInformation.associatedQosFlowList, associatedQosFlowItem);
}
auto &upInfo = tr->dLQosFlowPerTNLInformation.uPTransportLayerInformation;
upInfo.present = ASN_NGAP_UPTransportLayerInformation_PR_gTPTunnel;
upInfo.choice.gTPTunnel = asn::New<ASN_NGAP_GTPTunnel>();
asn::SetBitString(upInfo.choice.gTPTunnel->transportLayerAddress, resource->downTunnel.address);
asn::SetOctetString4(upInfo.choice.gTPTunnel->gTP_TEID, (octet4)resource->downTunnel.teid);
OctetString encodedTr =
ngap_encode::EncodeS(asn_DEF_ASN_NGAP_PDUSessionResourceSetupResponseTransfer, tr);
if (encodedTr.length() == 0)
throw std::runtime_error("PDUSessionResourceSetupResponseTransfer encoding failed");
asn::Free(asn_DEF_ASN_NGAP_PDUSessionResourceSetupResponseTransfer, tr);
auto *res = asn::New<ASN_NGAP_PDUSessionResourceSetupItemSURes>();
res->pDUSessionID = resource->psi;
asn::SetOctetString(res->pDUSessionResourceSetupResponseTransfer, encodedTr);
successList.push_back(res);
}
asn::Free(asn_DEF_ASN_NGAP_PDUSessionResourceSetupRequestTransfer, transfer);
}
}
auto *ieNasPdu = asn::ngap::GetProtocolIe(msg, ASN_NGAP_ProtocolIE_ID_id_NAS_PDU);
if (ieNasPdu)
deliverDownlinkNas(ue->ctxId, asn::GetOctetString(ieNasPdu->NAS_PDU));
std::vector<ASN_NGAP_PDUSessionResourceSetupResponseIEs *> responseIes;
if (!successList.empty())
{
auto *ie = asn::New<ASN_NGAP_PDUSessionResourceSetupResponseIEs>();
ie->id = ASN_NGAP_ProtocolIE_ID_id_PDUSessionResourceSetupListSURes;
ie->criticality = ASN_NGAP_Criticality_ignore;
ie->value.present = ASN_NGAP_PDUSessionResourceSetupResponseIEs__value_PR_PDUSessionResourceSetupListSURes;
for (auto &item : successList)
asn::SequenceAdd(ie->value.choice.PDUSessionResourceSetupListSURes, item);
responseIes.push_back(ie);
}
if (!failedList.empty())
{
auto *ie = asn::New<ASN_NGAP_PDUSessionResourceSetupResponseIEs>();
ie->id = ASN_NGAP_ProtocolIE_ID_id_PDUSessionResourceFailedToSetupListSURes;
ie->criticality = ASN_NGAP_Criticality_ignore;
ie->value.present =
ASN_NGAP_PDUSessionResourceSetupResponseIEs__value_PR_PDUSessionResourceFailedToSetupListSURes;
for (auto &item : failedList)
asn::SequenceAdd(ie->value.choice.PDUSessionResourceFailedToSetupListSURes, item);
responseIes.push_back(ie);
}
auto *respPdu = asn::ngap::NewMessagePdu<ASN_NGAP_PDUSessionResourceSetupResponse>(responseIes);
sendNgapUeAssociated(ue->ctxId, respPdu);
if (failedList.empty())
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(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]",
static_cast<int>(successList.size()), static_cast<int>(failedList.size()));
}
std::optional<NgapCause> NgapTask::setupPduSessionResource(NgapUeContext *ue, PduSessionResource *resource)
{
if (resource->sessionType != PduSessionType::IPv4)
{
m_logger->err("PDU session resource could not setup: Only IPv4 is supported");
return NgapCause::RadioNetwork_unspecified;
}
if (resource->upTunnel.address.length() == 0)
{
m_logger->err("PDU session resource could not setup: Uplink TNL information is missing");
return NgapCause::Protocol_transfer_syntax_error;
}
if (resource->qosFlows == nullptr || resource->qosFlows->list.count == 0)
{
m_logger->err("PDU session resource could not setup: QoS flow list is null or empty");
return NgapCause::Protocol_semantic_error;
}
std::string gtpIp = m_base->config->gtpAdvertiseIp.value_or(m_base->config->gtpIp);
resource->downTunnel.address = utils::IpToOctetString(gtpIp);
resource->downTunnel.teid = ++m_downlinkTeidCounter;
auto w = std::make_unique<NmGnbNgapToGtp>(NmGnbNgapToGtp::SESSION_CREATE);
w->resource = resource;
m_base->gtpTask->push(std::move(w));
ue->pduSessions.insert(resource->psi);
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 = std::make_unique<NmGnbNgapToGtp>(NmGnbNgapToGtp::SESSION_RELEASE);
w->ueId = ue->ctxId;
w->psi = psi;
m_base->gtpTask->push(std::move(w));
ue->pduSessions.erase(psi);
}
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

108
src/gnb/ngap/task.cpp Normal file
View File

@@ -0,0 +1,108 @@
//
// This file is a part of UERANSIM project.
// Copyright (c) 2023 ALİ GÜNGÖR.
//
// https://github.com/aligungr/UERANSIM/
// See README, LICENSE, and CONTRIBUTING files for licensing details.
//
#include "task.hpp"
#include <sstream>
#include <gnb/app/task.hpp>
#include <gnb/sctp/task.hpp>
namespace nr::gnb
{
NgapTask::NgapTask(TaskBase *base) : m_base{base}, m_ueNgapIdCounter{}, m_downlinkTeidCounter{}, m_isInitialized{}
{
m_logger = base->logBase->makeUniqueLogger("ngap");
}
void NgapTask::onStart()
{
for (auto &amfConfig : m_base->config->amfConfigs)
createAmfContext(amfConfig);
if (m_amfCtx.empty())
m_logger->warn("No AMF configuration is provided");
for (auto &amfCtx : m_amfCtx)
{
auto msg = std::make_unique<NmGnbSctp>(NmGnbSctp::CONNECTION_REQUEST);
msg->clientId = amfCtx.second->ctxId;
msg->localAddress = m_base->config->ngapIp;
msg->localPort = 0;
msg->remoteAddress = amfCtx.second->address;
msg->remotePort = amfCtx.second->port;
msg->ppid = sctp::PayloadProtocolId::NGAP;
msg->associatedTask = this;
m_base->sctpTask->push(std::move(msg));
}
}
void NgapTask::onLoop()
{
auto msg = take();
if (!msg)
return;
switch (msg->msgType)
{
case NtsMessageType::GNB_RRC_TO_NGAP: {
auto &w = dynamic_cast<NmGnbRrcToNgap &>(*msg);
switch (w.present)
{
case NmGnbRrcToNgap::INITIAL_NAS_DELIVERY: {
handleInitialNasTransport(w.ueId, w.pdu, w.rrcEstablishmentCause, w.sTmsi);
break;
}
case NmGnbRrcToNgap::UPLINK_NAS_DELIVERY: {
handleUplinkNasTransport(w.ueId, w.pdu);
break;
}
case NmGnbRrcToNgap::RADIO_LINK_FAILURE: {
handleRadioLinkFailure(w.ueId);
break;
}
}
break;
}
case NtsMessageType::GNB_SCTP: {
auto &w = dynamic_cast<NmGnbSctp &>(*msg);
switch (w.present)
{
case NmGnbSctp::ASSOCIATION_SETUP:
handleAssociationSetup(w.clientId, w.associationId, w.inStreams, w.outStreams);
break;
case NmGnbSctp::RECEIVE_MESSAGE:
handleSctpMessage(w.clientId, w.stream, w.buffer);
break;
case NmGnbSctp::ASSOCIATION_SHUTDOWN:
handleAssociationShutdown(w.clientId);
break;
default:
m_logger->unhandledNts(*msg);
break;
}
break;
}
default: {
m_logger->unhandledNts(*msg);
break;
}
}
}
void NgapTask::onQuit()
{
for (auto &i : m_ueCtx)
delete i.second;
for (auto &i : m_amfCtx)
delete i.second;
m_ueCtx.clear();
m_amfCtx.clear();
}
} // namespace nr::gnb

129
src/gnb/ngap/task.hpp vendored Normal file
View File

@@ -0,0 +1,129 @@
//
// This file is a part of UERANSIM project.
// Copyright (c) 2023 ALİ GÜNGÖR.
//
// https://github.com/aligungr/UERANSIM/
// See README, LICENSE, and CONTRIBUTING files for licensing details.
//
#pragma once
#include <optional>
#include <unordered_map>
#include <gnb/nts.hpp>
#include <gnb/types.hpp>
#include <lib/app/monitor.hpp>
#include <utils/logger.hpp>
#include <utils/nts.hpp>
extern "C"
{
struct ASN_NGAP_NGAP_PDU;
struct ASN_NGAP_NGSetupResponse;
struct ASN_NGAP_NGSetupFailure;
struct ASN_NGAP_ErrorIndication;
struct ASN_NGAP_DownlinkNASTransport;
struct ASN_NGAP_RerouteNASRequest;
struct ASN_NGAP_PDUSessionResourceSetupRequest;
struct ASN_NGAP_InitialContextSetupRequest;
struct ASN_NGAP_UEContextReleaseCommand;
struct ASN_NGAP_UEContextModificationRequest;
struct ASN_NGAP_AMFConfigurationUpdate;
struct ASN_NGAP_OverloadStart;
struct ASN_NGAP_OverloadStop;
struct ASN_NGAP_PDUSessionResourceReleaseCommand;
struct ASN_NGAP_Paging;
}
namespace nr::gnb
{
class SctpTask;
class GnbRrcTask;
class GtpTask;
class GnbAppTask;
class NgapTask : public NtsTask
{
private:
TaskBase *m_base;
std::unique_ptr<Logger> m_logger;
std::unordered_map<int, NgapAmfContext *> m_amfCtx;
std::unordered_map<int, NgapUeContext *> m_ueCtx;
int64_t m_ueNgapIdCounter;
uint32_t m_downlinkTeidCounter;
bool m_isInitialized;
friend class GnbCmdHandler;
public:
explicit NgapTask(TaskBase *base);
~NgapTask() override = default;
protected:
void onStart() override;
void onLoop() override;
void onQuit() override;
private:
/* Utility functions */
void createAmfContext(const GnbAmfConfig &config);
NgapAmfContext *findAmfContext(int ctxId);
void createUeContext(int ueId, int32_t &requestedSliceType);
NgapUeContext *findUeContext(int ctxId);
NgapUeContext *findUeByRanId(int64_t ranUeNgapId);
NgapUeContext *findUeByAmfId(int64_t amfUeNgapId);
NgapUeContext *findUeByNgapIdPair(int amfCtxId, const NgapIdPair &idPair);
void deleteUeContext(int ueId);
void deleteAmfContext(int amfId);
/* Interface management */
void handleAssociationSetup(int amfId, int ascId, int inCount, int outCount);
void handleAssociationShutdown(int amfId);
void sendNgSetupRequest(int amfId);
void sendErrorIndication(int amfId, NgapCause cause = NgapCause::Protocol_unspecified, int ueId = 0);
void receiveNgSetupResponse(int amfId, ASN_NGAP_NGSetupResponse *msg);
void receiveNgSetupFailure(int amfId, ASN_NGAP_NGSetupFailure *msg);
void receiveErrorIndication(int amfId, ASN_NGAP_ErrorIndication *msg);
void receiveAmfConfigurationUpdate(int amfId, ASN_NGAP_AMFConfigurationUpdate *msg);
void receiveOverloadStart(int amfId, ASN_NGAP_OverloadStart *msg);
void receiveOverloadStop(int amfId, ASN_NGAP_OverloadStop *msg);
/* Message transport */
void sendNgapNonUe(int amfId, ASN_NGAP_NGAP_PDU *pdu);
void sendNgapUeAssociated(int ueId, ASN_NGAP_NGAP_PDU *pdu);
void handleSctpMessage(int amfId, uint16_t stream, const UniqueBuffer &buffer);
bool handleSctpStreamId(int amfId, int stream, const ASN_NGAP_NGAP_PDU &pdu);
/* NAS transport */
void handleInitialNasTransport(int ueId, OctetString &nasPdu, int64_t rrcEstablishmentCause,
const std::optional<GutiMobileIdentity> &sTmsi);
void handleUplinkNasTransport(int ueId, const OctetString &nasPdu);
void receiveDownlinkNasTransport(int amfId, ASN_NGAP_DownlinkNASTransport *msg);
void deliverDownlinkNas(int ueId, OctetString &&nasPdu);
void sendNasNonDeliveryIndication(int ueId, const OctetString &nasPdu, NgapCause cause);
void receiveRerouteNasRequest(int amfId, ASN_NGAP_RerouteNASRequest *msg);
/* PDU session management */
void receiveSessionResourceSetupRequest(int amfId, ASN_NGAP_PDUSessionResourceSetupRequest *msg);
void receiveSessionResourceReleaseCommand(int amfId, ASN_NGAP_PDUSessionResourceReleaseCommand *msg);
std::optional<NgapCause> setupPduSessionResource(NgapUeContext *ue, PduSessionResource *resource);
/* UE context management */
void receiveInitialContextSetup(int amfId, ASN_NGAP_InitialContextSetupRequest *msg);
void receiveContextRelease(int amfId, ASN_NGAP_UEContextReleaseCommand *msg);
void receiveContextModification(int amfId, ASN_NGAP_UEContextModificationRequest *msg);
void sendContextRelease(int ueId, NgapCause cause);
/* NAS Node Selection */
NgapAmfContext *selectAmf(int ueId, int32_t &requestedSliceType);
NgapAmfContext *selectNewAmfForReAllocation(int ueId, int initiatedAmfId, int amfSetId);
/* Radio resource control */
void handleRadioLinkFailure(int ueId);
void receivePaging(int amfId, ASN_NGAP_Paging *msg);
};
} // namespace nr::gnb

407
src/gnb/ngap/transport.cpp Normal file
View File

@@ -0,0 +1,407 @@
//
// This file is a part of UERANSIM project.
// Copyright (c) 2023 ALİ GÜNGÖR.
//
// https://github.com/aligungr/UERANSIM/
// See README, LICENSE, and CONTRIBUTING files for licensing details.
//
#include "encode.hpp"
#include "task.hpp"
#include "utils.hpp"
#include <gnb/app/task.hpp>
#include <gnb/nts.hpp>
#include <gnb/sctp/task.hpp>
#include <lib/asn/ngap.hpp>
#include <lib/asn/utils.hpp>
#include <asn/ngap/ASN_NGAP_AMF-UE-NGAP-ID.h>
#include <asn/ngap/ASN_NGAP_InitiatingMessage.h>
#include <asn/ngap/ASN_NGAP_NGAP-PDU.h>
#include <asn/ngap/ASN_NGAP_ProtocolIE-Field.h>
#include <asn/ngap/ASN_NGAP_RAN-UE-NGAP-ID.h>
#include <asn/ngap/ASN_NGAP_SuccessfulOutcome.h>
#include <asn/ngap/ASN_NGAP_UnsuccessfulOutcome.h>
#include <asn/ngap/ASN_NGAP_UserLocationInformation.h>
#include <asn/ngap/ASN_NGAP_UserLocationInformationNR.h>
static e_ASN_NGAP_Criticality FindCriticalityOfUserIe(ASN_NGAP_NGAP_PDU *pdu, ASN_NGAP_ProtocolIE_ID_t ieId)
{
auto procedureCode =
pdu->present == ASN_NGAP_NGAP_PDU_PR_initiatingMessage ? pdu->choice.initiatingMessage->procedureCode
: pdu->present == ASN_NGAP_NGAP_PDU_PR_successfulOutcome ? pdu->choice.successfulOutcome->procedureCode
: pdu->choice.unsuccessfulOutcome->procedureCode;
if (ieId == ASN_NGAP_ProtocolIE_ID_id_UserLocationInformation)
{
return procedureCode == ASN_NGAP_ProcedureCode_id_InitialUEMessage ? ASN_NGAP_Criticality_reject
: ASN_NGAP_Criticality_ignore;
}
if (ieId == ASN_NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID || ieId == ASN_NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID)
{
if (procedureCode == ASN_NGAP_ProcedureCode_id_RerouteNASRequest)
{
return ieId == ASN_NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID ? ASN_NGAP_Criticality_reject
: ASN_NGAP_Criticality_ignore;
}
if (pdu->present == ASN_NGAP_NGAP_PDU_PR_initiatingMessage)
{
if (procedureCode == ASN_NGAP_ProcedureCode_id_UEContextReleaseRequest ||
procedureCode == ASN_NGAP_ProcedureCode_id_HandoverPreparation)
return ASN_NGAP_Criticality_reject;
}
if (procedureCode == ASN_NGAP_ProcedureCode_id_PDUSessionResourceNotify ||
procedureCode == ASN_NGAP_ProcedureCode_id_PDUSessionResourceModifyIndication ||
procedureCode == ASN_NGAP_ProcedureCode_id_RRCInactiveTransitionReport ||
procedureCode == ASN_NGAP_ProcedureCode_id_HandoverNotification ||
procedureCode == ASN_NGAP_ProcedureCode_id_PathSwitchRequest ||
procedureCode == ASN_NGAP_ProcedureCode_id_HandoverCancel ||
procedureCode == ASN_NGAP_ProcedureCode_id_UplinkRANStatusTransfer ||
procedureCode == ASN_NGAP_ProcedureCode_id_InitialUEMessage ||
procedureCode == ASN_NGAP_ProcedureCode_id_DownlinkNASTransport ||
procedureCode == ASN_NGAP_ProcedureCode_id_UplinkNASTransport ||
procedureCode == ASN_NGAP_ProcedureCode_id_NASNonDeliveryIndication ||
procedureCode == ASN_NGAP_ProcedureCode_id_UplinkUEAssociatedNRPPaTransport ||
procedureCode == ASN_NGAP_ProcedureCode_id_UplinkNonUEAssociatedNRPPaTransport ||
procedureCode == ASN_NGAP_ProcedureCode_id_CellTrafficTrace ||
procedureCode == ASN_NGAP_ProcedureCode_id_TraceStart ||
procedureCode == ASN_NGAP_ProcedureCode_id_DeactivateTrace ||
procedureCode == ASN_NGAP_ProcedureCode_id_TraceFailureIndication ||
procedureCode == ASN_NGAP_ProcedureCode_id_LocationReport ||
procedureCode == ASN_NGAP_ProcedureCode_id_LocationReportingControl ||
procedureCode == ASN_NGAP_ProcedureCode_id_LocationReportingFailureIndication ||
procedureCode == ASN_NGAP_ProcedureCode_id_UERadioCapabilityInfoIndication)
return ASN_NGAP_Criticality_reject;
}
return ASN_NGAP_Criticality_ignore;
}
namespace nr::gnb
{
void NgapTask::sendNgapNonUe(int associatedAmf, ASN_NGAP_NGAP_PDU *pdu)
{
auto *amf = findAmfContext(associatedAmf);
if (amf == nullptr)
{
asn::Free(asn_DEF_ASN_NGAP_NGAP_PDU, pdu);
return;
}
char errorBuffer[1024];
size_t len;
if (asn_check_constraints(&asn_DEF_ASN_NGAP_NGAP_PDU, pdu, errorBuffer, &len) != 0)
{
m_logger->err("NGAP PDU ASN constraint validation failed");
asn::Free(asn_DEF_ASN_NGAP_NGAP_PDU, pdu);
return;
}
ssize_t encoded;
uint8_t *buffer;
if (!ngap_encode::Encode(asn_DEF_ASN_NGAP_NGAP_PDU, pdu, encoded, buffer))
m_logger->err("NGAP APER encoding failed");
else
{
auto msg = std::make_unique<NmGnbSctp>(NmGnbSctp::SEND_MESSAGE);
msg->clientId = amf->ctxId;
msg->stream = 0;
msg->buffer = UniqueBuffer{buffer, static_cast<size_t>(encoded)};
m_base->sctpTask->push(std::move(msg));
if (m_base->nodeListener)
{
std::string xer = ngap_encode::EncodeXer(asn_DEF_ASN_NGAP_NGAP_PDU, pdu);
if (xer.length() > 0)
{
m_base->nodeListener->onSend(app::NodeType::GNB, m_base->config->name, app::NodeType::AMF, amf->amfName,
app::ConnectionType::NGAP, xer);
}
}
}
asn::Free(asn_DEF_ASN_NGAP_NGAP_PDU, pdu);
}
void NgapTask::sendNgapUeAssociated(int ueId, ASN_NGAP_NGAP_PDU *pdu)
{
/* Find UE and AMF contexts */
auto *ue = findUeContext(ueId);
if (ue == nullptr)
{
asn::Free(asn_DEF_ASN_NGAP_NGAP_PDU, pdu);
return;
}
auto *amf = findAmfContext(ue->associatedAmfId);
if (amf == nullptr)
{
asn::Free(asn_DEF_ASN_NGAP_NGAP_PDU, pdu);
return;
}
/* Insert UE-related information elements */
{
if (ue->amfUeNgapId > 0)
{
asn::ngap::AddProtocolIeIfUsable(
*pdu, asn_DEF_ASN_NGAP_AMF_UE_NGAP_ID, ASN_NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID,
FindCriticalityOfUserIe(pdu, ASN_NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID), [ue](void *mem) {
auto &id = *reinterpret_cast<ASN_NGAP_AMF_UE_NGAP_ID_t *>(mem);
asn::SetSigned64(ue->amfUeNgapId, id);
});
}
asn::ngap::AddProtocolIeIfUsable(
*pdu, asn_DEF_ASN_NGAP_RAN_UE_NGAP_ID, ASN_NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID,
FindCriticalityOfUserIe(pdu, ASN_NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID),
[ue](void *mem) { *reinterpret_cast<ASN_NGAP_RAN_UE_NGAP_ID_t *>(mem) = ue->ranUeNgapId; });
asn::ngap::AddProtocolIeIfUsable(
*pdu, asn_DEF_ASN_NGAP_UserLocationInformation, ASN_NGAP_ProtocolIE_ID_id_UserLocationInformation,
FindCriticalityOfUserIe(pdu, ASN_NGAP_ProtocolIE_ID_id_UserLocationInformation), [this](void *mem) {
auto *loc = reinterpret_cast<ASN_NGAP_UserLocationInformation *>(mem);
loc->present = ASN_NGAP_UserLocationInformation_PR_userLocationInformationNR;
loc->choice.userLocationInformationNR = asn::New<ASN_NGAP_UserLocationInformationNR>();
auto &nr = loc->choice.userLocationInformationNR;
nr->timeStamp = asn::New<ASN_NGAP_TimeStamp_t>();
ngap_utils::ToPlmnAsn_Ref(m_base->config->plmn, nr->nR_CGI.pLMNIdentity);
asn::SetBitStringLong<36>(m_base->config->nci, nr->nR_CGI.nRCellIdentity);
ngap_utils::ToPlmnAsn_Ref(m_base->config->plmn, nr->tAI.pLMNIdentity);
asn::SetOctetString3(nr->tAI.tAC, octet3{m_base->config->tac});
asn::SetOctetString4(*nr->timeStamp, octet4{utils::CurrentTimeStamp().seconds32()});
});
}
/* Encode and send the PDU */
char errorBuffer[1024];
size_t len;
if (asn_check_constraints(&asn_DEF_ASN_NGAP_NGAP_PDU, pdu, errorBuffer, &len) != 0)
{
m_logger->err("NGAP PDU ASN constraint validation failed");
asn::Free(asn_DEF_ASN_NGAP_NGAP_PDU, pdu);
return;
}
ssize_t encoded;
uint8_t *buffer;
if (!ngap_encode::Encode(asn_DEF_ASN_NGAP_NGAP_PDU, pdu, encoded, buffer))
m_logger->err("NGAP APER encoding failed");
else
{
auto msg = std::make_unique<NmGnbSctp>(NmGnbSctp::SEND_MESSAGE);
msg->clientId = amf->ctxId;
msg->stream = ue->uplinkStream;
msg->buffer = UniqueBuffer{buffer, static_cast<size_t>(encoded)};
m_base->sctpTask->push(std::move(msg));
if (m_base->nodeListener)
{
std::string xer = ngap_encode::EncodeXer(asn_DEF_ASN_NGAP_NGAP_PDU, pdu);
if (xer.length() > 0)
{
m_base->nodeListener->onSend(app::NodeType::GNB, m_base->config->name, app::NodeType::AMF, amf->amfName,
app::ConnectionType::NGAP, xer);
}
}
}
asn::Free(asn_DEF_ASN_NGAP_NGAP_PDU, pdu);
}
void NgapTask::handleSctpMessage(int amfId, uint16_t stream, const UniqueBuffer &buffer)
{
auto *amf = findAmfContext(amfId);
if (amf == nullptr)
return;
auto *pdu = ngap_encode::Decode<ASN_NGAP_NGAP_PDU>(asn_DEF_ASN_NGAP_NGAP_PDU, buffer.data(), buffer.size());
if (pdu == nullptr)
{
m_logger->err("APER decoding failed for SCTP message");
asn::Free(asn_DEF_ASN_NGAP_NGAP_PDU, pdu);
sendErrorIndication(amfId, NgapCause::Protocol_transfer_syntax_error);
return;
}
if (m_base->nodeListener)
{
std::string xer = ngap_encode::EncodeXer(asn_DEF_ASN_NGAP_NGAP_PDU, pdu);
if (xer.length() > 0)
{
m_base->nodeListener->onReceive(app::NodeType::GNB, m_base->config->name, app::NodeType::AMF, amf->amfName,
app::ConnectionType::NGAP, xer);
}
}
if (!handleSctpStreamId(amf->ctxId, stream, *pdu))
{
asn::Free(asn_DEF_ASN_NGAP_NGAP_PDU, pdu);
return;
}
if (pdu->present == ASN_NGAP_NGAP_PDU_PR_initiatingMessage)
{
auto value = pdu->choice.initiatingMessage->value;
switch (value.present)
{
case ASN_NGAP_InitiatingMessage__value_PR_ErrorIndication:
receiveErrorIndication(amf->ctxId, &value.choice.ErrorIndication);
break;
case ASN_NGAP_InitiatingMessage__value_PR_InitialContextSetupRequest:
receiveInitialContextSetup(amf->ctxId, &value.choice.InitialContextSetupRequest);
break;
case ASN_NGAP_InitiatingMessage__value_PR_RerouteNASRequest:
receiveRerouteNasRequest(amf->ctxId, &value.choice.RerouteNASRequest);
break;
case ASN_NGAP_InitiatingMessage__value_PR_UEContextReleaseCommand:
receiveContextRelease(amf->ctxId, &value.choice.UEContextReleaseCommand);
break;
case ASN_NGAP_InitiatingMessage__value_PR_UEContextModificationRequest:
receiveContextModification(amf->ctxId, &value.choice.UEContextModificationRequest);
break;
case ASN_NGAP_InitiatingMessage__value_PR_PDUSessionResourceSetupRequest:
receiveSessionResourceSetupRequest(amf->ctxId, &value.choice.PDUSessionResourceSetupRequest);
break;
case ASN_NGAP_InitiatingMessage__value_PR_DownlinkNASTransport:
receiveDownlinkNasTransport(amf->ctxId, &value.choice.DownlinkNASTransport);
break;
case ASN_NGAP_InitiatingMessage__value_PR_AMFConfigurationUpdate:
receiveAmfConfigurationUpdate(amf->ctxId, &value.choice.AMFConfigurationUpdate);
break;
case ASN_NGAP_InitiatingMessage__value_PR_OverloadStart:
receiveOverloadStart(amf->ctxId, &value.choice.OverloadStart);
break;
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;
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;
}
}
else if (pdu->present == ASN_NGAP_NGAP_PDU_PR_successfulOutcome)
{
auto value = pdu->choice.successfulOutcome->value;
switch (value.present)
{
case ASN_NGAP_SuccessfulOutcome__value_PR_NGSetupResponse:
receiveNgSetupResponse(amf->ctxId, &value.choice.NGSetupResponse);
break;
default:
m_logger->err("Unhandled NGAP successful-outcome received (%d)", value.present);
break;
}
}
else if (pdu->present == ASN_NGAP_NGAP_PDU_PR_unsuccessfulOutcome)
{
auto value = pdu->choice.unsuccessfulOutcome->value;
switch (value.present)
{
case ASN_NGAP_UnsuccessfulOutcome__value_PR_NGSetupFailure:
receiveNgSetupFailure(amf->ctxId, &value.choice.NGSetupFailure);
break;
default:
m_logger->err("Unhandled NGAP unsuccessful-outcome received (%d)", value.present);
break;
}
}
else
{
m_logger->warn("Empty NGAP PDU ignored");
}
asn::Free(asn_DEF_ASN_NGAP_NGAP_PDU, pdu);
}
bool NgapTask::handleSctpStreamId(int amfId, int stream, const ASN_NGAP_NGAP_PDU &pdu)
{
if (m_base->config->ignoreStreamIds)
return true;
auto *ptr =
asn::ngap::FindProtocolIeInPdu(pdu, asn_DEF_ASN_NGAP_UE_NGAP_IDs, ASN_NGAP_ProtocolIE_ID_id_UE_NGAP_IDs);
if (ptr != nullptr)
{
if (stream == 0)
{
m_logger->err("Received stream number == 0 in UE-associated signalling");
sendErrorIndication(amfId, NgapCause::Protocol_unspecified);
return false;
}
auto &ids = *reinterpret_cast<ASN_NGAP_UE_NGAP_IDs *>(ptr);
auto *ue = findUeByNgapIdPair(amfId, ngap_utils::FindNgapIdPairFromAsnNgapIds(ids));
if (ue == nullptr)
return false;
if (ue->downlinkStream == 0)
ue->downlinkStream = stream;
else if (ue->downlinkStream != stream)
{
m_logger->err("received stream number is inconsistent. received %d, expected :%d", stream,
ue->downlinkStream);
sendErrorIndication(amfId, NgapCause::Protocol_unspecified);
return false;
}
}
else
{
ptr = asn::ngap::FindProtocolIeInPdu(pdu, asn_DEF_ASN_NGAP_RAN_UE_NGAP_ID,
ASN_NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID);
if (ptr != nullptr)
{
if (stream == 0)
{
m_logger->err("Received stream number == 0 in UE-associated signalling");
sendErrorIndication(amfId, NgapCause::Protocol_unspecified);
return false;
}
auto id = static_cast<int64_t>(*reinterpret_cast<ASN_NGAP_RAN_UE_NGAP_ID_t *>(ptr));
auto *ue = findUeByRanId(id);
if (ue == nullptr)
return false;
if (ue->downlinkStream == 0)
ue->downlinkStream = stream;
else if (ue->downlinkStream != stream)
{
m_logger->err("received stream number is inconsistent. received %d, expected :%d", stream,
ue->downlinkStream);
sendErrorIndication(amfId, NgapCause::Protocol_unspecified);
return false;
}
}
else
{
if (stream != 0)
{
m_logger->err("Received stream number != 0 in non-UE-associated signalling");
sendErrorIndication(amfId, NgapCause::Protocol_unspecified);
return false;
}
}
}
return true;
}
} // namespace nr::gnb

230
src/gnb/ngap/utils.cpp Normal file
View File

@@ -0,0 +1,230 @@
//
// This file is a part of UERANSIM project.
// Copyright (c) 2023 ALİ GÜNGÖR.
//
// https://github.com/aligungr/UERANSIM/
// See README, LICENSE, and CONTRIBUTING files for licensing details.
//
#include "utils.hpp"
namespace nr::gnb::ngap_utils
{
ASN_NGAP_PagingDRX_t PagingDrxToAsn(EPagingDrx pagingDrx)
{
switch (pagingDrx)
{
case EPagingDrx::V32:
return ASN_NGAP_PagingDRX_v32;
case EPagingDrx::V64:
return ASN_NGAP_PagingDRX_v64;
case EPagingDrx::V128:
return ASN_NGAP_PagingDRX_v128;
case EPagingDrx::V256:
return ASN_NGAP_PagingDRX_v256;
}
return ~0;
}
octet3 PlmnToOctet3(const Plmn &plmn)
{
int mcc = plmn.mcc;
int mcc3 = mcc % 10;
int mcc2 = (mcc % 100) / 10;
int mcc1 = (mcc % 1000) / 100;
int mnc = plmn.mnc;
if (plmn.isLongMnc)
{
int mnc1 = mnc % 1000 / 100;
int mnc2 = mnc % 100 / 10;
int mnc3 = mnc % 10;
int octet1 = mcc2 << 4 | mcc1;
int octet2 = mnc1 << 4 | mcc3;
int octet3 = mnc3 << 4 | mnc2;
return {(uint8_t)octet1, (uint8_t)octet2, (uint8_t)octet3};
}
else
{
int mnc1 = mnc % 100 / 10;
int mnc2 = mnc % 10;
int mnc3 = 0xF;
int octet1 = mcc2 << 4 | mcc1;
int octet2 = mnc3 << 4 | mcc3;
int octet3 = mnc2 << 4 | mnc1;
return {(uint8_t)octet1, (uint8_t)octet2, (uint8_t)octet3};
}
}
void PlmnFromAsn_Ref(const ASN_NGAP_PLMNIdentity_t &source, Plmn &target)
{
assert(source.size == 3);
int i = ((source.buf[1] & 0xf0) >> 4);
if (i == 0xf)
{
i = 0;
target.isLongMnc = false;
}
else
target.isLongMnc = true;
target.mcc = (((source.buf[0] & 0xf0) >> 4) * 10) + ((source.buf[0] & 0x0f) * 100) + (source.buf[1] & 0x0f);
target.mnc = (i * 100) + ((source.buf[2] & 0xf0) >> 4) + ((source.buf[2] & 0x0f) * 10);
}
void GuamiFromAsn_Ref(const ASN_NGAP_GUAMI_t &guami, Guami &target)
{
target.amfRegionId = asn::GetBitStringInt<8>(guami.aMFRegionID);
target.amfSetId = asn::GetBitStringInt<10>(guami.aMFSetID);
target.amfPointer = asn::GetBitStringInt<6>(guami.aMFPointer);
target.plmn = {};
PlmnFromAsn_Ref(guami.pLMNIdentity, target.plmn);
}
SingleSlice SliceSupportFromAsn(ASN_NGAP_SliceSupportItem &supportItem)
{
SingleSlice s{};
s.sst = asn::GetOctet1(supportItem.s_NSSAI.sST);
s.sd = std::nullopt;
if (supportItem.s_NSSAI.sD)
s.sd = asn::GetOctet3(*supportItem.s_NSSAI.sD);
return s;
}
std::string CauseToString(const ASN_NGAP_Cause_t &cause)
{
std::string result;
int64_t enumValue;
asn_TYPE_descriptor_t *typeDescriptor;
switch (cause.present)
{
case ASN_NGAP_Cause_PR_radioNetwork:
result = "radio-network";
enumValue = static_cast<int64_t>(cause.choice.radioNetwork);
typeDescriptor = &asn_DEF_ASN_NGAP_CauseRadioNetwork;
break;
case ASN_NGAP_Cause_PR_transport:
result = "transport";
enumValue = static_cast<int64_t>(cause.choice.transport);
typeDescriptor = &asn_DEF_ASN_NGAP_CauseTransport;
break;
case ASN_NGAP_Cause_PR_nas:
result = "nas";
enumValue = static_cast<int64_t>(cause.choice.nas);
typeDescriptor = &asn_DEF_ASN_NGAP_CauseNas;
break;
case ASN_NGAP_Cause_PR_protocol:
result = "protocol";
enumValue = static_cast<int64_t>(cause.choice.protocol);
typeDescriptor = &asn_DEF_ASN_NGAP_CauseProtocol;
break;
case ASN_NGAP_Cause_PR_misc:
result = "misc";
enumValue = static_cast<int64_t>(cause.choice.misc);
typeDescriptor = &asn_DEF_ASN_NGAP_CauseMisc;
break;
default:
return "<?>";
}
auto *specs = reinterpret_cast<const asn_INTEGER_specifics_t *>(typeDescriptor->specifics);
if (specs)
{
if (specs->value2enum)
{
for (int i = 0; i < specs->map_count; i++)
{
if (static_cast<int64_t>(specs->value2enum[i].nat_value) == enumValue)
{
result += "/" + std::string(specs->value2enum[i].enum_name);
break;
}
}
}
}
return result;
}
void ToCauseAsn_Ref(NgapCause source, ASN_NGAP_Cause_t &target)
{
int val = (int)source;
if (val >= 400)
{
val -= 400;
target.present = ASN_NGAP_Cause_PR_misc;
target.choice.misc = static_cast<ASN_NGAP_CauseMisc_t>(val);
}
else if (val >= 300)
{
val -= 300;
target.present = ASN_NGAP_Cause_PR_protocol;
target.choice.protocol = static_cast<ASN_NGAP_CauseProtocol_t>(val);
}
else if (val >= 200)
{
val -= 200;
target.present = ASN_NGAP_Cause_PR_nas;
target.choice.nas = static_cast<ASN_NGAP_CauseNas_t>(val);
}
else if (val >= 100)
{
val -= 100;
target.present = ASN_NGAP_Cause_PR_transport;
target.choice.transport = static_cast<ASN_NGAP_CauseTransport_t>(val);
}
else
{
target.present = ASN_NGAP_Cause_PR_radioNetwork;
target.choice.radioNetwork = static_cast<ASN_NGAP_CauseRadioNetwork_t>(val);
}
}
PduSessionType PduSessionTypeFromAsn(const ASN_NGAP_PDUSessionType_t &source)
{
switch (source)
{
case ASN_NGAP_PDUSessionType_ipv4:
return PduSessionType::IPv4;
case ASN_NGAP_PDUSessionType_ipv6:
return PduSessionType::IPv6;
case ASN_NGAP_PDUSessionType_ipv4v6:
return PduSessionType::IPv4v6;
case ASN_NGAP_PDUSessionType_ethernet:
return PduSessionType::ETHERNET;
default:
return PduSessionType::UNSTRUCTURED;
}
}
void ToPlmnAsn_Ref(const Plmn &source, ASN_NGAP_PLMNIdentity_t &target)
{
octet3 val = PlmnToOctet3(source);
asn::SetOctetString3(target, val);
}
NgapIdPair FindNgapIdPairFromAsnNgapIds(const ASN_NGAP_UE_NGAP_IDs &ngapIDs)
{
std::optional<int64_t> amfUeNgapId{}, ranUeNgapId{};
if (ngapIDs.present == ASN_NGAP_UE_NGAP_IDs_PR_uE_NGAP_ID_pair)
{
amfUeNgapId = asn::GetSigned64(ngapIDs.choice.uE_NGAP_ID_pair->aMF_UE_NGAP_ID);
ranUeNgapId = ngapIDs.choice.uE_NGAP_ID_pair->rAN_UE_NGAP_ID;
}
else if (ngapIDs.present == ASN_NGAP_UE_NGAP_IDs_PR_aMF_UE_NGAP_ID)
{
amfUeNgapId = asn::GetSigned64(ngapIDs.choice.aMF_UE_NGAP_ID);
}
return NgapIdPair{amfUeNgapId, ranUeNgapId};
}
} // namespace nr::gnb::ngap_utils

65
src/gnb/ngap/utils.hpp vendored Normal file
View File

@@ -0,0 +1,65 @@
//
// This file is a part of UERANSIM project.
// Copyright (c) 2023 ALİ GÜNGÖR.
//
// https://github.com/aligungr/UERANSIM/
// See README, LICENSE, and CONTRIBUTING files for licensing details.
//
#pragma once
#include <gnb/types.hpp>
#include <lib/asn/ngap.hpp>
#include <lib/asn/utils.hpp>
#include <utils/common.hpp>
#include <utils/common_types.hpp>
#include <asn/ngap/ASN_NGAP_Cause.h>
#include <asn/ngap/ASN_NGAP_GUAMI.h>
#include <asn/ngap/ASN_NGAP_PagingDRX.h>
#include <asn/ngap/ASN_NGAP_ProtocolIE-Field.h>
#include <asn/ngap/ASN_NGAP_SliceSupportItem.h>
#include <asn/ngap/ASN_NGAP_UE-NGAP-ID-pair.h>
namespace nr::gnb::ngap_utils
{
ASN_NGAP_PagingDRX_t PagingDrxToAsn(EPagingDrx pagingDrx);
std::string CauseToString(const ASN_NGAP_Cause_t &cause);
octet3 PlmnToOctet3(const Plmn &plmn);
PduSessionType PduSessionTypeFromAsn(const ASN_NGAP_PDUSessionType_t &source);
void PlmnFromAsn_Ref(const ASN_NGAP_PLMNIdentity_t &source, Plmn &target);
void GuamiFromAsn_Ref(const ASN_NGAP_GUAMI_t &guami, Guami &target);
void ToCauseAsn_Ref(NgapCause source, ASN_NGAP_Cause_t &target);
void ToPlmnAsn_Ref(const Plmn &source, ASN_NGAP_PLMNIdentity_t &target);
SingleSlice SliceSupportFromAsn(ASN_NGAP_SliceSupportItem &supportItem);
NgapIdPair FindNgapIdPairFromAsnNgapIds(const ASN_NGAP_UE_NGAP_IDs &ngapIDs);
template <typename T>
inline NgapIdPair FindNgapIdPair(T *msg)
{
auto *ieAmfUeNgapId = asn::ngap::GetProtocolIe(msg, ASN_NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID);
auto *ieRanUeNgapId = asn::ngap::GetProtocolIe(msg, ASN_NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID);
std::optional<int64_t> amfUeNgapId{}, ranUeNgapId{};
if (ieAmfUeNgapId)
amfUeNgapId = asn::GetSigned64(ieAmfUeNgapId->AMF_UE_NGAP_ID);
if (ieRanUeNgapId)
ranUeNgapId = ieRanUeNgapId->RAN_UE_NGAP_ID;
return NgapIdPair{amfUeNgapId, ranUeNgapId};
}
template <typename T>
inline NgapIdPair FindNgapIdPairFromUeNgapIds(T *msg)
{
auto *ieUeNgapIds = asn::ngap::GetProtocolIe(msg, ASN_NGAP_ProtocolIE_ID_id_UE_NGAP_IDs);
if (ieUeNgapIds)
return FindNgapIdPairFromAsnNgapIds(ieUeNgapIds->UE_NGAP_IDs);
return NgapIdPair{{}, {}};
}
} // namespace nr::gnb::ngap_utils