diff --git a/common/platform_constants.h b/common/platform_constants.h index 636f5d956d..4d25c7f00a 100644 --- a/common/platform_constants.h +++ b/common/platform_constants.h @@ -45,6 +45,8 @@ #define NR_MAX_NB_PDU_SESSIONS (256) +#define NR_MAX_NB_ALLOWED_SNSSAI (8) /* Maximum number of allowed S-NSSAI in TS 38.413 */ + #define MAX_DRBS_PER_UE (32) /* Maximum number of Data Radio Bearers per UE * defined for NGAP in TS 38.413 - maxnoofDRBs */ #define MAX_PDUS_PER_UE (8) /* Maximum number of PDU Sessions per UE */ diff --git a/openair2/COMMON/ngap_messages_def.h b/openair2/COMMON/ngap_messages_def.h index a5b0a8ef9a..c467abf0ef 100644 --- a/openair2/COMMON/ngap_messages_def.h +++ b/openair2/COMMON/ngap_messages_def.h @@ -53,6 +53,8 @@ MESSAGE_DEF(NGAP_HANDOVER_CANCEL, MESSAGE_PRIORITY_MED, ngap_handover_cancel_t, MESSAGE_DEF(NGAP_UL_RAN_STATUS_TRANSFER, MESSAGE_PRIORITY_MED, ngap_ran_status_transfer_t, ngap_ul_ran_status_transfer) MESSAGE_DEF(NGAP_DL_RAN_STATUS_TRANSFER, MESSAGE_PRIORITY_MED, ngap_ran_status_transfer_t, ngap_dl_ran_status_transfer) MESSAGE_DEF(NGAP_RECONNECT_TIMER, MESSAGE_PRIORITY_MED, IttiMsgText, ngap_gNB_amf_data) +MESSAGE_DEF(NGAP_PATH_SWITCH_REQ, MESSAGE_PRIORITY_MED, ngap_path_switch_req_t, ngap_path_switch_req) +MESSAGE_DEF(NGAP_PATH_SWITCH_REQ_ACK, MESSAGE_PRIORITY_MED, ngap_path_switch_req_ack_t, ngap_path_switch_req_ack) /* NGAP -> RRC messages */ MESSAGE_DEF(NGAP_DOWNLINK_NAS , MESSAGE_PRIORITY_MED, ngap_downlink_nas_t , ngap_downlink_nas ) diff --git a/openair2/COMMON/ngap_messages_types.h b/openair2/COMMON/ngap_messages_types.h index 6a77ac6e45..95d43acf97 100644 --- a/openair2/COMMON/ngap_messages_types.h +++ b/openair2/COMMON/ngap_messages_types.h @@ -47,6 +47,8 @@ #define NGAP_HANDOVER_NOTIFY(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_notify #define NGAP_HANDOVER_CANCEL(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_cancel #define NGAP_HANDOVER_CANCEL_ACK(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_cancel_ack +#define NGAP_PATH_SWITCH_REQ(mSGpTR) (mSGpTR)->ittiMsg.ngap_path_switch_req +#define NGAP_PATH_SWITCH_REQ_ACK(mSGpTR) (mSGpTR)->ittiMsg.ngap_path_switch_req_ack #define NGAP_UE_CONTEXT_RELEASE_REQ(mSGpTR) (mSGpTR)->ittiMsg.ngap_ue_release_req #define NGAP_PDUSESSION_RELEASE_COMMAND(mSGpTR) (mSGpTR)->ittiMsg.ngap_pdusession_release_command @@ -669,6 +671,69 @@ typedef struct { uint64_t amf_ue_ngap_id; } ngap_handover_cancel_ack_t; +/* Path Switch Request 9.2.3.8 3GPP TS 38.413 */ +typedef struct ngap_path_switch_req_s { + // RAN UE NGAP ID + uint32_t gNB_ue_ngap_id; + // Source AMF UE NGAP ID + uint64_t amf_ue_ngap_id; + // User Location Information + user_location_information_t user_info; + // UE Security Capabilities + ngap_security_capabilities_t security_capabilities; + // Number of pdusession to be switched in the downlink list + uint16_t nb_of_pdusessions; + // List of PDU Session Resource to be Switched in Downlink + pdusession_setup_t pdusessions_tobeswitched[NR_MAX_NB_PDU_SESSIONS]; +} ngap_path_switch_req_t; + +typedef enum ngap_security_ind_s { + NGAP_SECURITY_REQUIRED = 0, + NGAP_SECURITY_PREFERRED = 1, + NGAP_SECURITY_NOT_NEEDED = 2, +} ngap_security_ind_t; + +/* 9.3.1.27 3GPP TS 38.413 */ +typedef struct security_ind_s { + ngap_security_ind_t integrity_protection_ind; + ngap_security_ind_t confidentiality_protection_ind; +} security_ind_t; + +/* 9.3.4.9 3GPP TS 38.413 */ +typedef struct path_switch_request_ack_transfer_s { + // UL NG-U UP TNL Information (O) + gtpu_tunnel_t *n3_incoming; + // Security Indication (O) + security_ind_t *security_ind; +} path_switch_request_ack_transfer_t; + +/* Path Switch Request Acknowledge 9.2.3.9 3GPP TS 38.413 + * PDU Session Resource Switched Item */ +typedef struct path_switch_request_ack_pdusession_s { + // PDU Session ID (M) + int pdusession_id; + // Path Switch Request Acknowledge Transfer (M) + path_switch_request_ack_transfer_t pathSwitchReqAckTransfer; +} path_switch_request_ack_pdusession_t; + +/* Path Switch Request Acknowledge 9.2.3.9 3GPP TS 38.413 */ +typedef struct ngap_path_switch_req_ack_s { + // AMF UE NGAP ID (M) + uint64_t amf_ue_ngap_id; + // RAN UE NGAP ID (M) + uint32_t gNB_ue_ngap_id; + // Security Context - Next-Hop Chaining Count (M) + uint8_t nh_ncc; + // Security Context - Next-Hop NH (M) + uint8_t next_security_key[SECURITY_KEY_LENGTH]; + // List of PDU Session Resource Switched (M) + uint16_t nb_of_pdusessions; + path_switch_request_ack_pdusession_t pdusessions_switched[NR_MAX_NB_PDU_SESSIONS]; + // Allowed NSSAI (M) + uint8_t nb_allowed_nssais; + nssai_t allowed_nssai[NR_MAX_NB_ALLOWED_SNSSAI]; +} ngap_path_switch_req_ack_t; + typedef struct ngap_ue_cap_info_ind_s { uint32_t gNB_ue_ngap_id; byte_array_t ue_radio_cap; diff --git a/openair3/NGAP/ngap_common.c b/openair3/NGAP/ngap_common.c index eb708918eb..22ce977325 100644 --- a/openair3/NGAP/ngap_common.c +++ b/openair3/NGAP/ngap_common.c @@ -133,6 +133,14 @@ nssai_t decode_ngap_nssai(const NGAP_S_NSSAI_t *in) return nssai; } +void encode_ngap_security_capabilities(NGAP_UESecurityCapabilities_t *out, const ngap_security_capabilities_t *in) +{ + ENCRALG_TO_BIT_STRING(in->nRencryption_algorithms, &out->nRencryptionAlgorithms); + ENCRALG_TO_BIT_STRING(in->eUTRAencryption_algorithms, &out->eUTRAencryptionAlgorithms); + INTPROTALG_TO_BIT_STRING(in->nRintegrity_algorithms, &out->nRintegrityProtectionAlgorithms); + INTPROTALG_TO_BIT_STRING(in->eUTRAintegrity_algorithms, &out->eUTRAintegrityProtectionAlgorithms); +} + ngap_security_capabilities_t decode_ngap_security_capabilities(const NGAP_UESecurityCapabilities_t *in) { ngap_security_capabilities_t out = {0}; diff --git a/openair3/NGAP/ngap_common.h b/openair3/NGAP/ngap_common.h index cf0b93403c..6f8945a0f6 100644 --- a/openair3/NGAP/ngap_common.h +++ b/openair3/NGAP/ngap_common.h @@ -41,6 +41,7 @@ pdusession_level_qos_parameter_t fill_qos(uint8_t qfi, const NGAP_QosFlowLevelQo void *decode_pdusession_transfer(const asn_TYPE_descriptor_t *td, const OCTET_STRING_t buf); bool decodePDUSessionResourceSetup(pdusession_transfer_t *out, const OCTET_STRING_t in); byte_array_t encode_ngap_pdusession_setup_response_transfer(const pdusession_setup_t *pdusession); +void encode_ngap_security_capabilities(NGAP_UESecurityCapabilities_t *out, const ngap_security_capabilities_t *in); bool eq_ngap_plmn(const plmn_id_t *a, const plmn_id_t *b); diff --git a/openair3/NGAP/ngap_gNB.c b/openair3/NGAP/ngap_gNB.c index 746a7b1ad8..7814984aef 100644 --- a/openair3/NGAP/ngap_gNB.c +++ b/openair3/NGAP/ngap_gNB.c @@ -281,6 +281,56 @@ void ngap_gNB_handle_sctp_data_ind(sctp_data_ind_t *sctp_data_ind) { AssertFatal (result == EXIT_SUCCESS, "Failed to free memory (%d)!\n", result); } +static int ngap_gNB_path_switch_request(instance_t instance, ngap_path_switch_req_t *msg) +{ + DevAssert(msg != NULL); + NGAP_DEBUG("Triggered Pathswitch Request\n"); + + /*Retrieve the NGAP gNB instance associated with Mod_id */ + ngap_gNB_instance_t *ngap_gNB_instance_p = ngap_gNB_get_instance(instance); + DevAssert(ngap_gNB_instance_p != NULL); + + NGAP_NGAP_PDU_t *pdu = encode_ng_path_switch_request(msg); + DevAssert(pdu != NULL); + + if (LOG_DEBUGFLAG(DEBUG_ASN1)) + xer_fprint(stdout, &asn_DEF_NGAP_NGAP_PDU, pdu); + + byte_array_t out = { .buf = NULL, .len = 0 }; + if (ngap_gNB_encode_pdu(pdu, &out.buf, (uint32_t *)&out.len) < 0) { + NGAP_ERROR("Failed to encode Path Switch Request message\n"); + ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, pdu); + return -1; + } + + ngap_gNB_amf_data_t *amf_ref = ngap_gNB_get_AMF_from_instance(ngap_gNB_instance_p); + if (!amf_ref) { + NGAP_ERROR("Failed to fetch AMF for current NGAP instance\n"); + ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, pdu); + return -1; + } + + // Create and then store the NGAP UE context + ngap_gNB_ue_context_t ue_context_p = { + .amf_ref = amf_ref, + .gNB_ue_ngap_id = msg->gNB_ue_ngap_id, + .amf_ue_ngap_id = msg->amf_ue_ngap_id, + .gNB_instance = ngap_gNB_instance_p, + .ue_state = NGAP_UE_CONNECTED, + }; + ngap_store_ue_context(&ue_context_p); + + /* UE associated signalling -> use the allocated stream */ + ngap_gNB_itti_send_sctp_data_req(ngap_gNB_instance_p->instance, + ue_context_p.amf_ref->assoc_id, + out.buf, + out.len, + ue_context_p.tx_stream); + NGAP_INFO("Sent Path Switch Request to AMF\n"); + ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, pdu); + return 0; +} + /** @brief UE Mobility Management: callback for Handover Required */ int ngap_handover_required(instance_t instance, ngap_handover_required_t *msg) { @@ -613,6 +663,10 @@ void *ngap_gNB_process_itti_msg(void *notUsed) ngap_gNB_pdusession_release_resp(instance, &NGAP_PDUSESSION_RELEASE_RESPONSE(received_msg)); break; + case NGAP_PATH_SWITCH_REQ: + ngap_gNB_path_switch_request(instance, &NGAP_PATH_SWITCH_REQ(received_msg)); + break; + case NGAP_HANDOVER_REQUIRED: if (ngap_handover_required(instance, &NGAP_HANDOVER_REQUIRED(received_msg)) < 0) { NGAP_ERROR("Handover Required failure: indication to RRC is not sent!\n"); diff --git a/openair3/NGAP/ngap_gNB_handlers.c b/openair3/NGAP/ngap_gNB_handlers.c index 22ded50b74..31478d741e 100644 --- a/openair3/NGAP/ngap_gNB_handlers.c +++ b/openair3/NGAP/ngap_gNB_handlers.c @@ -1092,6 +1092,54 @@ static int ngap_gNB_handle_handover_cancel_ack(sctp_assoc_t assoc_id, uint32_t s return 0; } +/** @brief Handler for NGAP Path Switch Request Acknowledge + * AMF -> NG-RAN Node */ +static int ngap_gNB_handle_ng_path_switch_request_ack(sctp_assoc_t assoc_id, uint32_t stream, NGAP_NGAP_PDU_t *pdu) +{ + NGAP_INFO("Received NG Path Switch Request Acknowledge\n"); + ngap_gNB_amf_data_t *amf_desc_p = NULL; + DevAssert(pdu != NULL); + + if ((amf_desc_p = ngap_gNB_get_AMF(NULL, assoc_id, 0)) == NULL) { + NGAP_ERROR("[SCTP %u] Received Path Switch Request " + "Acknowledge for non existing AMF context\n", assoc_id); + return -1; + } + MessageDef *message_p = itti_alloc_new_message(TASK_NGAP, 0, NGAP_PATH_SWITCH_REQ_ACK); + ngap_path_switch_req_ack_t *msg = &NGAP_PATH_SWITCH_REQ_ACK(message_p); + memset(msg, 0, sizeof(*msg)); + if (decode_ng_path_switch_request_acknowledge(msg, pdu) < 0) { + NGAP_ERROR("Failed to decode NG Path Switch Request Acknowledge\n"); + free_ng_path_switch_req_ack(msg); + itti_free(TASK_NGAP, message_p); + return -1; + } + + ngap_gNB_ue_context_t *ue_desc_p = ngap_get_ue_context(msg->gNB_ue_ngap_id); + if (!ue_desc_p) { + NGAP_ERROR("[SCTP %u] Received Path Switch Request Acknowledge for non " + "existing UE context (gNB_ue_ngap_id %d)\n", + assoc_id, + msg->gNB_ue_ngap_id); + free_ng_path_switch_req_ack(msg); + itti_free(TASK_NGAP, message_p); + return -1; + } + + ue_desc_p->rx_stream = stream; + if (ue_desc_p->amf_ue_ngap_id != msg->amf_ue_ngap_id) { + NGAP_ERROR("UE context amf_ue_ngap_id is different from that of the message (%ld != %ld)", + ue_desc_p->amf_ue_ngap_id, + msg->amf_ue_ngap_id); + free_ng_path_switch_req_ack(msg); + itti_free(TASK_NGAP, message_p); + return -1; + } + + itti_send_msg_to_task(TASK_RRC_GNB, amf_desc_p->ngap_gNB_instance->instance, message_p); + return 0; +} + /** * @brief Handle NGAP Paging message from AMF * @param assoc_id SCTP association ID @@ -1542,7 +1590,7 @@ const ngap_message_decoded_callback ngap_messages_callback[][3] = { {0, 0, 0}, /* OverloadStart */ {0, 0, 0}, /* OverloadStop */ {ngap_gNB_handle_paging, 0, 0}, /* Paging */ - {0, 0, 0}, /* PathSwitchRequest */ + {0, ngap_gNB_handle_ng_path_switch_request_ack, 0}, /* PathSwitchRequest */ {ngap_gNB_handle_pdusession_modify_request, 0, 0}, /* PDUSessionResourceModify */ {0, 0, 0}, /* PDUSessionResourceModifyIndication */ {ngap_gNB_handle_pdusession_release_command, 0, 0}, /* PDUSessionResourceRelease */ diff --git a/openair3/NGAP/ngap_gNB_mobility_management.c b/openair3/NGAP/ngap_gNB_mobility_management.c index 01ed804512..c684f37d5a 100644 --- a/openair3/NGAP/ngap_gNB_mobility_management.c +++ b/openair3/NGAP/ngap_gNB_mobility_management.c @@ -732,3 +732,184 @@ NGAP_NGAP_PDU_t *encode_ng_ul_ran_status_transfer(const ngap_ran_status_transfer return pdu; } + +/** @brief Encode NGAP Path Switch Request (9.2.3.8 3GPP TS 38.413) NG-RAN node -> AMF */ +NGAP_NGAP_PDU_t *encode_ng_path_switch_request(const ngap_path_switch_req_t *msg) +{ + NGAP_NGAP_PDU_t *pdu = calloc_or_fail(1,sizeof(*pdu)); + + /* Prepare the NGAP message to encode */ + pdu->present = NGAP_NGAP_PDU_PR_initiatingMessage; + asn1cCalloc(pdu->choice.initiatingMessage, head); + head->procedureCode = NGAP_ProcedureCode_id_PathSwitchRequest; + head->criticality = NGAP_Criticality_reject; + head->value.present = NGAP_InitiatingMessage__value_PR_PathSwitchRequest; + NGAP_PathSwitchRequest_t *out = &head->value.choice.PathSwitchRequest; + + // RAN UE NGAP ID (M) + asn1cSequenceAdd(out->protocolIEs.list, NGAP_PathSwitchRequestIEs_t, ie1); + ie1->id = NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID; + ie1->criticality = NGAP_Criticality_reject; + ie1->value.present = NGAP_PathSwitchRequestIEs__value_PR_RAN_UE_NGAP_ID; + ie1->value.choice.RAN_UE_NGAP_ID = msg->gNB_ue_ngap_id; + + // Source AMF UE NGAP ID (M) + asn1cSequenceAdd(out->protocolIEs.list, NGAP_PathSwitchRequestIEs_t, ie2); + ie2->id = NGAP_ProtocolIE_ID_id_SourceAMF_UE_NGAP_ID; + ie2->criticality = NGAP_Criticality_reject; + ie2->value.present = NGAP_PathSwitchRequestIEs__value_PR_AMF_UE_NGAP_ID; + asn_uint642INTEGER(&ie2->value.choice.AMF_UE_NGAP_ID, msg->amf_ue_ngap_id); + + // User Location Information (M) + { + asn1cSequenceAdd(out->protocolIEs.list, NGAP_PathSwitchRequestIEs_t, ie3); + ie3->id = NGAP_ProtocolIE_ID_id_UserLocationInformation; + ie3->criticality = NGAP_Criticality_ignore; + ie3->value.present = NGAP_PathSwitchRequestIEs__value_PR_UserLocationInformation; + ie3->value.choice.UserLocationInformation.present = NGAP_UserLocationInformation_PR_userLocationInformationNR; + asn1cCalloc(ie3->value.choice.UserLocationInformation.choice.userLocationInformationNR, userinfo_nr_p); + + // NR user location information + const target_ran_node_id_t *target = &msg->user_info.target_ng_ran; + // CGI (M) + MACRO_GNB_ID_TO_CELL_IDENTITY(target->targetgNBId, msg->user_info.nrCellIdentity, &userinfo_nr_p->nR_CGI.nRCellIdentity); + MCC_MNC_TO_TBCD(target->plmn_identity.mcc, + target->plmn_identity.mnc, + target->plmn_identity.mnc_digit_length, + &userinfo_nr_p->nR_CGI.pLMNIdentity); + // TAI (M) + INT24_TO_OCTET_STRING(target->tac, &userinfo_nr_p->tAI.tAC); + MCC_MNC_TO_PLMNID(target->plmn_identity.mcc, + target->plmn_identity.mnc, + target->plmn_identity.mnc_digit_length, + &userinfo_nr_p->tAI.pLMNIdentity); + } + + // UE Security Capabilities (M) + asn1cSequenceAdd(out->protocolIEs.list, NGAP_PathSwitchRequestIEs_t, ie4); + ie4->id = NGAP_ProtocolIE_ID_id_UESecurityCapabilities; + ie4->criticality = NGAP_Criticality_ignore; + ie4->value.present = NGAP_PathSwitchRequestIEs__value_PR_UESecurityCapabilities; + encode_ngap_security_capabilities(&ie4->value.choice.UESecurityCapabilities, &msg->security_capabilities); + + // PDU Session Resource to be Switched in Downlink List (M) + { + asn1cSequenceAdd(out->protocolIEs.list, NGAP_PathSwitchRequestIEs_t, ie5); + ie5->id = NGAP_ProtocolIE_ID_id_PDUSessionResourceToBeSwitchedDLList; + ie5->criticality = NGAP_Criticality_reject; + ie5->value.present = NGAP_PathSwitchRequestIEs__value_PR_PDUSessionResourceToBeSwitchedDLList; + + for (const pdusession_setup_t *pdu = msg->pdusessions_tobeswitched; pdu < msg->pdusessions_tobeswitched + msg->nb_of_pdusessions; pdu++) { + asn1cSequenceAdd(ie5->value.choice.PDUSessionResourceToBeSwitchedDLList.list, NGAP_PDUSessionResourceToBeSwitchedDLItem_t, item); + item->pDUSessionID = pdu->pdusession_id; + /* dLQoSFlowPerTNLInformation */ + NGAP_PathSwitchRequestTransfer_t transfer = {0}; + transfer.dL_NGU_UP_TNLInformation.present = NGAP_UPTransportLayerInformation_PR_gTPTunnel; + asn1cCalloc(transfer.dL_NGU_UP_TNLInformation.choice.gTPTunnel, tmp); + GTP_TEID_TO_ASN1(pdu->n3_outgoing.teid, &tmp->gTP_TEID); + tnl_to_bitstring(&tmp->transportLayerAddress, pdu->n3_outgoing.addr); + for (int j = 0; j < pdu->nb_of_qos_flow; j++) { + asn1cSequenceAdd(transfer.qosFlowAcceptedList.list, NGAP_QosFlowAcceptedItem_t, qosItem); + qosItem->qosFlowIdentifier = pdu->associated_qos_flows[j].qfi; + } + + void *buf; + ssize_t encoded = aper_encode_to_new_buffer(&asn_DEF_NGAP_PathSwitchRequestTransfer, NULL, &transfer, &buf); + AssertFatal(encoded > 0, "ASN1 message encoding failed !\n"); + item->pathSwitchRequestTransfer.buf = buf; + item->pathSwitchRequestTransfer.size = encoded; + + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_PathSwitchRequestTransfer, &transfer); + } + } + return pdu; +} + +void free_ng_path_switch_req_ack(ngap_path_switch_req_ack_t *msg) +{ + if (msg == NULL) + return; + + for (int i = 0; i < msg->nb_of_pdusessions; i++) { + path_switch_request_ack_transfer_t *transfer = &msg->pdusessions_switched[i].pathSwitchReqAckTransfer; + + free(transfer->n3_incoming); + transfer->n3_incoming = NULL; + + free(transfer->security_ind); + transfer->security_ind = NULL; + } +} + +/** @brief Decode NGAP Path Switch Request Acknowledge (9.2.3.9 3GPP TS 38.413) */ +int decode_ng_path_switch_request_acknowledge(ngap_path_switch_req_ack_t *msg, NGAP_NGAP_PDU_t *pdu) +{ + DevAssert(pdu != NULL); + NGAP_PathSwitchRequestAcknowledgeIEs_t *ie; + NGAP_PathSwitchRequestAcknowledge_t *container = &pdu->choice.successfulOutcome->value.choice.PathSwitchRequestAcknowledge; + + // AMF UE NGAP ID (M) + NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PathSwitchRequestAcknowledgeIEs_t, ie, container, NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID, true); + asn_INTEGER2ulong(&(ie->value.choice.AMF_UE_NGAP_ID), &msg->amf_ue_ngap_id); + + // RAN UE NGAP ID (M) + NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PathSwitchRequestAcknowledgeIEs_t, ie, container, NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID, true); + msg->gNB_ue_ngap_id = ie->value.choice.RAN_UE_NGAP_ID; + + // Security Context (M) + NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PathSwitchRequestAcknowledgeIEs_t, ie, container, NGAP_ProtocolIE_ID_id_SecurityContext, true); + msg->nh_ncc = ie->value.choice.SecurityContext.nextHopChainingCount; + memcpy(msg->next_security_key, ie->value.choice.SecurityContext.nextHopNH.buf, ie->value.choice.SecurityContext.nextHopNH.size); + + // PDU Session Resource Switched List (M) + NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PathSwitchRequestAcknowledgeIEs_t, ie, container, NGAP_ProtocolIE_ID_id_PDUSessionResourceSwitchedList, true); + msg->nb_of_pdusessions = ie->value.choice.PDUSessionResourceSwitchedList.list.count; + + for (int i = 0; i < msg->nb_of_pdusessions; ++i) { + NGAP_PDUSessionResourceSwitchedItem_t *item = ie->value.choice.PDUSessionResourceSwitchedList.list.array[i]; + path_switch_request_ack_pdusession_t *switched = &msg->pdusessions_switched[i]; + // PDU Session ID (M) + switched->pdusession_id = item->pDUSessionID; + // Path Switch Request Acknowledge Transfer (M) + NGAP_PathSwitchRequestAcknowledgeTransfer_t *psrAckTransfer = NULL; + asn_dec_rval_t dec_rval = uper_decode_complete(NULL, + &asn_DEF_NGAP_PathSwitchRequestAcknowledgeTransfer, + (void **)&psrAckTransfer, + item->pathSwitchRequestAcknowledgeTransfer.buf, + item->pathSwitchRequestAcknowledgeTransfer.size); + if (dec_rval.code != RC_OK) { + NGAP_ERROR("Failed to decode Path Switch Request Acknowledge Transfer\n"); + ASN_STRUCT_FREE(asn_DEF_NGAP_PathSwitchRequestAcknowledgeTransfer, psrAckTransfer); + return -1; + } + + // UL NG-U UP TNL Information (O) + if (psrAckTransfer->uL_NGU_UP_TNLInformation != NULL) { + NGAP_UPTransportLayerInformation_t *up_tnl = psrAckTransfer->uL_NGU_UP_TNLInformation; + if (up_tnl->present == NGAP_UPTransportLayerInformation_PR_gTPTunnel && up_tnl->choice.gTPTunnel != NULL) { + switched->pathSwitchReqAckTransfer.n3_incoming = calloc_or_fail(1, sizeof(*switched->pathSwitchReqAckTransfer.n3_incoming)); + OCTET_STRING_TO_INT32(&(up_tnl->choice.gTPTunnel->gTP_TEID), switched->pathSwitchReqAckTransfer.n3_incoming->teid); + bitstring_to_tnl(&switched->pathSwitchReqAckTransfer.n3_incoming->addr, up_tnl->choice.gTPTunnel->transportLayerAddress); + } + } + + // Security Indication (O) + if (psrAckTransfer->securityIndication != NULL) { + switched->pathSwitchReqAckTransfer.security_ind = calloc_or_fail(1, sizeof(*switched->pathSwitchReqAckTransfer.security_ind)); + switched->pathSwitchReqAckTransfer.security_ind->integrity_protection_ind = psrAckTransfer->securityIndication->integrityProtectionIndication; + switched->pathSwitchReqAckTransfer.security_ind->confidentiality_protection_ind = psrAckTransfer->securityIndication->confidentialityProtectionIndication; + } + + ASN_STRUCT_FREE(asn_DEF_NGAP_PathSwitchRequestAcknowledgeTransfer, psrAckTransfer); + } + + // Allowed NSSAI (M) + NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PathSwitchRequestAcknowledgeIEs_t, ie, container, NGAP_ProtocolIE_ID_id_AllowedNSSAI, true); + NGAP_DEBUG("AllowedNSSAI.list.count %d\n", ie->value.choice.AllowedNSSAI.list.count); + msg->nb_allowed_nssais = ie->value.choice.AllowedNSSAI.list.count; + for (int i = 0; i < msg->nb_allowed_nssais; ++i) { + msg->allowed_nssai[i] = decode_ngap_nssai(&ie->value.choice.AllowedNSSAI.list.array[i]->s_NSSAI); + } + + return 0; +} diff --git a/openair3/NGAP/ngap_gNB_mobility_management.h b/openair3/NGAP/ngap_gNB_mobility_management.h index 16be3ddc77..446242e2ad 100644 --- a/openair3/NGAP/ngap_gNB_mobility_management.h +++ b/openair3/NGAP/ngap_gNB_mobility_management.h @@ -20,5 +20,8 @@ NGAP_NGAP_PDU_t *encode_ng_handover_notify(const ngap_handover_notify_t *msg); NGAP_NGAP_PDU_t *encode_ng_ul_ran_status_transfer(const ngap_ran_status_transfer_t *msg); NGAP_NGAP_PDU_t *encode_ng_handover_cancel(const ngap_handover_cancel_t *msg); int decode_ng_handover_cancel_ack(ngap_handover_cancel_ack_t *out, const NGAP_NGAP_PDU_t *pdu); +NGAP_NGAP_PDU_t *encode_ng_path_switch_request(const ngap_path_switch_req_t *msg); +int decode_ng_path_switch_request_acknowledge(ngap_path_switch_req_ack_t *msg, NGAP_NGAP_PDU_t *pdu); +void free_ng_path_switch_req_ack(ngap_path_switch_req_ack_t *msg); #endif /* NGAP_GNB_MOBILITY_MANAGEMENT_H_ */ diff --git a/openair3/NGAP/ngap_msg_includes.h b/openair3/NGAP/ngap_msg_includes.h index 1a42183a62..69c4ca45d7 100644 --- a/openair3/NGAP/ngap_msg_includes.h +++ b/openair3/NGAP/ngap_msg_includes.h @@ -97,4 +97,9 @@ #include "NGAP_DRBStatusDL12.h" #include "NGAP_QosFlowListWithCause.h" #include "NGAP_QosFlowWithCauseItem.h" +#include "NGAP_PDUSessionResourceToBeSwitchedDLItem.h" +#include "NGAP_QosFlowAcceptedItem.h" +#include "NGAP_PathSwitchRequestTransfer.h" +#include "NGAP_PDUSessionResourceSwitchedItem.h" +#include "NGAP_PathSwitchRequestAcknowledgeTransfer.h" #endif // NGAP_MSG_INCLUDES_H