diff --git a/openair2/RRC/NR/nr_rrc_proto.h b/openair2/RRC/NR/nr_rrc_proto.h index ab2a95ecde..52009b2959 100644 --- a/openair2/RRC/NR/nr_rrc_proto.h +++ b/openair2/RRC/NR/nr_rrc_proto.h @@ -86,6 +86,7 @@ bool ue_associated_to_cuup(const gNB_RRC_INST *rrc, const gNB_RRC_UE_t *ue); sctp_assoc_t get_existing_cuup_for_ue(const gNB_RRC_INST *rrc, const gNB_RRC_UE_t *ue); sctp_assoc_t get_new_cuup_for_ue(const gNB_RRC_INST *rrc, const gNB_RRC_UE_t *ue, int sst, int sd); int rrc_gNB_process_e1_setup_req(sctp_assoc_t assoc_id, e1ap_setup_req_t *req); +bool is_cuup_associated(gNB_RRC_INST *rrc); /* Process indication of E1 connection loss on CU-CP */ void rrc_gNB_process_e1_lost_connection(gNB_RRC_INST *rrc, e1ap_lost_connection_t *lc, sctp_assoc_t assoc_id); @@ -104,7 +105,8 @@ void ue_cxt_mod_direct(MessageDef *msg, void prepare_and_send_ue_context_modification_f1(rrc_gNB_ue_context_t *ue_context_p, e1ap_bearer_setup_resp_t *e1ap_resp); -void trigger_bearer_setup(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, pdusession_t *sessions, uint64_t ueAggMaxBitRateDownlink); +bool trigger_bearer_setup(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, pdusession_t *sessions, uint64_t ueAggMaxBitRateDownlink) + __attribute__((warn_unused_result)); int rrc_gNB_generate_pcch_msg(sctp_assoc_t assoc_id, const NR_SIB1_t *sib, uint32_t tmsi, uint8_t paging_drx); diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index ba4ed17267..81fbf7fba1 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -1403,6 +1403,14 @@ static int handle_rrcReestablishmentComplete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE return 0; } +/** + * @brief Forward stored NAS PDU to UE (3GPP TS 38.413) + * - 8.2.1.2: If the NAS-PDU IE is included in the PDU SESSION RESOURCE SETUP REQUEST message, + * the NG-RAN node shall pass it to the UE. + * - 8.3.1.2: If the NAS-PDU IE is included in the INITIAL CONTEXT SETUP REQUEST message, + * the NG-RAN node shall pass it transparently towards the UE. + * - 8.6.2: The NAS-PDU IE contains an AMF–UE message that is transferred without interpretation in the NG-RAN node. + */ void rrc_forward_ue_nas_message(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE) { if (UE->nas_pdu.buffer == NULL || UE->nas_pdu.length == 0) @@ -1523,7 +1531,12 @@ static void handle_ueCapabilityInformation(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, * up security and request capabilities, so trigger PDU sessions now. The * UE NAS message will be forwarded in the corresponding reconfiguration, * the Initial context setup response after reconfiguration complete. */ - trigger_bearer_setup(rrc, UE, UE->n_initial_pdu, UE->initial_pdus, 0); + if (!trigger_bearer_setup(rrc, UE, UE->n_initial_pdu, UE->initial_pdus, 0)) { + LOG_W(NR_RRC, "Failed to setup bearers for UE %d: send Initial Context Setup Response\n", UE->rrc_ue_id); + rrc_gNB_send_NGAP_INITIAL_CONTEXT_SETUP_RESP(rrc, UE); + rrc_forward_ue_nas_message(rrc, UE); + return; + } } else { rrc_gNB_send_NGAP_INITIAL_CONTEXT_SETUP_RESP(rrc, UE); rrc_forward_ue_nas_message(rrc, UE); @@ -1735,7 +1748,11 @@ static int rrc_gNB_decode_dcch(gNB_RRC_INST *rrc, const f1ap_ul_rrc_message_t *m * to set up security, so trigger PDU sessions now. The UE NAS * message will be forwarded in the corresponding reconfiguration, * the Initial context setup response after reconfiguration complete. */ - trigger_bearer_setup(rrc, UE, UE->n_initial_pdu, UE->initial_pdus, 0); + if (!trigger_bearer_setup(rrc, UE, UE->n_initial_pdu, UE->initial_pdus, 0)) { + LOG_W(NR_RRC, "Failed to setup bearers for UE %d: send Initial Context Setup Response\n", UE->rrc_ue_id); + rrc_gNB_send_NGAP_INITIAL_CONTEXT_SETUP_RESP(rrc, UE); + rrc_forward_ue_nas_message(rrc, UE); + } } else { /* we already have capabilities, and no PDU sessions to setup, ack * this UE */ diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index 86f3e82740..08f83ea5c8 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -320,11 +320,29 @@ static int decodePDUSessionResourceSetup(pdusession_t *session) return 0; } -void trigger_bearer_setup(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, pdusession_t *sessions, uint64_t ueAggMaxBitRateDownlink) +/** + * @brief Triggers bearer setup for the specified UE. + * + * This function initiates the setup of bearer contexts for a given UE + * by preparing and sending an E1AP Bearer Setup Request message to the CU-UP. + * + * @return True if bearer setup was successfully initiated, false otherwise + * @retval true Bearer setup was initiated successfully + * @retval false No CU-UP is associated, so bearer setup could not be initiated + * + * @note the return value is expected to be used (as per declaration) + * + */ +bool trigger_bearer_setup(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, pdusession_t *sessions, uint64_t ueAggMaxBitRateDownlink) { AssertFatal(UE->as_security_active, "logic bug: security should be active when activating DRBs\n"); e1ap_bearer_setup_req_t bearer_req = {0}; + // Reject bearers setup if there's no CU-UP associated + if (!is_cuup_associated(rrc)) { + return false; + } + 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); @@ -419,17 +437,49 @@ void trigger_bearer_setup(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, pdusession * 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); + return true; +} + +/** + * @brief Fill PDU Session Resource Failed to Setup Item of the + * PDU Session Resource Failed to Setup List for either: + * - NGAP PDU Session Resource Setup Response + * - NGAP Initial Context Setup Response + */ +static void fill_pdu_session_resource_failed_to_setup_item(pdusession_failed_t *f, int pdusession_id, ngap_cause_t cause) +{ + f->pdusession_id = pdusession_id; + f->cause = cause; +} + +/** + * @brief Fill Initial Context Setup Response with a PDU Session Resource Failed to Setup List + * and send ITTI message to TASK_NGAP + */ +static void send_ngap_initial_context_setup_resp_fail(instance_t instance, + ngap_initial_context_setup_req_t *msg, + ngap_cause_t cause) +{ + MessageDef *msg_p = itti_alloc_new_message(TASK_RRC_GNB, instance, NGAP_INITIAL_CONTEXT_SETUP_RESP); + ngap_initial_context_setup_resp_t *resp = &NGAP_INITIAL_CONTEXT_SETUP_RESP(msg_p); + resp->gNB_ue_ngap_id = msg->gNB_ue_ngap_id; + + for (int i = 0; i < msg->nb_of_pdusessions; i++) { + fill_pdu_session_resource_failed_to_setup_item(&resp->pdusessions_failed[i], msg->pdusession_param[i].pdusession_id, cause); + } + resp->nb_of_pdusessions = 0; + resp->nb_of_pdusessions_failed = msg->nb_of_pdusessions; + itti_send_msg_to_task(TASK_NGAP, instance, msg_p); } //------------------------------------------------------------------------------ int rrc_gNB_process_NGAP_INITIAL_CONTEXT_SETUP_REQ(MessageDef *msg_p, instance_t instance) //------------------------------------------------------------------------------ { + gNB_RRC_INST *rrc = RC.nrrrc[instance]; 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; - + rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, 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! */ 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); @@ -439,7 +489,8 @@ int rrc_gNB_process_NGAP_INITIAL_CONTEXT_SETUP_REQ(MessageDef *msg_p, instance_t cause); return (-1); } - gNB_RRC_INST *rrc = RC.nrrrc[instance]; + gNB_RRC_UE_t *UE = &ue_context_p->ue_context; + UE->amf_ue_ngap_id = req->amf_ue_ngap_id; /* store guami in gNB_RRC_UE_t context; @@ -485,7 +536,13 @@ int rrc_gNB_process_NGAP_INITIAL_CONTEXT_SETUP_REQ(MessageDef *msg_p, instance_t // do not remove the above allocation which is reused here: this is used // in handle_rrcReconfigurationComplete() to know that we need to send a // Initial context setup response message - trigger_bearer_setup(rrc, UE, UE->n_initial_pdu, UE->initial_pdus, 0); + if (!trigger_bearer_setup(rrc, UE, UE->n_initial_pdu, UE->initial_pdus, 0)) { + LOG_W(NR_RRC, "UE %d: reject PDU Session Setup in Initial Context Setup Response\n", UE->rrc_ue_id); + ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CAUSE_RADIO_NETWORK_RESOURCES_NOT_AVAILABLE_FOR_THE_SLICE}; + send_ngap_initial_context_setup_resp_fail(rrc->module_id, req, cause); + rrc_forward_ue_nas_message(rrc, UE); + return -1; + } } else { /* no PDU sesion to setup: acknowledge this message, and forward NAS * message, if required */ @@ -528,10 +585,10 @@ void rrc_gNB_send_NGAP_INITIAL_CONTEXT_SETUP_RESP(gNB_RRC_INST *rrc, gNB_RRC_UE_ } } 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.type = NGAP_CAUSE_RADIO_NETWORK; - fail->cause.value = NGAP_CAUSE_RADIO_NETWORK_UNKNOWN_PDU_SESSION_ID; + ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CAUSE_RADIO_NETWORK_UNKNOWN_PDU_SESSION_ID}; + fill_pdu_session_resource_failed_to_setup_item(&resp->pdusessions_failed[pdu_sessions_failed], + session->param.pdusession_id, + cause); pdu_sessions_failed++; } } @@ -760,47 +817,72 @@ void rrc_gNB_send_NGAP_PDUSESSION_SETUP_RESP(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE return; } -//------------------------------------------------------------------------------ -void rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ(MessageDef *msg_p, instance_t instance) -//------------------------------------------------------------------------------ +/** + * @brief Fill PDU Session Resource Setup Response with a list of PDU Session Resources Failed to Setup + * and send ITTI message to TASK_NGAP + */ +static void send_ngap_pdu_session_setup_resp_fail(instance_t instance, ngap_pdusession_setup_req_t *msg, ngap_cause_t cause) { + MessageDef *msg_resp = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_PDUSESSION_SETUP_RESP); + ngap_pdusession_setup_resp_t *resp = &NGAP_PDUSESSION_SETUP_RESP(msg_resp); + resp->gNB_ue_ngap_id = msg->gNB_ue_ngap_id; + resp->nb_of_pdusessions_failed = msg->nb_pdusessions_tosetup; + resp->nb_of_pdusessions = 0; + for (int i = 0; i < resp->nb_of_pdusessions_failed; ++i) { + fill_pdu_session_resource_failed_to_setup_item(&resp->pdusessions_failed[i], + msg->pdusession_setup_params[i].pdusession_id, + cause); + } + itti_send_msg_to_task(TASK_NGAP, instance, msg_resp); +} - 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; +void rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ(MessageDef *msg_p, instance_t instance) +{ gNB_RRC_INST *rrc = RC.nrrrc[instance]; - + 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(rrc, msg->gNB_ue_ngap_id); + // Reject PDU Session Resource setup if no UE context is found 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 ; + LOG_W(NR_RRC, + "[gNB %ld] In NGAP_PDUSESSION_SETUP_REQ: no UE context found from UE NGAP ID (%u)\n", + instance, + msg->gNB_ue_ngap_id); + ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CAUSE_RADIO_NETWORK_UNKNOWN_LOCAL_UE_NGAP_ID}; + send_ngap_pdu_session_setup_resp_fail(instance, msg, cause); + return; } - DevAssert(UE->rrc_ue_id == msg->gNB_ue_ngap_id); - LOG_I(NR_RRC, "UE %d: received PDU session setup request\n", UE->rrc_ue_id); + gNB_RRC_UE_t *UE = &ue_context_p->ue_context; + LOG_I(NR_RRC, "UE %d: received PDU Session Resource Setup Request\n", UE->rrc_ue_id); + // Reject PDU Session Resource setup if gNB_ue_ngap_id is not matching + if (UE->rrc_ue_id != msg->gNB_ue_ngap_id) { + LOG_W(NR_RRC, "[gNB %ld] In NGAP_PDUSESSION_SETUP_REQ: unknown UE NGAP ID (%u)\n", instance, msg->gNB_ue_ngap_id); + ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CAUSE_RADIO_NETWORK_UNKNOWN_LOCAL_UE_NGAP_ID}; + send_ngap_pdu_session_setup_resp_fail(instance, msg, cause); + rrc_forward_ue_nas_message(rrc, UE); + return; + } + + // Reject PDU Session Resource setup if there is no security context active if (!UE->as_security_active) { - LOG_E(NR_RRC, "UE %d: no security context active for UE, rejecting PDU session setup request\n", UE->rrc_ue_id); - MessageDef *msg_resp = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_PDUSESSION_SETUP_RESP); - ngap_pdusession_setup_resp_t *resp = &NGAP_PDUSESSION_SETUP_RESP(msg_resp); - resp->gNB_ue_ngap_id = UE->rrc_ue_id; - resp->nb_of_pdusessions_failed = msg->nb_pdusessions_tosetup; - for (int i = 0; i < resp->nb_of_pdusessions_failed; ++i) { - pdusession_failed_t *f = &resp->pdusessions_failed[i]; - f->pdusession_id = msg->pdusession_setup_params[i].pdusession_id; - f->cause.type = NGAP_CAUSE_PROTOCOL; - f->cause.value = NGAP_CAUSE_PROTOCOL_MSG_NOT_COMPATIBLE_WITH_RECEIVER_STATE; - } - itti_send_msg_to_task(TASK_NGAP, instance, msg_resp); + LOG_E(NR_RRC, "UE %d: no security context active for UE, rejecting PDU Session Resource Setup Request\n", UE->rrc_ue_id); + ngap_cause_t cause = {.type = NGAP_CAUSE_PROTOCOL, .value = NGAP_CAUSE_PROTOCOL_MSG_NOT_COMPATIBLE_WITH_RECEIVER_STATE}; + send_ngap_pdu_session_setup_resp_fail(instance, msg, cause); + rrc_forward_ue_nas_message(rrc, UE); return; } 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); + + if (!trigger_bearer_setup(rrc, UE, msg->nb_pdusessions_tosetup, msg->pdusession_setup_params, msg->ueAggMaxBitRateDownlink)) { + // Reject PDU Session Resource setup if there's no CU-UP associated + LOG_W(NR_RRC, "UE %d: reject PDU Session Setup in PDU Session Resource Setup Response\n", UE->rrc_ue_id); + ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CAUSE_RADIO_NETWORK_RESOURCES_NOT_AVAILABLE_FOR_THE_SLICE}; + send_ngap_pdu_session_setup_resp_fail(instance, msg, cause); + rrc_forward_ue_nas_message(rrc, UE); + } + return; } diff --git a/openair2/RRC/NR/rrc_gNB_cuup.c b/openair2/RRC/NR/rrc_gNB_cuup.c index 621ebca56e..d0cc371209 100644 --- a/openair2/RRC/NR/rrc_gNB_cuup.c +++ b/openair2/RRC/NR/rrc_gNB_cuup.c @@ -90,6 +90,16 @@ static const nr_rrc_cuup_container_t *select_cuup_round_robin(size_t n_t, const return NULL; } +bool is_cuup_associated(gNB_RRC_INST *rrc) +{ + nr_rrc_cuup_container_t *cuup = NULL; + RB_FOREACH(cuup, rrc_cuup_tree, &rrc->cuups) { + return true; + } + LOG_W(NR_RRC, "no CU-UP associated to CU-CP\n"); + return false; +} + bool ue_associated_to_cuup(const gNB_RRC_INST *rrc, const gNB_RRC_UE_t *ue) { f1_ue_data_t ue_data = cu_get_f1_ue_data(ue->rrc_ue_id);