init-version-3.27
This commit is contained in:
324
src/gnb/ngap/context.cpp
Normal file
324
src/gnb/ngap/context.cpp
Normal 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
14
src/gnb/ngap/encode.cpp
Normal 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
96
src/gnb/ngap/encode.hpp
vendored
Normal 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
362
src/gnb/ngap/interface.cpp
Normal 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
157
src/gnb/ngap/management.cpp
Normal 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
248
src/gnb/ngap/nas.cpp
Normal 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
35
src/gnb/ngap/nnsf.cpp
Normal 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
59
src/gnb/ngap/radio.cpp
Normal 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
316
src/gnb/ngap/session.cpp
Normal 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
108
src/gnb/ngap/task.cpp
Normal 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
129
src/gnb/ngap/task.hpp
vendored
Normal 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
407
src/gnb/ngap/transport.cpp
Normal 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
230
src/gnb/ngap/utils.cpp
Normal 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
65
src/gnb/ngap/utils.hpp
vendored
Normal 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
|
||||
Reference in New Issue
Block a user