// // 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace nr::gnb { void NgapTask::receiveSessionResourceSetupRequest(int amfId, ASN_NGAP_PDUSessionResourceSetupRequest *msg) { std::vector successList; std::vector 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_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(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::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(); 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(); 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(); auto &qosList = resource->qosFlows->list; for (int iQos = 0; iQos < qosList.count; iQos++) { auto *associatedQosFlowItem = asn::New(); 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::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(); 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 responseIes; if (!successList.empty()) { auto *ie = asn::New(); 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(); 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(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(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(failedList.size())); else m_logger->err("PDU session establishment is partially successful for UE[%d], success[%d], failed[%d]", static_cast(successList.size()), static_cast(failedList.size())); } std::optional 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::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 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(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(); 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::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(); 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(); item->pDUSessionID = static_cast(psi); asn::SetOctetString(item->pDUSessionResourceReleaseResponseTransfer, encodedTr); asn::SequenceAdd(ieResp->value.choice.PDUSessionResourceReleasedListRelRes, item); } auto *respPdu = asn::ngap::NewMessagePdu({ieResp}); sendNgapUeAssociated(ue->ctxId, respPdu); m_logger->info("PDU session resource(s) released for UE[%d] count[%d]", ue->ctxId, static_cast(psIds.size())); } } // namespace nr::gnb