This reverts commit 0f100a6e94, which
introduced a hack to possibly wait with a PDU session setup at the RRC
in case a RRC UE capability transaction (requesting UE capabilities from
a UE) was ongoing. This happened, as we delayed the UE capabilities to
after the first RRC reconfiguration; in that case, certain UEs were
requesting the next PDU session, and if RRC did not delay the new PDU
session (as requested from the core), this procedure might occur while
UE capability enquiry was ongoing, leading to failures in these
transactions.
1408 lines
62 KiB
C
1408 lines
62 KiB
C
/*
|
|
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership.
|
|
* The OpenAirInterface Software Alliance licenses this file to You under
|
|
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
|
* except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.openairinterface.org/?page_id=698
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*-------------------------------------------------------------------------------
|
|
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
|
* contact@openairinterface.org
|
|
*/
|
|
|
|
/*! \file rrc_gNB_NGAP.h
|
|
* \brief rrc NGAP procedures for gNB
|
|
* \author Yoshio INOUE, Masayuki HARADA
|
|
* \date 2020
|
|
* \version 0.1
|
|
* \email: yoshio.inoue@fujitsu.com,masayuki.harada@fujitsu.com
|
|
* (yoshio.inoue%40fujitsu.com%2cmasayuki.harada%40fujitsu.com)
|
|
*/
|
|
|
|
#include "rrc_gNB_NGAP.h"
|
|
#include "RRC/L2_INTERFACE/openair_rrc_L2_interface.h"
|
|
#include "rrc_eNB_S1AP.h"
|
|
#include "gnb_config.h"
|
|
#include "common/ran_context.h"
|
|
|
|
#include "oai_asn1.h"
|
|
#include "intertask_interface.h"
|
|
#include "nr_pdcp/nr_pdcp_oai_api.h"
|
|
#include "pdcp_primitives.h"
|
|
#include "SDAP/nr_sdap/nr_sdap.h"
|
|
|
|
#include "openair3/ocp-gtpu/gtp_itf.h"
|
|
#include <openair3/ocp-gtpu/gtp_itf.h>
|
|
#include "RRC/LTE/rrc_eNB_GTPV1U.h"
|
|
#include "RRC/NR/rrc_gNB_GTPV1U.h"
|
|
|
|
#include "S1AP_NAS-PDU.h"
|
|
#include "executables/softmodem-common.h"
|
|
#include "openair3/SECU/key_nas_deriver.h"
|
|
|
|
#include "ngap_gNB_defs.h"
|
|
#include "ngap_gNB_ue_context.h"
|
|
#include "ngap_gNB_management_procedures.h"
|
|
#include "NR_ULInformationTransfer.h"
|
|
#include "RRC/NR/MESSAGES/asn1_msg.h"
|
|
#include "NR_UERadioAccessCapabilityInformation.h"
|
|
#include "NR_UE-CapabilityRAT-ContainerList.h"
|
|
#include "NGAP_CauseRadioNetwork.h"
|
|
#include "f1ap_messages_types.h"
|
|
#include "openair2/F1AP/f1ap_ids.h"
|
|
#include "openair2/E1AP/e1ap_asnc.h"
|
|
#include "openair2/E1AP/e1ap.h"
|
|
#include "NGAP_asn_constant.h"
|
|
#include "NGAP_PDUSessionResourceSetupRequestTransfer.h"
|
|
#include "NGAP_PDUSessionResourceModifyRequestTransfer.h"
|
|
#include "NGAP_ProtocolIE-Field.h"
|
|
#include "NGAP_GTPTunnel.h"
|
|
#include "NGAP_QosFlowSetupRequestItem.h"
|
|
#include "NGAP_QosFlowAddOrModifyRequestItem.h"
|
|
#include "NGAP_NonDynamic5QIDescriptor.h"
|
|
#include "NGAP_Dynamic5QIDescriptor.h"
|
|
#include "conversions.h"
|
|
#include "RRC/NR/rrc_gNB_radio_bearers.h"
|
|
|
|
#ifdef E2_AGENT
|
|
#include "openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc_extern.h"
|
|
#endif
|
|
|
|
#include "uper_encoder.h"
|
|
|
|
extern RAN_CONTEXT_t RC;
|
|
|
|
/* Masks for NGAP Encryption algorithms, NEA0 is always supported (not coded) */
|
|
static const uint16_t NGAP_ENCRYPTION_NEA1_MASK = 0x8000;
|
|
static const uint16_t NGAP_ENCRYPTION_NEA2_MASK = 0x4000;
|
|
static const uint16_t NGAP_ENCRYPTION_NEA3_MASK = 0x2000;
|
|
|
|
/* Masks for NGAP Integrity algorithms, NIA0 is always supported (not coded) */
|
|
static const uint16_t NGAP_INTEGRITY_NIA1_MASK = 0x8000;
|
|
static const uint16_t NGAP_INTEGRITY_NIA2_MASK = 0x4000;
|
|
static const uint16_t NGAP_INTEGRITY_NIA3_MASK = 0x2000;
|
|
|
|
#define INTEGRITY_ALGORITHM_NONE NR_IntegrityProtAlgorithm_nia0
|
|
|
|
static int rrc_gNB_process_security(const protocol_ctxt_t *const ctxt_pP, rrc_gNB_ue_context_t *const ue_context_pP, ngap_security_capabilities_t *security_capabilities_pP);
|
|
|
|
/*! \fn void process_gNB_security_key (const protocol_ctxt_t* const ctxt_pP, eNB_RRC_UE_t * const ue_context_pP, uint8_t *security_key)
|
|
*\brief save security key.
|
|
*\param ctxt_pP Running context.
|
|
*\param ue_context_pP UE context.
|
|
*\param security_key_pP The security key received from NGAP.
|
|
*/
|
|
//------------------------------------------------------------------------------
|
|
void process_gNB_security_key (
|
|
const protocol_ctxt_t *const ctxt_pP,
|
|
rrc_gNB_ue_context_t *const ue_context_pP,
|
|
uint8_t *security_key_pP
|
|
)
|
|
//------------------------------------------------------------------------------
|
|
{
|
|
char ascii_buffer[65];
|
|
uint8_t i;
|
|
gNB_RRC_UE_t *UE = &ue_context_pP->ue_context;
|
|
|
|
/* Saves the security key */
|
|
memcpy(UE->kgnb, security_key_pP, SECURITY_KEY_LENGTH);
|
|
memset(UE->nh, 0, SECURITY_KEY_LENGTH);
|
|
UE->nh_ncc = -1;
|
|
|
|
for (i = 0; i < 32; i++) {
|
|
sprintf(&ascii_buffer[2 * i], "%02X", UE->kgnb[i]);
|
|
}
|
|
|
|
ascii_buffer[2 * i] = '\0';
|
|
LOG_I(NR_RRC, "[gNB %d][UE %x] Saved security key %s\n", ctxt_pP->module_id, UE->rnti, ascii_buffer);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void
|
|
nr_rrc_pdcp_config_security(
|
|
const protocol_ctxt_t *const ctxt_pP,
|
|
rrc_gNB_ue_context_t *const ue_context_pP,
|
|
const uint8_t enable_ciphering
|
|
)
|
|
//------------------------------------------------------------------------------
|
|
{
|
|
uint8_t kRRCenc[NR_K_KEY_SIZE] = {0};
|
|
uint8_t kRRCint[NR_K_KEY_SIZE] = {0};
|
|
uint8_t kUPenc[NR_K_KEY_SIZE] = {0};
|
|
//uint8_t *k_kdf = NULL;
|
|
static int print_keys= 1;
|
|
gNB_RRC_UE_t *UE = &ue_context_pP->ue_context;
|
|
|
|
/* Derive the keys from kgnb */
|
|
if (UE->Srb[1].Active || UE->Srb[2].Active) {
|
|
nr_derive_key(UP_ENC_ALG, UE->ciphering_algorithm, UE->kgnb, kUPenc);
|
|
}
|
|
|
|
nr_derive_key(RRC_ENC_ALG, UE->ciphering_algorithm, UE->kgnb, kRRCenc);
|
|
nr_derive_key(RRC_INT_ALG, UE->integrity_algorithm, UE->kgnb, kRRCint);
|
|
|
|
if ( LOG_DUMPFLAG( DEBUG_SECURITY ) ) {
|
|
if (print_keys == 1 ) {
|
|
print_keys =0;
|
|
LOG_DUMPMSG(NR_RRC, DEBUG_SECURITY, UE->kgnb, 32, "\nKgNB:");
|
|
LOG_DUMPMSG(NR_RRC, DEBUG_SECURITY, kRRCenc, 16,"\nKRRCenc:" );
|
|
LOG_DUMPMSG(NR_RRC, DEBUG_SECURITY, kRRCint, 16,"\nKRRCint:" );
|
|
}
|
|
}
|
|
|
|
uint8_t security_mode =
|
|
enable_ciphering ? UE->ciphering_algorithm | (UE->integrity_algorithm << 4) : 0 | (UE->integrity_algorithm << 4);
|
|
nr_pdcp_config_set_security(ctxt_pP->rntiMaybeUEid, DCCH, security_mode, kRRCenc, kRRCint, kUPenc);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/*
|
|
* Initial UE NAS message on S1AP.
|
|
*/
|
|
void
|
|
rrc_gNB_send_NGAP_NAS_FIRST_REQ(
|
|
const protocol_ctxt_t *const ctxt_pP,
|
|
rrc_gNB_ue_context_t *ue_context_pP,
|
|
NR_RRCSetupComplete_IEs_t *rrcSetupComplete
|
|
)
|
|
//------------------------------------------------------------------------------
|
|
{
|
|
// gNB_RRC_INST *rrc = RC.nrrrc[ctxt_pP->module_id];
|
|
MessageDef *message_p = NULL;
|
|
gNB_RRC_UE_t *UE = &ue_context_pP->ue_context;
|
|
|
|
message_p = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_NAS_FIRST_REQ);
|
|
ngap_nas_first_req_t *req = &NGAP_NAS_FIRST_REQ(message_p);
|
|
memset(req, 0, sizeof(*req));
|
|
|
|
req->gNB_ue_ngap_id = UE->rrc_ue_id;
|
|
|
|
/* Assume that cause is coded in the same way in RRC and NGap, just check that the value is in NGap range */
|
|
AssertFatal(UE->establishment_cause < NGAP_RRC_CAUSE_LAST, "Establishment cause invalid (%jd/%d) for gNB %d!", UE->establishment_cause, NGAP_RRC_CAUSE_LAST, ctxt_pP->module_id);
|
|
req->establishment_cause = UE->establishment_cause;
|
|
|
|
/* Forward NAS message */
|
|
req->nas_pdu.length = rrcSetupComplete->dedicatedNAS_Message.size;
|
|
req->nas_pdu.buffer = malloc(req->nas_pdu.length);
|
|
AssertFatal(req->nas_pdu.buffer != NULL, "out of memory\n");
|
|
memcpy(req->nas_pdu.buffer, rrcSetupComplete->dedicatedNAS_Message.buf, req->nas_pdu.length);
|
|
// extract_imsi(NGAP_NAS_FIRST_REQ (message_p).nas_pdu.buffer,
|
|
// NGAP_NAS_FIRST_REQ (message_p).nas_pdu.length,
|
|
// ue_context_pP);
|
|
|
|
/* Fill UE identities with available information */
|
|
req->ue_identity.presenceMask = NGAP_UE_IDENTITIES_NONE;
|
|
if (UE->Initialue_identity_5g_s_TMSI.presence) {
|
|
/* Fill s-TMSI */
|
|
req->ue_identity.presenceMask = NGAP_UE_IDENTITIES_FiveG_s_tmsi;
|
|
req->ue_identity.s_tmsi.amf_set_id = UE->Initialue_identity_5g_s_TMSI.amf_set_id;
|
|
req->ue_identity.s_tmsi.amf_pointer = UE->Initialue_identity_5g_s_TMSI.amf_pointer;
|
|
req->ue_identity.s_tmsi.m_tmsi = UE->Initialue_identity_5g_s_TMSI.fiveg_tmsi;
|
|
}
|
|
|
|
/* selected_plmn_identity: IE is 1-based, convert to 0-based (C array) */
|
|
int selected_plmn_identity = rrcSetupComplete->selectedPLMN_Identity - 1;
|
|
req->selected_plmn_identity = selected_plmn_identity;
|
|
|
|
if (rrcSetupComplete->registeredAMF != NULL) {
|
|
NR_RegisteredAMF_t *r_amf = rrcSetupComplete->registeredAMF;
|
|
req->ue_identity.presenceMask |= NGAP_UE_IDENTITIES_guami;
|
|
|
|
if (r_amf->plmn_Identity != NULL) {
|
|
if ((r_amf->plmn_Identity->mcc != NULL) && (r_amf->plmn_Identity->mcc->list.count > 0)) {
|
|
/* Use first indicated PLMN MCC if it is defined */
|
|
req->ue_identity.guami.mcc = *r_amf->plmn_Identity->mcc->list.array[selected_plmn_identity];
|
|
LOG_I(NGAP, "[gNB %d] Build NGAP_NAS_FIRST_REQ adding in s_TMSI: GUMMEI MCC %u ue %x\n", ctxt_pP->module_id, req->ue_identity.guami.mcc, UE->rnti);
|
|
}
|
|
|
|
if (r_amf->plmn_Identity->mnc.list.count > 0) {
|
|
/* Use first indicated PLMN MNC if it is defined */
|
|
req->ue_identity.guami.mnc = *r_amf->plmn_Identity->mnc.list.array[selected_plmn_identity];
|
|
LOG_I(NGAP, "[gNB %d] Build NGAP_NAS_FIRST_REQ adding in s_TMSI: GUMMEI MNC %u ue %x\n", ctxt_pP->module_id, req->ue_identity.guami.mnc, UE->rnti);
|
|
}
|
|
} else {
|
|
/* TODO */
|
|
}
|
|
|
|
/* amf_Identifier */
|
|
uint32_t amf_Id = BIT_STRING_to_uint32(&r_amf->amf_Identifier);
|
|
req->ue_identity.guami.amf_region_id = amf_Id >> 16;
|
|
req->ue_identity.guami.amf_set_id = UE->Initialue_identity_5g_s_TMSI.amf_set_id;
|
|
req->ue_identity.guami.amf_pointer = UE->Initialue_identity_5g_s_TMSI.amf_pointer;
|
|
|
|
// fixme: illogical place to set UE values, should be in the function that call this one
|
|
UE->ue_guami.mcc = req->ue_identity.guami.mcc;
|
|
UE->ue_guami.mnc = req->ue_identity.guami.mnc;
|
|
UE->ue_guami.mnc_len = req->ue_identity.guami.mnc_len;
|
|
UE->ue_guami.amf_region_id = req->ue_identity.guami.amf_region_id;
|
|
UE->ue_guami.amf_set_id = req->ue_identity.guami.amf_set_id;
|
|
UE->ue_guami.amf_pointer = req->ue_identity.guami.amf_pointer;
|
|
|
|
LOG_I(NGAP,
|
|
"[gNB %d] Build NGAP_NAS_FIRST_REQ adding in s_TMSI: GUAMI amf_set_id %u amf_region_id %u ue %x\n",
|
|
ctxt_pP->module_id,
|
|
req->ue_identity.guami.amf_set_id,
|
|
req->ue_identity.guami.amf_region_id,
|
|
UE->rnti);
|
|
}
|
|
|
|
itti_send_msg_to_task (TASK_NGAP, ctxt_pP->instance, message_p);
|
|
}
|
|
|
|
static void fill_qos(NGAP_QosFlowSetupRequestList_t *qos, pdusession_t *session)
|
|
{
|
|
DevAssert(qos->list.count > 0);
|
|
DevAssert(qos->list.count <= NGAP_maxnoofQosFlows);
|
|
for (int qosIdx = 0; qosIdx < qos->list.count; qosIdx++) {
|
|
NGAP_QosFlowSetupRequestItem_t *qosFlowItem_p = qos->list.array[qosIdx];
|
|
// Set the QOS informations
|
|
session->qos[qosIdx].qfi = (uint8_t)qosFlowItem_p->qosFlowIdentifier;
|
|
NGAP_QosCharacteristics_t *qosChar = &qosFlowItem_p->qosFlowLevelQosParameters.qosCharacteristics;
|
|
AssertFatal(qosChar, "Qos characteristics are not available for qos flow index %d\n", qosIdx);
|
|
if (qosChar->present == NGAP_QosCharacteristics_PR_nonDynamic5QI) {
|
|
AssertFatal(qosChar->choice.dynamic5QI, "Non-Dynamic 5QI is NULL\n");
|
|
session->qos[qosIdx].fiveQI_type = non_dynamic;
|
|
session->qos[qosIdx].fiveQI = (uint64_t)qosChar->choice.nonDynamic5QI->fiveQI;
|
|
} else {
|
|
AssertFatal(qosChar->choice.dynamic5QI, "Dynamic 5QI is NULL\n");
|
|
session->qos[qosIdx].fiveQI_type = dynamic;
|
|
session->qos[qosIdx].fiveQI = (uint64_t)(*qosChar->choice.dynamic5QI->fiveQI);
|
|
}
|
|
|
|
ngap_allocation_retention_priority_t *tmp = &session->qos[qosIdx].allocation_retention_priority;
|
|
NGAP_AllocationAndRetentionPriority_t *tmp2 = &qosFlowItem_p->qosFlowLevelQosParameters.allocationAndRetentionPriority;
|
|
tmp->priority_level = tmp2->priorityLevelARP;
|
|
tmp->pre_emp_capability = tmp2->pre_emptionCapability;
|
|
tmp->pre_emp_vulnerability = tmp2->pre_emptionVulnerability;
|
|
}
|
|
session->nb_qos = qos->list.count;
|
|
}
|
|
|
|
static int decodePDUSessionResourceSetup(pdusession_t *session)
|
|
{
|
|
NGAP_PDUSessionResourceSetupRequestTransfer_t *pdusessionTransfer = NULL;
|
|
asn_codec_ctx_t st = {.max_stack_size = 100 * 1000};
|
|
asn_dec_rval_t dec_rval =
|
|
aper_decode(&st, &asn_DEF_NGAP_PDUSessionResourceSetupRequestTransfer, (void **)&pdusessionTransfer, session->pdusessionTransfer.buffer, session->pdusessionTransfer.length, 0, 0);
|
|
|
|
if (dec_rval.code != RC_OK) {
|
|
LOG_E(NR_RRC, "can not decode PDUSessionResourceSetupRequestTransfer\n");
|
|
return -1;
|
|
}
|
|
|
|
for (int i = 0; i < pdusessionTransfer->protocolIEs.list.count; i++) {
|
|
NGAP_PDUSessionResourceSetupRequestTransferIEs_t *pdusessionTransfer_ies = pdusessionTransfer->protocolIEs.list.array[i];
|
|
switch (pdusessionTransfer_ies->id) {
|
|
/* optional PDUSessionAggregateMaximumBitRate */
|
|
case NGAP_ProtocolIE_ID_id_PDUSessionAggregateMaximumBitRate:
|
|
break;
|
|
|
|
/* mandatory UL-NGU-UP-TNLInformation */
|
|
case NGAP_ProtocolIE_ID_id_UL_NGU_UP_TNLInformation: {
|
|
NGAP_GTPTunnel_t *gTPTunnel_p = pdusessionTransfer_ies->value.choice.UPTransportLayerInformation.choice.gTPTunnel;
|
|
|
|
/* Set the transport layer address */
|
|
memcpy(session->upf_addr.buffer, gTPTunnel_p->transportLayerAddress.buf, gTPTunnel_p->transportLayerAddress.size);
|
|
|
|
session->upf_addr.length = gTPTunnel_p->transportLayerAddress.size * 8 - gTPTunnel_p->transportLayerAddress.bits_unused;
|
|
|
|
/* GTP tunnel endpoint ID */
|
|
OCTET_STRING_TO_INT32(&gTPTunnel_p->gTP_TEID, session->gtp_teid);
|
|
}
|
|
|
|
break;
|
|
|
|
/* optional AdditionalUL-NGU-UP-TNLInformation */
|
|
case NGAP_ProtocolIE_ID_id_AdditionalUL_NGU_UP_TNLInformation:
|
|
break;
|
|
|
|
/* optional DataForwardingNotPossible */
|
|
case NGAP_ProtocolIE_ID_id_DataForwardingNotPossible:
|
|
break;
|
|
|
|
/* mandatory PDUSessionType */
|
|
case NGAP_ProtocolIE_ID_id_PDUSessionType:
|
|
session->pdu_session_type = (uint8_t)pdusessionTransfer_ies->value.choice.PDUSessionType;
|
|
AssertFatal(session->pdu_session_type == PDUSessionType_ipv4 || session->pdu_session_type == PDUSessionType_ipv4v6, "To be developped: support not IPv4 sessions\n");
|
|
break;
|
|
|
|
/* optional SecurityIndication */
|
|
case NGAP_ProtocolIE_ID_id_SecurityIndication:
|
|
break;
|
|
|
|
/* optional NetworkInstance */
|
|
case NGAP_ProtocolIE_ID_id_NetworkInstance:
|
|
break;
|
|
|
|
/* mandatory QosFlowSetupRequestList */
|
|
case NGAP_ProtocolIE_ID_id_QosFlowSetupRequestList:
|
|
fill_qos(&pdusessionTransfer_ies->value.choice.QosFlowSetupRequestList, session);
|
|
break;
|
|
|
|
/* optional CommonNetworkInstance */
|
|
case NGAP_ProtocolIE_ID_id_CommonNetworkInstance:
|
|
break;
|
|
|
|
default:
|
|
LOG_E(NR_RRC, "could not found protocolIEs id %ld\n", pdusessionTransfer_ies->id);
|
|
return -1;
|
|
}
|
|
}
|
|
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_PDUSessionResourceSetupRequestTransfer,pdusessionTransfer );
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void trigger_bearer_setup(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, pdusession_t *sessions, uint64_t ueAggMaxBitRateDownlink)
|
|
{
|
|
e1ap_bearer_setup_req_t bearer_req = {0};
|
|
|
|
e1ap_nssai_t cuup_nssai = {0};
|
|
for (int i = 0; i < n; i++) {
|
|
rrc_pdu_session_param_t *pduSession = find_pduSession(UE, sessions[i].pdusession_id, true);
|
|
pdusession_t *session = &pduSession->param;
|
|
session->pdusession_id = sessions[i].pdusession_id;
|
|
LOG_I(NR_RRC, "Adding pdusession %d, total nb of sessions %d\n", session->pdusession_id, UE->nb_of_pdusessions);
|
|
session->pdu_session_type = sessions[i].pdu_session_type;
|
|
session->nas_pdu = sessions[i].nas_pdu;
|
|
session->pdusessionTransfer = sessions[i].pdusessionTransfer;
|
|
session->nssai = sessions[i].nssai;
|
|
decodePDUSessionResourceSetup(session);
|
|
bearer_req.gNB_cu_cp_ue_id = UE->rrc_ue_id;
|
|
bearer_req.cipheringAlgorithm = UE->ciphering_algorithm;
|
|
bearer_req.integrityProtectionAlgorithm = UE->integrity_algorithm;
|
|
nr_derive_key(UP_ENC_ALG, UE->ciphering_algorithm, UE->kgnb, (uint8_t *)bearer_req.encryptionKey);
|
|
nr_derive_key(UP_INT_ALG, UE->integrity_algorithm, UE->kgnb, (uint8_t *)bearer_req.integrityProtectionKey);
|
|
bearer_req.ueDlAggMaxBitRate = ueAggMaxBitRateDownlink;
|
|
pdu_session_to_setup_t *pdu = bearer_req.pduSession + bearer_req.numPDUSessions;
|
|
bearer_req.numPDUSessions++;
|
|
pdu->sessionId = session->pdusession_id;
|
|
pdu->nssai = sessions[i].nssai;
|
|
if (cuup_nssai.sst == 0)
|
|
cuup_nssai = pdu->nssai; /* for CU-UP selection below */
|
|
|
|
pdu->integrityProtectionIndication = rrc->security.do_drb_integrity ? E1AP_IntegrityProtectionIndication_required : E1AP_IntegrityProtectionIndication_not_needed;
|
|
|
|
pdu->confidentialityProtectionIndication = rrc->security.do_drb_ciphering ? E1AP_ConfidentialityProtectionIndication_required : E1AP_ConfidentialityProtectionIndication_not_needed;
|
|
pdu->teId = session->gtp_teid;
|
|
memcpy(&pdu->tlAddress, session->upf_addr.buffer, 4); // Fixme: dirty IPv4 target
|
|
|
|
/* we assume for the moment one DRB per PDU session. Activate the bearer,
|
|
* and configure in RRC. */
|
|
int drb_id = get_next_available_drb_id(UE);
|
|
drb_t *rrc_drb = generateDRB(UE,
|
|
drb_id,
|
|
pduSession,
|
|
rrc->configuration.enable_sdap,
|
|
rrc->security.do_drb_integrity,
|
|
rrc->security.do_drb_ciphering);
|
|
|
|
pdu->numDRB2Setup = 1; // One DRB per PDU Session. TODO: Remove hardcoding
|
|
for (int j=0; j < pdu->numDRB2Setup; j++) {
|
|
DRB_nGRAN_to_setup_t *drb = pdu->DRBnGRanList + j;
|
|
|
|
drb->id = rrc_drb->drb_id;
|
|
/* SDAP */
|
|
struct sdap_config_s *sdap_config = &rrc_drb->cnAssociation.sdap_config;
|
|
drb->sdap_config.defaultDRB = sdap_config->defaultDRB;
|
|
drb->sdap_config.sDAP_Header_UL = sdap_config->sdap_HeaderUL;
|
|
drb->sdap_config.sDAP_Header_DL = sdap_config->sdap_HeaderDL;
|
|
/* PDCP */
|
|
set_bearer_context_pdcp_config(&drb->pdcp_config, rrc_drb, rrc->configuration.um_on_default_drb);
|
|
|
|
drb->numCellGroups = 1; // assume one cell group associated with a DRB
|
|
|
|
for (int k=0; k < drb->numCellGroups; k++) {
|
|
cell_group_t *cellGroup = drb->cellGroupList + k;
|
|
cellGroup->id = 0; // MCG
|
|
}
|
|
|
|
drb->numQosFlow2Setup = session->nb_qos;
|
|
for (int k=0; k < drb->numQosFlow2Setup; k++) {
|
|
qos_flow_to_setup_t *qos_flow = drb->qosFlows + k;
|
|
pdusession_level_qos_parameter_t *qos_session = session->qos + k;
|
|
|
|
qos_characteristics_t *qos_char = &qos_flow->qos_params.qos_characteristics;
|
|
qos_flow->qfi = qos_session->qfi;
|
|
qos_char->qos_type = qos_session->fiveQI_type;
|
|
if (qos_char->qos_type == dynamic) {
|
|
qos_char->dynamic.fiveqi = qos_session->fiveQI;
|
|
qos_char->dynamic.qos_priority_level = qos_session->qos_priority;
|
|
} else {
|
|
qos_char->non_dynamic.fiveqi = qos_session->fiveQI;
|
|
qos_char->non_dynamic.qos_priority_level = qos_session->qos_priority;
|
|
}
|
|
|
|
ngran_allocation_retention_priority_t *rent_priority = &qos_flow->qos_params.alloc_reten_priority;
|
|
ngap_allocation_retention_priority_t *rent_priority_in = &qos_session->allocation_retention_priority;
|
|
rent_priority->priority_level = rent_priority_in->priority_level;
|
|
rent_priority->preemption_capability = rent_priority_in->pre_emp_capability;
|
|
rent_priority->preemption_vulnerability = rent_priority_in->pre_emp_vulnerability;
|
|
}
|
|
}
|
|
}
|
|
/* Limitation: we assume one fixed CU-UP per UE. We base the selection on
|
|
* NSSAI, but the UE might have multiple PDU sessions with differing slices,
|
|
* in which we might need to select different CU-UPs. In this case, we would
|
|
* actually need to group the E1 bearer context setup for the different
|
|
* CU-UPs, and send them to the different CU-UPs. */
|
|
sctp_assoc_t assoc_id = get_new_cuup_for_ue(rrc, UE, cuup_nssai.sst, cuup_nssai.sd);
|
|
rrc->cucp_cuup.bearer_context_setup(assoc_id, &bearer_req);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int rrc_gNB_process_NGAP_INITIAL_CONTEXT_SETUP_REQ(MessageDef *msg_p, instance_t instance)
|
|
//------------------------------------------------------------------------------
|
|
{
|
|
protocol_ctxt_t ctxt = {0};
|
|
ngap_initial_context_setup_req_t *req = &NGAP_INITIAL_CONTEXT_SETUP_REQ(msg_p);
|
|
|
|
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(RC.nrrrc[instance], req->gNB_ue_ngap_id);
|
|
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
|
|
|
|
if (ue_context_p == NULL) {
|
|
/* Can not associate this message to an UE index, send a failure to NGAP and discard it! */
|
|
MessageDef *msg_fail_p = NULL;
|
|
LOG_W(NR_RRC, "[gNB %ld] In NGAP_INITIAL_CONTEXT_SETUP_REQ: unknown UE from NGAP ids (%u)\n", instance, req->gNB_ue_ngap_id);
|
|
msg_fail_p = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_INITIAL_CONTEXT_SETUP_FAIL);
|
|
NGAP_INITIAL_CONTEXT_SETUP_FAIL(msg_fail_p).gNB_ue_ngap_id = req->gNB_ue_ngap_id;
|
|
// TODO add failure cause when defined!
|
|
itti_send_msg_to_task(TASK_NGAP, instance, msg_fail_p);
|
|
return (-1);
|
|
}
|
|
PROTOCOL_CTXT_SET_BY_INSTANCE(&ctxt, instance, GNB_FLAG_YES, UE->rrc_ue_id, 0, 0);
|
|
UE->amf_ue_ngap_id = req->amf_ue_ngap_id;
|
|
|
|
/* NAS PDU */
|
|
// this is malloced pointers, we pass it for later free()
|
|
UE->nas_pdu = req->nas_pdu;
|
|
|
|
/* security */
|
|
rrc_gNB_process_security(&ctxt, ue_context_p, &req->security_capabilities);
|
|
process_gNB_security_key(&ctxt, ue_context_p, req->security_key);
|
|
|
|
/* configure only integrity, ciphering comes after receiving SecurityModeComplete */
|
|
nr_rrc_pdcp_config_security(&ctxt, ue_context_p, 0);
|
|
|
|
uint8_t nb_pdusessions_tosetup = req->nb_of_pdusessions;
|
|
/* if there are PDU sessions to setup, first send them to the CU-UP.
|
|
* Once the E1 bearer are activated, the CUCP will trigger the context
|
|
* setup. */
|
|
AssertFatal(nb_pdusessions_tosetup == 0, "no UE context setup request, so can't setup PDU sessions\n");
|
|
if (nb_pdusessions_tosetup > 0) {
|
|
trigger_bearer_setup(RC.nrrrc[instance],
|
|
UE,
|
|
req->nb_of_pdusessions,
|
|
req->pdusession_param,
|
|
/*req->ueAggMaxBitRateDownlink*/ 0);
|
|
} else {
|
|
rrc_gNB_generate_SecurityModeCommand(&ctxt, ue_context_p);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void rrc_gNB_send_NGAP_INITIAL_CONTEXT_SETUP_RESP(const protocol_ctxt_t *const ctxt_pP, rrc_gNB_ue_context_t *const ue_context_pP)
|
|
//------------------------------------------------------------------------------
|
|
{
|
|
MessageDef *msg_p = NULL;
|
|
int pdu_sessions_done = 0;
|
|
int pdu_sessions_failed = 0;
|
|
msg_p = itti_alloc_new_message (TASK_RRC_ENB, 0, NGAP_INITIAL_CONTEXT_SETUP_RESP);
|
|
ngap_initial_context_setup_resp_t *resp = &NGAP_INITIAL_CONTEXT_SETUP_RESP(msg_p);
|
|
gNB_RRC_UE_t *UE = &ue_context_pP->ue_context;
|
|
|
|
resp->gNB_ue_ngap_id = UE->rrc_ue_id;
|
|
|
|
for (int pdusession = 0; pdusession < UE->nb_of_pdusessions; pdusession++) {
|
|
rrc_pdu_session_param_t *session = &UE->pduSession[pdusession];
|
|
if (session->status == PDU_SESSION_STATUS_DONE) {
|
|
pdu_sessions_done++;
|
|
resp->pdusessions[pdusession].pdusession_id = session->param.pdusession_id;
|
|
resp->pdusessions[pdusession].gtp_teid = session->param.gNB_teid_N3;
|
|
memcpy(resp->pdusessions[pdusession].gNB_addr.buffer,
|
|
session->param.gNB_addr_N3.buffer,
|
|
sizeof(resp->pdusessions[pdusession].gNB_addr.buffer));
|
|
resp->pdusessions[pdusession].gNB_addr.length = 4; // Fixme: IPv4 hard coded here
|
|
resp->pdusessions[pdusession].nb_of_qos_flow = session->param.nb_qos;
|
|
for (int qos_flow_index = 0; qos_flow_index < session->param.nb_qos; qos_flow_index++) {
|
|
resp->pdusessions[pdusession].associated_qos_flows[qos_flow_index].qfi = session->param.qos[qos_flow_index].qfi;
|
|
resp->pdusessions[pdusession].associated_qos_flows[qos_flow_index].qos_flow_mapping_ind = QOSFLOW_MAPPING_INDICATION_DL;
|
|
}
|
|
} else if (session->status != PDU_SESSION_STATUS_ESTABLISHED) {
|
|
session->status = PDU_SESSION_STATUS_FAILED;
|
|
pdusession_failed_t *fail = &resp->pdusessions_failed[pdu_sessions_failed];
|
|
fail->pdusession_id = session->param.pdusession_id;
|
|
fail->cause = NGAP_CAUSE_RADIO_NETWORK;
|
|
fail->cause_value = NGAP_CauseRadioNetwork_unknown_PDU_session_ID;
|
|
pdu_sessions_failed++;
|
|
}
|
|
}
|
|
|
|
resp->nb_of_pdusessions = pdu_sessions_done;
|
|
resp->nb_of_pdusessions_failed = pdu_sessions_failed;
|
|
itti_send_msg_to_task (TASK_NGAP, ctxt_pP->instance, msg_p);
|
|
}
|
|
|
|
static NR_CipheringAlgorithm_t rrc_gNB_select_ciphering(
|
|
const protocol_ctxt_t *const ctxt_pP,
|
|
uint16_t algorithms)
|
|
{
|
|
gNB_RRC_INST *rrc = RC.nrrrc[ctxt_pP->module_id];
|
|
int i;
|
|
/* preset nea0 as fallback */
|
|
int ret = 0;
|
|
|
|
/* Select ciphering algorithm based on gNB configuration file and
|
|
* UE's supported algorithms.
|
|
* We take the first from the list that is supported by the UE.
|
|
* The ordering of the list comes from the configuration file.
|
|
*/
|
|
for (i = 0; i < rrc->security.ciphering_algorithms_count; i++) {
|
|
int nea_mask[4] = {
|
|
0,
|
|
NGAP_ENCRYPTION_NEA1_MASK,
|
|
NGAP_ENCRYPTION_NEA2_MASK,
|
|
NGAP_ENCRYPTION_NEA3_MASK
|
|
};
|
|
if (rrc->security.ciphering_algorithms[i] == 0) {
|
|
/* nea0 */
|
|
break;
|
|
}
|
|
if (algorithms & nea_mask[rrc->security.ciphering_algorithms[i]]) {
|
|
ret = rrc->security.ciphering_algorithms[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
LOG_D(RRC, "selecting ciphering algorithm %d\n", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static e_NR_IntegrityProtAlgorithm rrc_gNB_select_integrity(
|
|
const protocol_ctxt_t *const ctxt_pP,
|
|
uint16_t algorithms)
|
|
{
|
|
gNB_RRC_INST *rrc = RC.nrrrc[ctxt_pP->module_id];
|
|
int i;
|
|
/* preset nia0 as fallback */
|
|
int ret = 0;
|
|
|
|
/* Select integrity algorithm based on gNB configuration file and
|
|
* UE's supported algorithms.
|
|
* We take the first from the list that is supported by the UE.
|
|
* The ordering of the list comes from the configuration file.
|
|
*/
|
|
for (i = 0; i < rrc->security.integrity_algorithms_count; i++) {
|
|
int nia_mask[4] = {
|
|
0,
|
|
NGAP_INTEGRITY_NIA1_MASK,
|
|
NGAP_INTEGRITY_NIA2_MASK,
|
|
NGAP_INTEGRITY_NIA3_MASK
|
|
};
|
|
if (rrc->security.integrity_algorithms[i] == 0) {
|
|
/* nia0 */
|
|
break;
|
|
}
|
|
if (algorithms & nia_mask[rrc->security.integrity_algorithms[i]]) {
|
|
ret = rrc->security.integrity_algorithms[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
LOG_D(RRC, "selecting integrity algorithm %d\n", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int rrc_gNB_process_security(const protocol_ctxt_t *const ctxt_pP, rrc_gNB_ue_context_t *const ue_context_pP, ngap_security_capabilities_t *security_capabilities_pP)
|
|
{
|
|
bool changed = false;
|
|
NR_CipheringAlgorithm_t cipheringAlgorithm;
|
|
e_NR_IntegrityProtAlgorithm integrityProtAlgorithm;
|
|
gNB_RRC_UE_t *UE = &ue_context_pP->ue_context;
|
|
|
|
/* Save security parameters */
|
|
UE->security_capabilities = *security_capabilities_pP;
|
|
// translation
|
|
LOG_D(NR_RRC,
|
|
"[gNB %d] NAS security_capabilities.encryption_algorithms %u AS ciphering_algorithm %lu NAS security_capabilities.integrity_algorithms %u AS integrity_algorithm %u\n",
|
|
ctxt_pP->module_id,
|
|
UE->security_capabilities.nRencryption_algorithms,
|
|
(unsigned long)UE->ciphering_algorithm,
|
|
UE->security_capabilities.nRintegrity_algorithms,
|
|
UE->integrity_algorithm);
|
|
/* Select relevant algorithms */
|
|
cipheringAlgorithm = rrc_gNB_select_ciphering(ctxt_pP, UE->security_capabilities.nRencryption_algorithms);
|
|
|
|
if (UE->ciphering_algorithm != cipheringAlgorithm) {
|
|
UE->ciphering_algorithm = cipheringAlgorithm;
|
|
changed = true;
|
|
}
|
|
|
|
integrityProtAlgorithm = rrc_gNB_select_integrity(ctxt_pP, UE->security_capabilities.nRintegrity_algorithms);
|
|
|
|
if (UE->integrity_algorithm != integrityProtAlgorithm) {
|
|
UE->integrity_algorithm = integrityProtAlgorithm;
|
|
changed = true;
|
|
}
|
|
|
|
LOG_I(NR_RRC,
|
|
"[gNB %d][UE %d] Selected security algorithms (%p): ciphering %lx, integrity %x (algorithms %s)\n",
|
|
ctxt_pP->module_id,
|
|
UE->rrc_ue_id,
|
|
security_capabilities_pP,
|
|
cipheringAlgorithm,
|
|
integrityProtAlgorithm,
|
|
changed ? "changed" : "are the same");
|
|
return changed;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int rrc_gNB_process_NGAP_DOWNLINK_NAS(MessageDef *msg_p, instance_t instance, mui_t *rrc_gNB_mui)
|
|
//------------------------------------------------------------------------------
|
|
{
|
|
uint32_t length;
|
|
uint8_t buffer[4096];
|
|
protocol_ctxt_t ctxt = {0};
|
|
ngap_downlink_nas_t *req = &NGAP_DOWNLINK_NAS(msg_p);
|
|
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(RC.nrrrc[instance], req->gNB_ue_ngap_id);
|
|
|
|
if (ue_context_p == NULL) {
|
|
/* Can not associate this message to an UE index, send a failure to NGAP and discard it! */
|
|
MessageDef *msg_fail_p;
|
|
LOG_W(NR_RRC, "[gNB %ld] In NGAP_DOWNLINK_NAS: unknown UE from NGAP ids (%u)\n", instance, req->gNB_ue_ngap_id);
|
|
msg_fail_p = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_NAS_NON_DELIVERY_IND);
|
|
ngap_nas_non_delivery_ind_t *msg = &NGAP_NAS_NON_DELIVERY_IND(msg_fail_p);
|
|
msg->gNB_ue_ngap_id = req->gNB_ue_ngap_id;
|
|
msg->nas_pdu.length = req->nas_pdu.length;
|
|
msg->nas_pdu.buffer = req->nas_pdu.buffer;
|
|
// TODO add failure cause when defined!
|
|
itti_send_msg_to_task(TASK_NGAP, instance, msg_fail_p);
|
|
return (-1);
|
|
}
|
|
|
|
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
|
|
PROTOCOL_CTXT_SET_BY_INSTANCE(&ctxt, instance, GNB_FLAG_YES, UE->rrc_ue_id, 0, 0);
|
|
|
|
/* Create message for PDCP (DLInformationTransfer_t) */
|
|
length = do_NR_DLInformationTransfer(buffer,
|
|
sizeof(buffer),
|
|
rrc_gNB_get_next_transaction_identifier(instance),
|
|
req->nas_pdu.length,
|
|
req->nas_pdu.buffer);
|
|
LOG_DUMPMSG(NR_RRC, DEBUG_RRC, buffer, length, "[MSG] RRC DL Information Transfer\n");
|
|
/*
|
|
* switch UL or DL NAS message without RRC piggybacked to SRB2 if active.
|
|
*/
|
|
AssertFatal(!NODE_IS_DU(RC.nrrrc[ctxt.module_id]->node_type), "illegal node type DU: receiving NGAP messages at this node\n");
|
|
/* Transfer data to PDCP */
|
|
rb_id_t srb_id = UE->Srb[2].Active ? DCCH1 : DCCH;
|
|
nr_rrc_transfer_protected_rrc_message(RC.nrrrc[instance], UE, srb_id, buffer, length);
|
|
return 0;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void
|
|
rrc_gNB_send_NGAP_UPLINK_NAS(
|
|
const protocol_ctxt_t *const ctxt_pP,
|
|
rrc_gNB_ue_context_t *const ue_context_pP,
|
|
NR_UL_DCCH_Message_t *const ul_dcch_msg
|
|
)
|
|
//------------------------------------------------------------------------------
|
|
{
|
|
MessageDef *msg_p;
|
|
NR_ULInformationTransfer_t *ulInformationTransfer = ul_dcch_msg->message.choice.c1->choice.ulInformationTransfer;
|
|
gNB_RRC_UE_t *UE = &ue_context_pP->ue_context;
|
|
|
|
if (ulInformationTransfer->criticalExtensions.present == NR_ULInformationTransfer__criticalExtensions_PR_ulInformationTransfer) {
|
|
NR_DedicatedNAS_Message_t *nas = ulInformationTransfer->criticalExtensions.choice.ulInformationTransfer->dedicatedNAS_Message;
|
|
uint8_t *buf = malloc(nas->size);
|
|
AssertFatal(buf != NULL, "out of memory\n");
|
|
memcpy(buf, nas->buf, nas->size);
|
|
msg_p = itti_alloc_new_message (TASK_RRC_GNB, 0, NGAP_UPLINK_NAS);
|
|
NGAP_UPLINK_NAS(msg_p).gNB_ue_ngap_id = UE->rrc_ue_id;
|
|
NGAP_UPLINK_NAS (msg_p).nas_pdu.length = nas->size;
|
|
NGAP_UPLINK_NAS (msg_p).nas_pdu.buffer = buf;
|
|
// extract_imsi(NGAP_UPLINK_NAS (msg_p).nas_pdu.buffer,
|
|
// NGAP_UPLINK_NAS (msg_p).nas_pdu.length,
|
|
// ue_context_pP);
|
|
itti_send_msg_to_task (TASK_NGAP, ctxt_pP->instance, msg_p);
|
|
LOG_D(NR_RRC,"Send RRC GNB UL Information Transfer \n");
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void
|
|
rrc_gNB_send_NGAP_PDUSESSION_SETUP_RESP(
|
|
const protocol_ctxt_t *const ctxt_pP,
|
|
rrc_gNB_ue_context_t *const ue_context_pP,
|
|
uint8_t xid
|
|
)
|
|
//------------------------------------------------------------------------------
|
|
{
|
|
MessageDef *msg_p;
|
|
int pdu_sessions_done = 0;
|
|
int pdu_sessions_failed = 0;
|
|
|
|
msg_p = itti_alloc_new_message (TASK_RRC_GNB, 0, NGAP_PDUSESSION_SETUP_RESP);
|
|
ngap_pdusession_setup_resp_t *resp = &NGAP_PDUSESSION_SETUP_RESP(msg_p);
|
|
gNB_RRC_UE_t *UE = &ue_context_pP->ue_context;
|
|
resp->gNB_ue_ngap_id = UE->rrc_ue_id;
|
|
|
|
for (int pdusession = 0; pdusession < UE->nb_of_pdusessions; pdusession++) {
|
|
rrc_pdu_session_param_t *session = &UE->pduSession[pdusession];
|
|
if (session->status == PDU_SESSION_STATUS_DONE) {
|
|
pdusession_setup_t *tmp = &resp->pdusessions[pdu_sessions_done];
|
|
tmp->pdusession_id = session->param.pdusession_id;
|
|
tmp->nb_of_qos_flow = session->param.nb_qos;
|
|
tmp->gtp_teid = session->param.gNB_teid_N3;
|
|
tmp->pdu_session_type = session->param.pdu_session_type;
|
|
tmp->gNB_addr.length = session->param.gNB_addr_N3.length;
|
|
memcpy(tmp->gNB_addr.buffer, session->param.gNB_addr_N3.buffer, tmp->gNB_addr.length);
|
|
for (int qos_flow_index = 0; qos_flow_index < tmp->nb_of_qos_flow; qos_flow_index++) {
|
|
tmp->associated_qos_flows[qos_flow_index].qfi = session->param.qos[qos_flow_index].qfi;
|
|
tmp->associated_qos_flows[qos_flow_index].qos_flow_mapping_ind = QOSFLOW_MAPPING_INDICATION_DL;
|
|
}
|
|
|
|
session->status = PDU_SESSION_STATUS_ESTABLISHED;
|
|
LOG_I(NR_RRC,
|
|
"msg index %d, pdu_sessions index %d, status %d, xid %d): nb_of_pdusessions %d, pdusession_id %d, teid: %u \n ",
|
|
pdu_sessions_done,
|
|
pdusession,
|
|
session->status,
|
|
xid,
|
|
UE->nb_of_pdusessions,
|
|
tmp->pdusession_id,
|
|
tmp->gtp_teid);
|
|
pdu_sessions_done++;
|
|
} else if (session->status != PDU_SESSION_STATUS_ESTABLISHED) {
|
|
session->status = PDU_SESSION_STATUS_FAILED;
|
|
pdusession_failed_t *fail = &resp->pdusessions_failed[pdu_sessions_failed];
|
|
fail->pdusession_id = session->param.pdusession_id;
|
|
fail->cause = NGAP_CAUSE_RADIO_NETWORK;
|
|
fail->cause_value = NGAP_CauseRadioNetwork_unknown_PDU_session_ID;
|
|
pdu_sessions_failed++;
|
|
}
|
|
resp->nb_of_pdusessions = pdu_sessions_done;
|
|
resp->nb_of_pdusessions_failed = pdu_sessions_failed;
|
|
// } else {
|
|
// LOG_D(NR_RRC,"xid does not corresponds (context pdu_sessions index %d, status %d, xid %d/%d) \n ",
|
|
// pdusession, UE->pdusession[pdusession].status, xid, UE->pdusession[pdusession].xid);
|
|
// }
|
|
}
|
|
|
|
if ((pdu_sessions_done > 0 || pdu_sessions_failed)) {
|
|
LOG_I(NR_RRC, "NGAP_PDUSESSION_SETUP_RESP: sending the message\n");
|
|
itti_send_msg_to_task (TASK_NGAP, ctxt_pP->instance, msg_p);
|
|
}
|
|
|
|
for(int i = 0; i < NB_RB_MAX; i++) {
|
|
UE->pduSession[i].xid = -1;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ(MessageDef *msg_p, instance_t instance)
|
|
//------------------------------------------------------------------------------
|
|
{
|
|
protocol_ctxt_t ctxt={0};
|
|
|
|
ngap_pdusession_setup_req_t* msg=&NGAP_PDUSESSION_SETUP_REQ(msg_p);
|
|
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(RC.nrrrc[instance], msg->gNB_ue_ngap_id);
|
|
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
|
|
PROTOCOL_CTXT_SET_BY_MODULE_ID(&ctxt, 0, GNB_FLAG_YES, UE->rnti, 0, 0, 0);
|
|
gNB_RRC_INST *rrc = RC.nrrrc[ctxt.module_id];
|
|
LOG_I(NR_RRC, "[gNB %ld] gNB_ue_ngap_id %u \n", instance, msg->gNB_ue_ngap_id);
|
|
|
|
if (ue_context_p == NULL) {
|
|
MessageDef *msg_fail_p = NULL;
|
|
LOG_W(NR_RRC, "[gNB %ld] In NGAP_PDUSESSION_SETUP_REQ: unknown UE from NGAP ids (%u)\n", instance, msg->gNB_ue_ngap_id);
|
|
msg_fail_p = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_PDUSESSION_SETUP_REQUEST_FAIL);
|
|
NGAP_PDUSESSION_SETUP_FAIL(msg_fail_p).gNB_ue_ngap_id = msg->gNB_ue_ngap_id;
|
|
// TODO add failure cause when defined!
|
|
itti_send_msg_to_task (TASK_NGAP, instance, msg_fail_p);
|
|
return ;
|
|
}
|
|
|
|
AssertFatal(UE->rrc_ue_id == msg->gNB_ue_ngap_id, "logic bug\n");
|
|
UE->amf_ue_ngap_id = msg->amf_ue_ngap_id;
|
|
trigger_bearer_setup(rrc, UE, msg->nb_pdusessions_tosetup, msg->pdusession_setup_params, msg->ueAggMaxBitRateDownlink);
|
|
return;
|
|
}
|
|
|
|
static void fill_qos2(NGAP_QosFlowAddOrModifyRequestList_t *qos, pdusession_t *session)
|
|
{
|
|
// we need to duplicate the function fill_qos because all data types are slightly different
|
|
DevAssert(qos->list.count > 0);
|
|
DevAssert(qos->list.count <= NGAP_maxnoofQosFlows);
|
|
for (int qosIdx = 0; qosIdx < qos->list.count; qosIdx++) {
|
|
NGAP_QosFlowAddOrModifyRequestItem_t *qosFlowItem_p = qos->list.array[qosIdx];
|
|
// Set the QOS informations
|
|
session->qos[qosIdx].qfi = (uint8_t)qosFlowItem_p->qosFlowIdentifier;
|
|
NGAP_QosCharacteristics_t *qosChar = &qosFlowItem_p->qosFlowLevelQosParameters->qosCharacteristics;
|
|
AssertFatal(qosChar, "Qos characteristics are not available for qos flow index %d\n", qosIdx);
|
|
if (qosChar->present == NGAP_QosCharacteristics_PR_nonDynamic5QI) {
|
|
AssertFatal(qosChar->choice.dynamic5QI, "Non-Dynamic 5QI is NULL\n");
|
|
session->qos[qosIdx].fiveQI_type = non_dynamic;
|
|
session->qos[qosIdx].fiveQI = (uint64_t)qosChar->choice.nonDynamic5QI->fiveQI;
|
|
} else {
|
|
AssertFatal(qosChar->choice.dynamic5QI, "Dynamic 5QI is NULL\n");
|
|
session->qos[qosIdx].fiveQI_type = dynamic;
|
|
session->qos[qosIdx].fiveQI = (uint64_t)(*qosChar->choice.dynamic5QI->fiveQI);
|
|
}
|
|
|
|
ngap_allocation_retention_priority_t *tmp = &session->qos[qosIdx].allocation_retention_priority;
|
|
NGAP_AllocationAndRetentionPriority_t *tmp2 = &qosFlowItem_p->qosFlowLevelQosParameters->allocationAndRetentionPriority;
|
|
tmp->priority_level = tmp2->priorityLevelARP;
|
|
tmp->pre_emp_capability = tmp2->pre_emptionCapability;
|
|
tmp->pre_emp_vulnerability = tmp2->pre_emptionVulnerability;
|
|
}
|
|
session->nb_qos = qos->list.count;
|
|
}
|
|
|
|
static void decodePDUSessionResourceModify(pdusession_t *param, const ngap_pdu_t pdu)
|
|
{
|
|
NGAP_PDUSessionResourceModifyRequestTransfer_t *pdusessionTransfer = NULL;
|
|
asn_dec_rval_t dec_rval = aper_decode(NULL, &asn_DEF_NGAP_PDUSessionResourceModifyRequestTransfer, (void **)&pdusessionTransfer, pdu.buffer, pdu.length, 0, 0);
|
|
|
|
if (dec_rval.code != RC_OK) {
|
|
LOG_E(NR_RRC, "could not decode PDUSessionResourceModifyRequestTransfer\n");
|
|
return;
|
|
}
|
|
|
|
for (int j = 0; j < pdusessionTransfer->protocolIEs.list.count; j++) {
|
|
NGAP_PDUSessionResourceModifyRequestTransferIEs_t *pdusessionTransfer_ies = pdusessionTransfer->protocolIEs.list.array[j];
|
|
switch (pdusessionTransfer_ies->id) {
|
|
/* optional PDUSessionAggregateMaximumBitRate */
|
|
case NGAP_ProtocolIE_ID_id_PDUSessionAggregateMaximumBitRate:
|
|
// TODO
|
|
LOG_E(NR_RRC, "Cant' handle NGAP_ProtocolIE_ID_id_PDUSessionAggregateMaximumBitRate\n");
|
|
break;
|
|
|
|
/* optional UL-NGU-UP-TNLModifyList */
|
|
case NGAP_ProtocolIE_ID_id_UL_NGU_UP_TNLModifyList:
|
|
// TODO
|
|
LOG_E(NR_RRC, "Cant' handle NGAP_ProtocolIE_ID_id_UL_NGU_UP_TNLModifyList\n");
|
|
break;
|
|
|
|
/* optional NetworkInstance */
|
|
case NGAP_ProtocolIE_ID_id_NetworkInstance:
|
|
// TODO
|
|
LOG_E(NR_RRC, "Cant' handle NGAP_ProtocolIE_ID_id_NetworkInstance\n");
|
|
break;
|
|
|
|
/* optional QosFlowAddOrModifyRequestList */
|
|
case NGAP_ProtocolIE_ID_id_QosFlowAddOrModifyRequestList:
|
|
fill_qos2(&pdusessionTransfer_ies->value.choice.QosFlowAddOrModifyRequestList, param);
|
|
break;
|
|
|
|
/* optional QosFlowToReleaseList */
|
|
case NGAP_ProtocolIE_ID_id_QosFlowToReleaseList:
|
|
// TODO
|
|
LOG_E(NR_RRC, "Can't handle NGAP_ProtocolIE_ID_id_QosFlowToReleaseList\n");
|
|
break;
|
|
|
|
/* optional AdditionalUL-NGU-UP-TNLInformation */
|
|
case NGAP_ProtocolIE_ID_id_AdditionalUL_NGU_UP_TNLInformation:
|
|
// TODO
|
|
LOG_E(NR_RRC, "Cant' handle NGAP_ProtocolIE_ID_id_AdditionalUL_NGU_UP_TNLInformation\n");
|
|
break;
|
|
|
|
/* optional CommonNetworkInstance */
|
|
case NGAP_ProtocolIE_ID_id_CommonNetworkInstance:
|
|
// TODO
|
|
LOG_E(NR_RRC, "Cant' handle NGAP_ProtocolIE_ID_id_CommonNetworkInstance\n");
|
|
break;
|
|
|
|
default:
|
|
LOG_E(NR_RRC, "could not found protocolIEs id %ld\n", pdusessionTransfer_ies->id);
|
|
return;
|
|
}
|
|
}
|
|
ASN_STRUCT_FREE(asn_DEF_NGAP_PDUSessionResourceModifyRequestTransfer,pdusessionTransfer );
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int rrc_gNB_process_NGAP_PDUSESSION_MODIFY_REQ(MessageDef *msg_p, instance_t instance)
|
|
//------------------------------------------------------------------------------
|
|
{
|
|
rrc_gNB_ue_context_t *ue_context_p = NULL;
|
|
|
|
protocol_ctxt_t ctxt;
|
|
ngap_pdusession_modify_req_t *req = &NGAP_PDUSESSION_MODIFY_REQ(msg_p);
|
|
|
|
ue_context_p = rrc_gNB_get_ue_context(RC.nrrrc[instance], req->gNB_ue_ngap_id);
|
|
if (ue_context_p == NULL) {
|
|
LOG_W(NR_RRC, "[gNB %ld] In NGAP_PDUSESSION_MODIFY_REQ: unknown UE from NGAP ids (%u)\n", instance, req->gNB_ue_ngap_id);
|
|
// TO implement return setup failed
|
|
return (-1);
|
|
}
|
|
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
|
|
PROTOCOL_CTXT_SET_BY_INSTANCE(&ctxt, instance, GNB_FLAG_YES, UE->rrc_ue_id, 0, 0);
|
|
ctxt.eNB_index = 0;
|
|
bool all_failed = true;
|
|
for (int i = 0; i < req->nb_pdusessions_tomodify; i++) {
|
|
rrc_pdu_session_param_t *sess;
|
|
const pdusession_t *sessMod = req->pdusession_modify_params + i;
|
|
for (sess = UE->pduSession; sess < UE->pduSession + UE->nb_of_pdusessions; sess++)
|
|
if (sess->param.pdusession_id == sessMod->pdusession_id)
|
|
break;
|
|
if (sess == UE->pduSession + UE->nb_of_pdusessions) {
|
|
LOG_W(NR_RRC, "Requested modification of non-existing PDU session, refusing modification\n");
|
|
UE->nb_of_pdusessions++;
|
|
sess->status = PDU_SESSION_STATUS_FAILED;
|
|
sess->param.pdusession_id = sessMod->pdusession_id;
|
|
sess->cause = NGAP_CAUSE_RADIO_NETWORK;
|
|
UE->pduSession[i].cause_value = NGAP_CauseRadioNetwork_unknown_PDU_session_ID;
|
|
} else {
|
|
all_failed = false;
|
|
sess->status = PDU_SESSION_STATUS_NEW;
|
|
sess->param.pdusession_id = sessMod->pdusession_id;
|
|
sess->cause = NGAP_CAUSE_RADIO_NETWORK;
|
|
sess->cause_value = NGAP_CauseRadioNetwork_multiple_PDU_session_ID_instances;
|
|
sess->status = PDU_SESSION_STATUS_NEW;
|
|
sess->param.pdusession_id = sessMod->pdusession_id;
|
|
sess->cause = NGAP_CAUSE_NOTHING;
|
|
if (sessMod->nas_pdu.buffer != NULL) {
|
|
UE->pduSession[i].param.nas_pdu = sessMod->nas_pdu;
|
|
}
|
|
// Save new pdu session parameters, qos, upf addr, teid
|
|
decodePDUSessionResourceModify(&sess->param, UE->pduSession[i].param.pdusessionTransfer);
|
|
sess->param.UPF_addr_N3 = sessMod->upf_addr;
|
|
sess->param.UPF_teid_N3 = sessMod->gtp_teid;
|
|
}
|
|
}
|
|
|
|
if (!all_failed) {
|
|
LOG_D(NR_RRC, "generate RRCReconfiguration \n");
|
|
rrc_gNB_modify_dedicatedRRCReconfiguration(&ctxt, ue_context_p);
|
|
} else {
|
|
LOG_I(NR_RRC,
|
|
"pdu session modify failed, fill NGAP_PDUSESSION_MODIFY_RESP with the pdu session information that failed to modify \n");
|
|
MessageDef *msg_fail_p = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_PDUSESSION_MODIFY_RESP);
|
|
if (msg_fail_p == NULL) {
|
|
LOG_E(NR_RRC, "itti_alloc_new_message failed, msg_fail_p is NULL \n");
|
|
return (-1);
|
|
}
|
|
ngap_pdusession_modify_resp_t *msg = &NGAP_PDUSESSION_MODIFY_RESP(msg_fail_p);
|
|
msg->gNB_ue_ngap_id = req->gNB_ue_ngap_id;
|
|
msg->nb_of_pdusessions = 0;
|
|
|
|
for (int i = 0; i < UE->nb_of_pdusessions; i++) {
|
|
if (UE->pduSession[i].status == PDU_SESSION_STATUS_FAILED) {
|
|
msg->pdusessions_failed[i].pdusession_id = UE->pduSession[i].param.pdusession_id;
|
|
msg->pdusessions_failed[i].cause = UE->pduSession[i].cause;
|
|
msg->pdusessions_failed[i].cause_value = UE->pduSession[i].cause_value;
|
|
}
|
|
}
|
|
itti_send_msg_to_task(TASK_NGAP, instance, msg_fail_p);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int
|
|
rrc_gNB_send_NGAP_PDUSESSION_MODIFY_RESP(
|
|
const protocol_ctxt_t *const ctxt_pP,
|
|
rrc_gNB_ue_context_t *const ue_context_pP,
|
|
uint8_t xid
|
|
)
|
|
//------------------------------------------------------------------------------
|
|
{
|
|
MessageDef *msg_p = NULL;
|
|
uint8_t pdu_sessions_failed = 0;
|
|
uint8_t pdu_sessions_done = 0;
|
|
gNB_RRC_UE_t *UE = &ue_context_pP->ue_context;
|
|
|
|
msg_p = itti_alloc_new_message (TASK_RRC_GNB, 0, NGAP_PDUSESSION_MODIFY_RESP);
|
|
if (msg_p == NULL) {
|
|
LOG_E(NR_RRC, "itti_alloc_new_message failed, msg_p is NULL \n");
|
|
return (-1);
|
|
}
|
|
ngap_pdusession_modify_resp_t *resp = &NGAP_PDUSESSION_MODIFY_RESP(msg_p);
|
|
LOG_I(NR_RRC, "send message NGAP_PDUSESSION_MODIFY_RESP \n");
|
|
|
|
resp->gNB_ue_ngap_id = UE->rrc_ue_id;
|
|
|
|
for (int i = 0; i < UE->nb_of_pdusessions; i++) {
|
|
if (xid != UE->pduSession[i].xid) {
|
|
LOG_W(NR_RRC, "xid does not correspond (context pdu session index %d, status %d, xid %d/%d) \n ", i, UE->pduSession[i].status, xid, UE->pduSession[i].xid);
|
|
continue;
|
|
}
|
|
if (UE->pduSession[i].status == PDU_SESSION_STATUS_DONE) {
|
|
rrc_pdu_session_param_t *pduSession = find_pduSession(UE, UE->pduSession[i].param.pdusession_id, false);
|
|
if (pduSession) {
|
|
LOG_I(NR_RRC, "update pdu session %d \n", pduSession->param.pdusession_id);
|
|
// Update UE->pduSession
|
|
pduSession->status = PDU_SESSION_STATUS_ESTABLISHED;
|
|
pduSession->cause = NGAP_CAUSE_NOTHING;
|
|
for (int qos_flow_index = 0; qos_flow_index < UE->pduSession[i].param.nb_qos; qos_flow_index++) {
|
|
pduSession->param.qos[qos_flow_index] = UE->pduSession[i].param.qos[qos_flow_index];
|
|
}
|
|
resp->pdusessions[pdu_sessions_done].pdusession_id = UE->pduSession[i].param.pdusession_id;
|
|
for (int qos_flow_index = 0; qos_flow_index < UE->pduSession[i].param.nb_qos; qos_flow_index++) {
|
|
resp->pdusessions[pdu_sessions_done].qos[qos_flow_index].qfi = UE->pduSession[i].param.qos[qos_flow_index].qfi;
|
|
}
|
|
resp->pdusessions[pdu_sessions_done].pdusession_id = UE->pduSession[i].param.pdusession_id;
|
|
resp->pdusessions[pdu_sessions_done].nb_of_qos_flow = UE->pduSession[i].param.nb_qos;
|
|
LOG_I(NR_RRC,
|
|
"Modify Resp (msg index %d, pdu session index %d, status %d, xid %d): nb_of_pduSessions %d, pdusession_id %d \n ",
|
|
pdu_sessions_done,
|
|
i,
|
|
UE->pduSession[i].status,
|
|
xid,
|
|
UE->nb_of_pdusessions,
|
|
resp->pdusessions[pdu_sessions_done].pdusession_id);
|
|
pdu_sessions_done++;
|
|
} else {
|
|
LOG_W(NR_RRC, "PDU SESSION modify of a not existing pdu session %d \n", UE->pduSession[i].param.pdusession_id);
|
|
resp->pdusessions_failed[pdu_sessions_failed].pdusession_id = UE->pduSession[i].param.pdusession_id;
|
|
resp->pdusessions_failed[pdu_sessions_failed].cause = NGAP_CAUSE_RADIO_NETWORK;
|
|
resp->pdusessions_failed[pdu_sessions_failed].cause_value = NGAP_CauseRadioNetwork_unknown_PDU_session_ID;
|
|
pdu_sessions_failed++;
|
|
}
|
|
} else if ((UE->pduSession[i].status == PDU_SESSION_STATUS_NEW) || (UE->pduSession[i].status == PDU_SESSION_STATUS_ESTABLISHED)) {
|
|
LOG_D(NR_RRC, "PDU SESSION is NEW or already ESTABLISHED\n");
|
|
} else if (UE->pduSession[i].status == PDU_SESSION_STATUS_FAILED) {
|
|
resp->pdusessions_failed[pdu_sessions_failed].pdusession_id = UE->pduSession[i].param.pdusession_id;
|
|
resp->pdusessions_failed[pdu_sessions_failed].cause = UE->pduSession[i].cause;
|
|
resp->pdusessions_failed[pdu_sessions_failed].cause_value = UE->pduSession[i].cause_value;
|
|
pdu_sessions_failed++;
|
|
}
|
|
else
|
|
LOG_W(NR_RRC,
|
|
"Modify pdu session %d, unknown state %d \n ",
|
|
UE->pduSession[i].param.pdusession_id,
|
|
UE->pduSession[i].status);
|
|
}
|
|
|
|
resp->nb_of_pdusessions = pdu_sessions_done;
|
|
resp->nb_of_pdusessions_failed = pdu_sessions_failed;
|
|
|
|
if (pdu_sessions_done > 0 || pdu_sessions_failed > 0) {
|
|
LOG_D(NR_RRC, "NGAP_PDUSESSION_MODIFY_RESP: sending the message (total pdu session %d)\n", UE->nb_of_pdusessions);
|
|
itti_send_msg_to_task (TASK_NGAP, ctxt_pP->instance, msg_p);
|
|
} else {
|
|
itti_free (ITTI_MSG_ORIGIN_ID(msg_p), msg_p);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_REQ(const module_id_t gnb_mod_idP, const rrc_gNB_ue_context_t *const ue_context_pP, const ngap_Cause_t causeP, const long cause_valueP)
|
|
//------------------------------------------------------------------------------
|
|
{
|
|
if (ue_context_pP == NULL) {
|
|
LOG_E(RRC, "[gNB] In NGAP_UE_CONTEXT_RELEASE_REQ: invalid UE\n");
|
|
} else {
|
|
const gNB_RRC_UE_t *UE = &ue_context_pP->ue_context;
|
|
MessageDef *msg = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_UE_CONTEXT_RELEASE_REQ);
|
|
ngap_ue_release_req_t *req = &NGAP_UE_CONTEXT_RELEASE_REQ(msg);
|
|
memset(req, 0, sizeof(*req));
|
|
req->gNB_ue_ngap_id = UE->rrc_ue_id;
|
|
req->cause = causeP;
|
|
req->cause_value = cause_valueP;
|
|
for (int i = 0; i < UE->nb_of_pdusessions; i++) {
|
|
req->pdusessions[i].pdusession_id = UE->pduSession[i].param.pdusession_id;
|
|
req->nb_of_pdusessions++;
|
|
}
|
|
itti_send_msg_to_task(TASK_NGAP, GNB_MODULE_ID_TO_INSTANCE(gnb_mod_idP), msg);
|
|
}
|
|
}
|
|
/*------------------------------------------------------------------------------*/
|
|
int rrc_gNB_process_NGAP_UE_CONTEXT_RELEASE_REQ(MessageDef *msg_p, instance_t instance)
|
|
{
|
|
uint32_t gNB_ue_ngap_id;
|
|
gNB_ue_ngap_id = NGAP_UE_CONTEXT_RELEASE_REQ(msg_p).gNB_ue_ngap_id;
|
|
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(RC.nrrrc[instance], gNB_ue_ngap_id);
|
|
|
|
if (ue_context_p == NULL) {
|
|
/* Can not associate this message to an UE index, send a failure to ngAP and discard it! */
|
|
MessageDef *msg_fail_p;
|
|
LOG_W(RRC, "[gNB %ld] In NGAP_UE_CONTEXT_RELEASE_REQ: unknown UE from gNB_ue_ngap_id (%u)\n",
|
|
instance,
|
|
gNB_ue_ngap_id);
|
|
msg_fail_p = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_UE_CONTEXT_RELEASE_RESP); /* TODO change message ID. */
|
|
NGAP_UE_CONTEXT_RELEASE_RESP(msg_fail_p).gNB_ue_ngap_id = gNB_ue_ngap_id;
|
|
// TODO add failure cause when defined!
|
|
itti_send_msg_to_task(TASK_NGAP, instance, msg_fail_p);
|
|
return (-1);
|
|
} else {
|
|
|
|
/* Send the response */
|
|
MessageDef *msg_resp_p;
|
|
msg_resp_p = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_UE_CONTEXT_RELEASE_RESP);
|
|
NGAP_UE_CONTEXT_RELEASE_RESP(msg_resp_p).gNB_ue_ngap_id = gNB_ue_ngap_id;
|
|
itti_send_msg_to_task(TASK_NGAP, instance, msg_resp_p);
|
|
return (0);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/*
|
|
* Process the NG command NGAP_UE_CONTEXT_RELEASE_COMMAND, sent by AMF.
|
|
* The gNB should remove all pdu session, NG context, and other context of the UE.
|
|
*/
|
|
int rrc_gNB_process_NGAP_UE_CONTEXT_RELEASE_COMMAND(MessageDef *msg_p, instance_t instance)
|
|
{
|
|
gNB_RRC_INST *rrc = RC.nrrrc[0];
|
|
uint32_t gNB_ue_ngap_id = 0;
|
|
protocol_ctxt_t ctxt;
|
|
gNB_ue_ngap_id = NGAP_UE_CONTEXT_RELEASE_COMMAND(msg_p).gNB_ue_ngap_id;
|
|
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(RC.nrrrc[instance], gNB_ue_ngap_id);
|
|
|
|
if (ue_context_p == NULL) {
|
|
/* Can not associate this message to an UE index */
|
|
MessageDef *msg_complete_p = NULL;
|
|
LOG_W(NR_RRC, "[gNB %ld] In NGAP_UE_CONTEXT_RELEASE_COMMAND: unknown UE from gNB_ue_ngap_id (%u)\n",
|
|
instance,
|
|
gNB_ue_ngap_id);
|
|
msg_complete_p = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_UE_CONTEXT_RELEASE_COMPLETE);
|
|
NGAP_UE_CONTEXT_RELEASE_COMPLETE(msg_complete_p).gNB_ue_ngap_id = gNB_ue_ngap_id;
|
|
itti_send_msg_to_task(TASK_NGAP, instance, msg_complete_p);
|
|
return -1;
|
|
}
|
|
|
|
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
|
|
#ifdef E2_AGENT
|
|
signal_rrc_state_changed_to(UE, RC_SM_RRC_IDLE);
|
|
#endif
|
|
|
|
/* a UE might not be associated to a CU-UP if it never requested a PDU
|
|
* session (intentionally, or because of erros) */
|
|
if (ue_associated_to_cuup(rrc, UE)) {
|
|
sctp_assoc_t assoc_id = get_existing_cuup_for_ue(rrc, UE);
|
|
e1ap_bearer_release_cmd_t cmd = {
|
|
.gNB_cu_cp_ue_id = UE->rrc_ue_id,
|
|
.gNB_cu_up_ue_id = UE->rrc_ue_id,
|
|
};
|
|
rrc->cucp_cuup.bearer_context_release(assoc_id, &cmd);
|
|
}
|
|
|
|
/* special case: the DU might be offline, in which case the f1_ue_data exists
|
|
* but is set to 0 */
|
|
if (cu_exists_f1_ue_data(UE->rrc_ue_id) && cu_get_f1_ue_data(UE->rrc_ue_id).du_assoc_id != 0) {
|
|
PROTOCOL_CTXT_SET_BY_INSTANCE(&ctxt, instance, GNB_FLAG_YES, UE->rrc_ue_id, 0, 0);
|
|
ctxt.eNB_index = 0;
|
|
rrc_gNB_generate_RRCRelease(&ctxt, ue_context_p);
|
|
|
|
/* UE will be freed after UE context release complete */
|
|
} else {
|
|
// the DU is offline already
|
|
rrc_remove_ue(rrc, ue_context_p);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_COMPLETE(
|
|
instance_t instance,
|
|
uint32_t gNB_ue_ngap_id) {
|
|
MessageDef *msg = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_UE_CONTEXT_RELEASE_COMPLETE);
|
|
NGAP_UE_CONTEXT_RELEASE_COMPLETE(msg).gNB_ue_ngap_id = gNB_ue_ngap_id;
|
|
itti_send_msg_to_task(TASK_NGAP, instance, msg);
|
|
}
|
|
|
|
void rrc_gNB_send_NGAP_UE_CAPABILITIES_IND(const protocol_ctxt_t *const ctxt_pP,
|
|
rrc_gNB_ue_context_t *const ue_context_pP,
|
|
const NR_UECapabilityInformation_t *const ue_cap_info)
|
|
//------------------------------------------------------------------------------
|
|
{
|
|
NR_UE_CapabilityRAT_ContainerList_t *ueCapabilityRATContainerList =
|
|
ue_cap_info->criticalExtensions.choice.ueCapabilityInformation->ue_CapabilityRAT_ContainerList;
|
|
void *buf;
|
|
NR_UERadioAccessCapabilityInformation_t rac = {0};
|
|
gNB_RRC_UE_t *UE = &ue_context_pP->ue_context;
|
|
|
|
if (ueCapabilityRATContainerList->list.count == 0) {
|
|
LOG_W(RRC, "[gNB %d][UE %x] bad UE capabilities\n", ctxt_pP->module_id, UE->rnti);
|
|
}
|
|
|
|
int ret = uper_encode_to_new_buffer(&asn_DEF_NR_UE_CapabilityRAT_ContainerList, NULL, ueCapabilityRATContainerList, &buf);
|
|
AssertFatal(ret > 0, "fail to encode ue capabilities\n");
|
|
|
|
rac.criticalExtensions.present = NR_UERadioAccessCapabilityInformation__criticalExtensions_PR_c1;
|
|
asn1cCalloc(rac.criticalExtensions.choice.c1, c1);
|
|
c1->present = NR_UERadioAccessCapabilityInformation__criticalExtensions__c1_PR_ueRadioAccessCapabilityInformation;
|
|
asn1cCalloc(c1->choice.ueRadioAccessCapabilityInformation, info);
|
|
info->ue_RadioAccessCapabilityInfo.buf = buf;
|
|
info->ue_RadioAccessCapabilityInfo.size = ret;
|
|
info->nonCriticalExtension = NULL;
|
|
/* 8192 is arbitrary, should be big enough */
|
|
void *buf2 = NULL;
|
|
int encoded = uper_encode_to_new_buffer(&asn_DEF_NR_UERadioAccessCapabilityInformation, NULL, &rac, &buf2);
|
|
|
|
AssertFatal(encoded > 0, "fail to encode ue capabilities\n");
|
|
;
|
|
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NR_UERadioAccessCapabilityInformation, &rac);
|
|
MessageDef *msg_p;
|
|
msg_p = itti_alloc_new_message (TASK_RRC_GNB, 0, NGAP_UE_CAPABILITIES_IND);
|
|
ngap_ue_cap_info_ind_t *ind = &NGAP_UE_CAPABILITIES_IND(msg_p);
|
|
memset(ind, 0, sizeof(*ind));
|
|
ind->gNB_ue_ngap_id = UE->rrc_ue_id;
|
|
ind->ue_radio_cap.length = encoded;
|
|
ind->ue_radio_cap.buffer = buf2;
|
|
itti_send_msg_to_task (TASK_NGAP, ctxt_pP->instance, msg_p);
|
|
LOG_I(NR_RRC,"Send message to ngap: NGAP_UE_CAPABILITIES_IND\n");
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void
|
|
rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE(
|
|
const protocol_ctxt_t *const ctxt_pP,
|
|
rrc_gNB_ue_context_t *const ue_context_pP,
|
|
uint8_t xid
|
|
)
|
|
//------------------------------------------------------------------------------
|
|
{
|
|
int pdu_sessions_released = 0;
|
|
MessageDef *msg_p;
|
|
gNB_RRC_UE_t *UE = &ue_context_pP->ue_context;
|
|
msg_p = itti_alloc_new_message (TASK_RRC_GNB, 0, NGAP_PDUSESSION_RELEASE_RESPONSE);
|
|
ngap_pdusession_release_resp_t *resp = &NGAP_PDUSESSION_RELEASE_RESPONSE(msg_p);
|
|
memset(resp, 0, sizeof(*resp));
|
|
resp->gNB_ue_ngap_id = UE->rrc_ue_id;
|
|
|
|
for (int i = 0; i < UE->nb_of_pdusessions; i++) {
|
|
if (xid == UE->pduSession[i].xid) {
|
|
resp->pdusession_release[pdu_sessions_released].pdusession_id = UE->pduSession[i].param.pdusession_id;
|
|
pdu_sessions_released++;
|
|
//clear
|
|
memset(&UE->pduSession[i], 0, sizeof(*UE->pduSession));
|
|
UE->pduSession[i].status = PDU_SESSION_STATUS_RELEASED;
|
|
LOG_W(NR_RRC, "Released pdu session, but code to finish to free memory\n");
|
|
}
|
|
}
|
|
|
|
resp->nb_of_pdusessions_released = pdu_sessions_released;
|
|
resp->nb_of_pdusessions_failed = 0;
|
|
LOG_I(NR_RRC, "NGAP PDUSESSION RELEASE RESPONSE: rrc_ue_id %u release_pdu_sessions %d\n", resp->gNB_ue_ngap_id, pdu_sessions_released);
|
|
itti_send_msg_to_task (TASK_NGAP, ctxt_pP->instance, msg_p);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND(MessageDef *msg_p, instance_t instance)
|
|
//------------------------------------------------------------------------------
|
|
{
|
|
uint32_t gNB_ue_ngap_id;
|
|
protocol_ctxt_t ctxt;
|
|
ngap_pdusession_release_command_t *cmd = &NGAP_PDUSESSION_RELEASE_COMMAND(msg_p);
|
|
gNB_ue_ngap_id = cmd->gNB_ue_ngap_id;
|
|
if (cmd->nb_pdusessions_torelease > NGAP_MAX_PDUSESSION) {
|
|
LOG_E(NR_RRC, "incorrect number of pdu session do release %d\n", cmd->nb_pdusessions_torelease);
|
|
return -1;
|
|
}
|
|
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(RC.nrrrc[instance], gNB_ue_ngap_id);
|
|
|
|
if (!ue_context_p) {
|
|
LOG_E(NR_RRC, "[gNB %ld] not found ue context gNB_ue_ngap_id %u \n", instance, gNB_ue_ngap_id);
|
|
return -1;
|
|
}
|
|
|
|
LOG_I(NR_RRC, "[gNB %ld] gNB_ue_ngap_id %u \n", instance, gNB_ue_ngap_id);
|
|
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
|
|
PROTOCOL_CTXT_SET_BY_INSTANCE(&ctxt, instance, GNB_FLAG_YES, UE->rrc_ue_id, 0, 0);
|
|
LOG_I(
|
|
NR_RRC, "PDU Session Release Command: AMF_UE_NGAP_ID %lu rrc_ue_id %u release_pdusessions %d \n", cmd->amf_ue_ngap_id, gNB_ue_ngap_id, cmd->nb_pdusessions_torelease);
|
|
bool found = false;
|
|
uint8_t xid = rrc_gNB_get_next_transaction_identifier(ctxt.module_id);
|
|
UE->xids[xid] = RRC_PDUSESSION_RELEASE;
|
|
for (int pdusession = 0; pdusession < cmd->nb_pdusessions_torelease; pdusession++) {
|
|
rrc_pdu_session_param_t *pduSession = find_pduSession(UE, cmd->pdusession_release_params[pdusession].pdusession_id, false);
|
|
if (!pduSession) {
|
|
LOG_I(NR_RRC, "no pdusession_id, AMF requested to close it id=%d\n", cmd->pdusession_release_params[pdusession].pdusession_id);
|
|
int j=UE->nb_of_pdusessions++;
|
|
UE->pduSession[j].status = PDU_SESSION_STATUS_FAILED;
|
|
UE->pduSession[j].param.pdusession_id = cmd->pdusession_release_params[pdusession].pdusession_id;
|
|
UE->pduSession[j].cause = NGAP_CAUSE_RADIO_NETWORK;
|
|
UE->pduSession[j].cause_value = 30;
|
|
continue;
|
|
}
|
|
if (pduSession->status == PDU_SESSION_STATUS_FAILED) {
|
|
pduSession->xid = xid;
|
|
continue;
|
|
}
|
|
if (pduSession->status == PDU_SESSION_STATUS_ESTABLISHED) {
|
|
found = true;
|
|
LOG_I(NR_RRC, "RELEASE pdusession %d \n", pduSession->param.pdusession_id);
|
|
pduSession->status = PDU_SESSION_STATUS_TORELEASE;
|
|
pduSession->xid = xid;
|
|
}
|
|
}
|
|
|
|
if (found) {
|
|
// TODO RRCReconfiguration To UE
|
|
LOG_I(NR_RRC, "Send RRCReconfiguration To UE \n");
|
|
rrc_gNB_generate_dedicatedRRCReconfiguration_release(&ctxt, ue_context_p, xid, cmd->nas_pdu.length, cmd->nas_pdu.buffer);
|
|
} else {
|
|
// gtp tunnel delete
|
|
LOG_I(NR_RRC, "gtp tunnel delete all tunnels for UE %04x\n", UE->rnti);
|
|
gtpv1u_gnb_delete_tunnel_req_t req = {0};
|
|
req.ue_id = UE->rnti;
|
|
gtpv1u_delete_ngu_tunnel(instance, &req);
|
|
// NGAP_PDUSESSION_RELEASE_RESPONSE
|
|
rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE(&ctxt, ue_context_p, xid);
|
|
LOG_I(NR_RRC, "Send PDU Session Release Response \n");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void nr_rrc_rx_tx(void) {
|
|
// check timers
|
|
|
|
// check if UEs are lost, to remove them from upper layers
|
|
|
|
//
|
|
|
|
}
|
|
|
|
/*------------------------------------------------------------------------------*/
|
|
int rrc_gNB_process_PAGING_IND(MessageDef *msg_p, instance_t instance)
|
|
{
|
|
for (uint16_t tai_size = 0; tai_size < NGAP_PAGING_IND(msg_p).tai_size; tai_size++) {
|
|
LOG_I(NR_RRC,"[gNB %ld] In NGAP_PAGING_IND: MCC %d, MNC %d, TAC %d\n", instance, NGAP_PAGING_IND(msg_p).plmn_identity[tai_size].mcc,
|
|
NGAP_PAGING_IND(msg_p).plmn_identity[tai_size].mnc, NGAP_PAGING_IND(msg_p).tac[tai_size]);
|
|
|
|
for (uint8_t j = 0; j < RC.nrrrc[instance]->configuration.num_plmn; j++) {
|
|
if (RC.nrrrc[instance]->configuration.mcc[j] == NGAP_PAGING_IND(msg_p).plmn_identity[tai_size].mcc
|
|
&& RC.nrrrc[instance]->configuration.mnc[j] == NGAP_PAGING_IND(msg_p).plmn_identity[tai_size].mnc
|
|
&& RC.nrrrc[instance]->configuration.tac == NGAP_PAGING_IND(msg_p).tac[tai_size]) {
|
|
for (uint8_t CC_id = 0; CC_id < MAX_NUM_CCs; CC_id++) {
|
|
AssertFatal(false, "to be implemented properly\n");
|
|
if (NODE_IS_CU(RC.nrrrc[instance]->node_type)) {
|
|
MessageDef *m = itti_alloc_new_message(TASK_RRC_GNB, 0, F1AP_PAGING_IND);
|
|
F1AP_PAGING_IND(m).plmn.mcc = RC.nrrrc[j]->configuration.mcc[0];
|
|
F1AP_PAGING_IND(m).plmn.mnc = RC.nrrrc[j]->configuration.mnc[0];
|
|
F1AP_PAGING_IND(m).plmn.mnc_digit_length = RC.nrrrc[j]->configuration.mnc_digit_length[0];
|
|
F1AP_PAGING_IND (m).nr_cellid = RC.nrrrc[j]->nr_cellid;
|
|
F1AP_PAGING_IND (m).ueidentityindexvalue = (uint16_t)(NGAP_PAGING_IND(msg_p).ue_paging_identity.s_tmsi.m_tmsi%1024);
|
|
F1AP_PAGING_IND (m).fiveg_s_tmsi = NGAP_PAGING_IND(msg_p).ue_paging_identity.s_tmsi.m_tmsi;
|
|
F1AP_PAGING_IND (m).paging_drx = NGAP_PAGING_IND(msg_p).paging_drx;
|
|
LOG_E(F1AP, "ueidentityindexvalue %u fiveg_s_tmsi %ld paging_drx %u\n", F1AP_PAGING_IND (m).ueidentityindexvalue, F1AP_PAGING_IND (m).fiveg_s_tmsi, F1AP_PAGING_IND (m).paging_drx);
|
|
itti_send_msg_to_task(TASK_CU_F1, instance, m);
|
|
} else {
|
|
//rrc_gNB_generate_pcch_msg(NGAP_PAGING_IND(msg_p).ue_paging_identity.s_tmsi.m_tmsi,(uint8_t)NGAP_PAGING_IND(msg_p).paging_drx, instance, CC_id);
|
|
} // end of nodetype check
|
|
} // end of cc loop
|
|
} // end of mcc mnc check
|
|
} // end of num_plmn
|
|
} // end of tai size
|
|
|
|
return 0;
|
|
}
|