From 08f0f9369c4bc9c05ad02d9fe83aa481cd3452bc Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Mon, 29 Dec 2025 13:55:27 +0100 Subject: [PATCH] Paging (F1AP): add F1 paging library (9.2.6.1 3GPP TS 38.473) Add F1AP Paging library and new message type to follow the existing F1AP library pattern and to support multi-cell paging and both RAN UE (I-RNTI) and CN UE (5G-S-TMSI) paging identities at the library level per TS 38.473. * New library (f1ap_paging.c/h, f1ap_lib_includes.h, CMakeLists.txt) - Implement encode/decode/copy/eq/free for F1AP Paging (procedure 9.2.6). - Add ASN.1 includes for Paging, PagingIdentity, CN/RAN UE identities. - Enables unit testing of Paging enc/dec in f1ap_lib_test.c. * New types in f1ap_messages_types.h (existing f1ap_paging_ind_t unchanged) - f1ap_paging_t: UE Identity Index, paging identity (union + type enum), optional paging_drx, paging_priority, paging_origin, and paging_cells[]. - f1ap_paging_cell_item_t, f1ap_paging_identity_type_t, f1ap_paging_identity_t for multi-cell list and RAN/CN identity discrimination. - Paging Origin IE is optional per F1AP 8.7.1.2. Tests: test_f1ap_paging() roundtrip and copy for CN UE (5G-S-TMSI) and RAN UE (I-RNTI) cases, with optional IEs. --- openair2/COMMON/f1ap_messages_types.h | 63 +++++ openair2/F1AP/lib/CMakeLists.txt | 1 + openair2/F1AP/lib/f1ap_lib_includes.h | 4 + openair2/F1AP/lib/f1ap_paging.c | 349 ++++++++++++++++++++++++++ openair2/F1AP/lib/f1ap_paging.h | 37 +++ openair2/F1AP/tests/f1ap_lib_test.c | 75 ++++++ openair3/UTILS/conversions.h | 12 + 7 files changed, 541 insertions(+) create mode 100644 openair2/F1AP/lib/f1ap_paging.c create mode 100644 openair2/F1AP/lib/f1ap_paging.h diff --git a/openair2/COMMON/f1ap_messages_types.h b/openair2/COMMON/f1ap_messages_types.h index c891a44adc..79cc178029 100644 --- a/openair2/COMMON/f1ap_messages_types.h +++ b/openair2/COMMON/f1ap_messages_types.h @@ -24,6 +24,7 @@ #include #include +#include #include "common/5g_platform_types.h" #include "common/platform_constants.h" #include "common/utils/ds/byte_array.h" @@ -640,6 +641,68 @@ typedef struct f1ap_paging_ind_s { uint8_t paging_drx; } f1ap_paging_ind_t; +typedef struct f1ap_paging_cell_item_s { + plmn_id_t plmn; + uint64_t nr_cellid; +} f1ap_paging_cell_item_t; + +typedef enum f1ap_paging_identity_type_e { + F1AP_PAGING_IDENTITY_RAN_UE, // RAN UE Paging Identity (I-RNTI) + F1AP_PAGING_IDENTITY_CN_UE // CN UE Paging Identity (5G-S-TMSI) +} f1ap_paging_identity_type_t; + +typedef union f1ap_paging_identity_u { + // RAN UE Paging Identity (I-RNTI) + uint64_t ran_ue_paging_identity; + // CN UE Paging Identity (5G-S-TMSI) + uint64_t cn_ue_paging_identity; +} f1ap_paging_identity_t; + +/** @brief Paging DRX (9.3.1.40 TS 38.473) + * This IE indicates the Paging DRX as defined in + * TS 38.304. Unit in radio frames.*/ +typedef enum f1ap_paging_drx_e { + F1AP_PAGING_DRX_32 = 0, + F1AP_PAGING_DRX_64 = 1, + F1AP_PAGING_DRX_128 = 2, + F1AP_PAGING_DRX_256 = 3 +} f1ap_paging_drx_t; + +/** @brief Paging Priority (9.3.1.41 TS 38.473) + * Lower value codepoint indicates higher priority. + * codepoint indicates higher priority.*/ +typedef enum f1ap_paging_priority_e { + F1AP_PAGING_PRIO_LEVEL1 = 0, + F1AP_PAGING_PRIO_LEVEL2 = 1, + F1AP_PAGING_PRIO_LEVEL3 = 2, + F1AP_PAGING_PRIO_LEVEL4 = 3, + F1AP_PAGING_PRIO_LEVEL5 = 4, + F1AP_PAGING_PRIO_LEVEL6 = 5, + F1AP_PAGING_PRIO_LEVEL7 = 6, + F1AP_PAGING_PRIO_LEVEL8 = 7 +} f1ap_paging_priority_t; + +typedef bool f1ap_paging_origin_t; +#define F1AP_PAGING_ORIGIN_NON_3GPP true + +/* 9.2.6.1 Paging (3GPP TS 38.473) */ +typedef struct { + // UE Identity Index value (mandatory) + uint16_t ue_identity_index_value; + // Paging Identity (mandatory) + f1ap_paging_identity_type_t identity_type; + f1ap_paging_identity_t identity; + // Paging DRX: 0 (32), 1 (64), 2 (128), 3 (256) (optional) + f1ap_paging_drx_t *drx; + // Paging priority (optional) + f1ap_paging_priority_t *priority; + // Paging origin (optional) + f1ap_paging_origin_t *origin; + // Paging Cell List (mandatory); max 512 cells + uint16_t n_cells; + f1ap_paging_cell_item_t *cells; +} f1ap_paging_t; + typedef struct f1ap_lost_connection_t { int dummy; } f1ap_lost_connection_t; diff --git a/openair2/F1AP/lib/CMakeLists.txt b/openair2/F1AP/lib/CMakeLists.txt index 0a46fa2bf0..847484c5a2 100644 --- a/openair2/F1AP/lib/CMakeLists.txt +++ b/openair2/F1AP/lib/CMakeLists.txt @@ -4,6 +4,7 @@ add_library(f1ap_lib OBJECT f1ap_interface_management.c f1ap_ue_context.c f1ap_positioning.c + f1ap_paging.c ) target_link_libraries(f1ap_lib PRIVATE asn1_f1ap ds) target_include_directories(f1ap_lib PUBLIC diff --git a/openair2/F1AP/lib/f1ap_lib_includes.h b/openair2/F1AP/lib/f1ap_lib_includes.h index a50bd5efe5..d8aa45d89c 100644 --- a/openair2/F1AP/lib/f1ap_lib_includes.h +++ b/openair2/F1AP/lib/f1ap_lib_includes.h @@ -90,5 +90,9 @@ #include "F1AP_UL-RTOA-Measurement.h" #include "F1AP_GNB-RxTxTimeDiff.h" #include "F1AP_LCS-to-GCS-TranslationAoA.h" +#include "F1AP_CNUEPagingIdentity.h" +#include "F1AP_RANUEPagingIdentity.h" +#include "F1AP_PagingIdentity.h" +#include "F1AP_Paging.h" #endif /* F1AP_LIB_INCLUDES_H_ */ diff --git a/openair2/F1AP/lib/f1ap_paging.c b/openair2/F1AP/lib/f1ap_paging.c new file mode 100644 index 0000000000..bfb1fc0a55 --- /dev/null +++ b/openair2/F1AP/lib/f1ap_paging.c @@ -0,0 +1,349 @@ +/* + * 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 + */ + +#include + +#include "common/utils/assertions.h" +#include "openair3/UTILS/conversions.h" +#include "common/utils/oai_asn1.h" +#include "common/utils/utils.h" + +#include "f1ap_paging.h" +#include "f1ap_lib_common.h" +#include "f1ap_lib_includes.h" +#include "f1ap_messages_types.h" + +/** @brief encode to F1AP_PagingDRX_t */ +static F1AP_PagingDRX_t encode_f1_paging_drx(const f1ap_paging_drx_t drx) +{ + switch (drx) { + case F1AP_PAGING_DRX_32: + return F1AP_PagingDRX_v32; + case F1AP_PAGING_DRX_64: + return F1AP_PagingDRX_v64; + case F1AP_PAGING_DRX_128: + return F1AP_PagingDRX_v128; + case F1AP_PAGING_DRX_256: + return F1AP_PagingDRX_v256; + default: + AssertFatal(1 == 0, "Unknown Paging DRX: %u\n", drx); + } + return 0; +} + +/** @brief decode from F1AP_PagingDRX_t */ +static f1ap_paging_drx_t decode_f1_paging_drx(const F1AP_PagingDRX_t drx) +{ + switch (drx) { + case F1AP_PagingDRX_v32: + return F1AP_PAGING_DRX_32; + case F1AP_PagingDRX_v64: + return F1AP_PAGING_DRX_64; + case F1AP_PagingDRX_v128: + return F1AP_PAGING_DRX_128; + case F1AP_PagingDRX_v256: + return F1AP_PAGING_DRX_256; + default: + AssertFatal(1 == 0, "Unknown Paging DRX: %ld\n", drx); + } + return 0; +} + +/** @brief encode F1 Paging (9.2.6 Paging 3GPP TS 38.473) */ +F1AP_F1AP_PDU_t *encode_f1ap_paging(const f1ap_paging_t *msg) +{ + DevAssert(msg); + F1AP_F1AP_PDU_t *pdu = calloc_or_fail(1, sizeof(*pdu)); + + pdu->present = F1AP_F1AP_PDU_PR_initiatingMessage; + asn1cCalloc(pdu->choice.initiatingMessage, initMsg); + initMsg->procedureCode = F1AP_ProcedureCode_id_Paging; + initMsg->criticality = F1AP_Criticality_reject; + initMsg->value.present = F1AP_InitiatingMessage__value_PR_Paging; + F1AP_Paging_t *paging = &initMsg->value.choice.Paging; + + /* UEIdentityIndexValue (Mandatory) 10 bits */ + { + AssertFatal(msg->ue_identity_index_value <= 1023, + "UE Identity Index Value exceeds 10 bits: %d\n", + msg->ue_identity_index_value); + asn1cSequenceAdd(paging->protocolIEs.list, F1AP_PagingIEs_t, ie); + ie->id = F1AP_ProtocolIE_ID_id_UEIdentityIndexValue; + ie->criticality = F1AP_Criticality_reject; + ie->value.present = F1AP_PagingIEs__value_PR_UEIdentityIndexValue; + F1AP_UEIdentityIndexValue_t *ue_identity_index = &ie->value.choice.UEIdentityIndexValue; + ue_identity_index->present = F1AP_UEIdentityIndexValue_PR_indexLength10; + UEIDENTITYINDEX_TO_BIT_STRING(msg->ue_identity_index_value, &ue_identity_index->choice.indexLength10); + } + + /* PagingIdentity (Mandatory) */ + { + asn1cSequenceAdd(paging->protocolIEs.list, F1AP_PagingIEs_t, ie); + ie->id = F1AP_ProtocolIE_ID_id_PagingIdentity; + ie->criticality = F1AP_Criticality_reject; + ie->value.present = F1AP_PagingIEs__value_PR_PagingIdentity; + + F1AP_PagingIdentity_t *paging_identity = &ie->value.choice.PagingIdentity; + const f1ap_paging_identity_t *paging_id = &msg->identity; + + if (msg->identity_type == F1AP_PAGING_IDENTITY_CN_UE) { + // CN UE Paging Identity (5G-S-TMSI) + paging_identity->present = F1AP_PagingIdentity_PR_cNUEPagingIdentity; + asn1cCalloc(paging_identity->choice.cNUEPagingIdentity, id); + id->present = F1AP_CNUEPagingIdentity_PR_fiveG_S_TMSI; + FIVEG_S_TMSI_TO_BIT_STRING(paging_id->cn_ue_paging_identity, &id->choice.fiveG_S_TMSI); + } else { + // RAN UE Paging Identity (I-RNTI) + paging_identity->present = F1AP_PagingIdentity_PR_rANUEPagingIdentity; + asn1cCalloc(paging_identity->choice.rANUEPagingIdentity, ran_id); + // I-RNTI is 40 bits, stored in 5 bytes + IRNTI_TO_BIT_STRING(paging_id->ran_ue_paging_identity, &ran_id->iRNTI); + } + } + + /* Paging Priority (Optional) */ + if (msg->priority != NULL) { + AssertFatal(*msg->priority >= F1AP_PAGING_PRIO_LEVEL1 && *msg->priority <= F1AP_PAGING_PRIO_LEVEL8, + "Unknown Paging Priority: %d\n", + *msg->priority); + asn1cSequenceAdd(paging->protocolIEs.list, F1AP_PagingIEs_t, ie); + ie->id = F1AP_ProtocolIE_ID_id_PagingPriority; + ie->criticality = F1AP_Criticality_ignore; + ie->value.present = F1AP_PagingIEs__value_PR_PagingPriority; + ie->value.choice.PagingPriority = (F1AP_PagingPriority_t)*msg->priority; + } + + /* Paging Origin (Optional) */ + if (msg->origin != NULL) { + AssertFatal(*msg->origin == F1AP_PAGING_ORIGIN_NON_3GPP, "Unknown Paging Origin: %d\n", *msg->origin); + asn1cSequenceAdd(paging->protocolIEs.list, F1AP_PagingIEs_t, ie); + ie->id = F1AP_ProtocolIE_ID_id_PagingOrigin; + ie->criticality = F1AP_Criticality_ignore; + ie->value.present = F1AP_PagingIEs__value_PR_PagingOrigin; + ie->value.choice.PagingOrigin = F1AP_PagingOrigin_non_3gpp; + } + + /* PagingDRX (Optional) */ + if (msg->drx != NULL) { + asn1cSequenceAdd(paging->protocolIEs.list, F1AP_PagingIEs_t, ie); + ie->id = F1AP_ProtocolIE_ID_id_PagingDRX; + ie->criticality = F1AP_Criticality_ignore; + ie->value.present = F1AP_PagingIEs__value_PR_PagingDRX; + ie->value.choice.PagingDRX = encode_f1_paging_drx(*msg->drx); + } + + /* PagingCell_list (Mandatory) */ + { + AssertFatal(msg->n_cells > 0 && msg->n_cells <= F1AP_MAX_NB_CELLS, + "invalid n_cells %u (must be 1..%d)\n", + msg->n_cells, + F1AP_MAX_NB_CELLS); + DevAssert(msg->cells != NULL); + asn1cSequenceAdd(paging->protocolIEs.list, F1AP_PagingIEs_t, ie); + ie->id = F1AP_ProtocolIE_ID_id_PagingCell_List; + ie->criticality = F1AP_Criticality_reject; + ie->value.present = F1AP_PagingIEs__value_PR_PagingCell_list; + + // Add all cells in the paging cell list + for (uint16_t cell_idx = 0; cell_idx < msg->n_cells; cell_idx++) { + asn1cSequenceAdd(ie->value.choice.PagingCell_list, F1AP_PagingCell_ItemIEs_t, item); + item->id = F1AP_ProtocolIE_ID_id_PagingCell_Item; + item->criticality = F1AP_Criticality_reject; + item->value.present = F1AP_PagingCell_ItemIEs__value_PR_PagingCell_Item; + // NRCGI (Mandatory) + F1AP_NRCGI_t *nRCGI = &item->value.choice.PagingCell_Item.nRCGI; + const f1ap_paging_cell_item_t *cell = &msg->cells[cell_idx]; + MCC_MNC_TO_PLMNID(cell->plmn.mcc, cell->plmn.mnc, cell->plmn.mnc_digit_length, &nRCGI->pLMN_Identity); + NR_CELL_ID_TO_BIT_STRING(cell->nr_cellid, &nRCGI->nRCellIdentity); + } + } + + return pdu; +} + +/** @brief decode F1 Paging (9.2.6 Paging 3GPP TS 38.473) */ +bool decode_f1ap_paging(f1ap_paging_t *out, const F1AP_F1AP_PDU_t *pdu) +{ + DevAssert(out); + DevAssert(pdu); + DevAssert(pdu->choice.initiatingMessage); + memset(out, 0, sizeof(*out)); + + _EQ_CHECK_INT(pdu->present, F1AP_F1AP_PDU_PR_initiatingMessage); + _EQ_CHECK_LONG(pdu->choice.initiatingMessage->procedureCode, F1AP_ProcedureCode_id_Paging); + _EQ_CHECK_INT(pdu->choice.initiatingMessage->value.present, F1AP_InitiatingMessage__value_PR_Paging); + + /* Check presence of mandatory IEs */ + const F1AP_Paging_t *in = &pdu->choice.initiatingMessage->value.choice.Paging; + F1AP_PagingIEs_t *ie; + F1AP_LIB_FIND_IE(F1AP_PagingIEs_t, ie, &in->protocolIEs.list, F1AP_ProtocolIE_ID_id_UEIdentityIndexValue, true); + F1AP_LIB_FIND_IE(F1AP_PagingIEs_t, ie, &in->protocolIEs.list, F1AP_ProtocolIE_ID_id_PagingIdentity, true); + F1AP_LIB_FIND_IE(F1AP_PagingIEs_t, ie, &in->protocolIEs.list, F1AP_ProtocolIE_ID_id_PagingCell_List, true); + + /* Loop over all IEs */ + for (int i = 0; i < in->protocolIEs.list.count; i++) { + DevAssert(in->protocolIEs.list.array[i] != NULL); + ie = in->protocolIEs.list.array[i]; + switch (ie->id) { + case F1AP_ProtocolIE_ID_id_UEIdentityIndexValue: + // (M) UE Identity Index Value + _EQ_CHECK_INT(ie->value.present, F1AP_PagingIEs__value_PR_UEIdentityIndexValue); + out->ue_identity_index_value = BIT_STRING_to_uint32(&ie->value.choice.UEIdentityIndexValue.choice.indexLength10); + break; + + case F1AP_ProtocolIE_ID_id_PagingIdentity: + // (M) Paging Identity + _EQ_CHECK_INT(ie->value.present, F1AP_PagingIEs__value_PR_PagingIdentity); + if (ie->value.choice.PagingIdentity.present == F1AP_PagingIdentity_PR_cNUEPagingIdentity) { + // CN UE Paging Identity (5G-S-TMSI) + out->identity_type = F1AP_PAGING_IDENTITY_CN_UE; + BIT_STRING_t *fiveg_tmsi = &ie->value.choice.PagingIdentity.choice.cNUEPagingIdentity->choice.fiveG_S_TMSI; + out->identity.cn_ue_paging_identity = BIT_STRING_to_uint64(fiveg_tmsi); + } else if (ie->value.choice.PagingIdentity.present == F1AP_PagingIdentity_PR_rANUEPagingIdentity) { + // RAN UE Paging Identity (I-RNTI) + out->identity_type = F1AP_PAGING_IDENTITY_RAN_UE; + BIT_STRING_t *irnti = &ie->value.choice.PagingIdentity.choice.rANUEPagingIdentity->iRNTI; + out->identity.ran_ue_paging_identity = BIT_STRING_to_uint64(irnti); + } else { + PRINT_ERROR("Unknown PagingIdentity type: %d\n", ie->value.choice.PagingIdentity.present); + return false; + } + break; + + case F1AP_ProtocolIE_ID_id_PagingDRX: + // (O) Paging DRX + _EQ_CHECK_INT(ie->value.present, F1AP_PagingIEs__value_PR_PagingDRX); + out->drx = malloc_or_fail(sizeof(*out->drx)); + *out->drx = decode_f1_paging_drx(ie->value.choice.PagingDRX); + break; + + case F1AP_ProtocolIE_ID_id_PagingPriority: + // (O) Paging Priority + _EQ_CHECK_INT(ie->value.present, F1AP_PagingIEs__value_PR_PagingPriority); + AssertFatal(ie->value.choice.PagingPriority >= F1AP_PagingPriority_priolevel1 + && ie->value.choice.PagingPriority <= F1AP_PagingPriority_priolevel8, + "Unknown Paging Priority: %ld\n", + ie->value.choice.PagingPriority); + out->priority = malloc_or_fail(sizeof(*out->priority)); + *out->priority = (f1ap_paging_priority_t)ie->value.choice.PagingPriority; + break; + + case F1AP_ProtocolIE_ID_id_PagingOrigin: + // (O) Paging Origin + _EQ_CHECK_INT(ie->value.present, F1AP_PagingIEs__value_PR_PagingOrigin); + AssertFatal(ie->value.choice.PagingOrigin == F1AP_PagingOrigin_non_3gpp, + "Unknown Paging Origin: %ld\n", + ie->value.choice.PagingOrigin); + out->origin = malloc_or_fail(sizeof(*out->origin)); + *out->origin = F1AP_PAGING_ORIGIN_NON_3GPP; + break; + + case F1AP_ProtocolIE_ID_id_PagingCell_List: + // (M) Paging Cell List (max 512 cells) + _EQ_CHECK_INT(ie->value.present, F1AP_PagingIEs__value_PR_PagingCell_list); + out->n_cells = ie->value.choice.PagingCell_list.list.count; + AssertError(out->n_cells > 0, return false, "at least 1 cell must be present"); + AssertError(out->n_cells <= F1AP_MAX_NB_CELLS, return false, "too many cells"); + out->cells = calloc_or_fail(out->n_cells, sizeof(*out->cells)); + for (int j = 0; j < out->n_cells; j++) { + F1AP_PagingCell_ItemIEs_t *cell_item = (F1AP_PagingCell_ItemIEs_t *)ie->value.choice.PagingCell_list.list.array[j]; + DevAssert(cell_item); + F1AP_NRCGI_t *nRCGI = &cell_item->value.choice.PagingCell_Item.nRCGI; + f1ap_paging_cell_item_t *cell = &out->cells[j]; + PLMNID_TO_MCC_MNC(&nRCGI->pLMN_Identity, cell->plmn.mcc, cell->plmn.mnc, cell->plmn.mnc_digit_length); + BIT_STRING_TO_NR_CELL_IDENTITY(&nRCGI->nRCellIdentity, cell->nr_cellid); + } + break; + + default: + AssertError(1 == 0, return false, "F1AP_ProtocolIE_ID_id %ld unknown\n", ie->id); + break; + } + } + return true; +} + +/** @brief free F1 Paging */ +void free_f1ap_paging(f1ap_paging_t *msg) +{ + DevAssert(msg); + free(msg->drx); + free(msg->priority); + free(msg->origin); + free(msg->cells); +} + +/** @brief Equality check for two F1 Paging */ +bool eq_f1ap_paging(const f1ap_paging_t *a, const f1ap_paging_t *b) +{ + DevAssert(a); + DevAssert(b); + _EQ_CHECK_INT(a->ue_identity_index_value, b->ue_identity_index_value); + _EQ_CHECK_INT(a->identity_type, b->identity_type); + if (a->identity_type == F1AP_PAGING_IDENTITY_CN_UE) { + _EQ_CHECK_LONG(a->identity.cn_ue_paging_identity, b->identity.cn_ue_paging_identity); + } else { + _EQ_CHECK_LONG(a->identity.ran_ue_paging_identity, b->identity.ran_ue_paging_identity); + } + _EQ_CHECK_OPTIONAL_IE(a, b, drx, _EQ_CHECK_INT); + _EQ_CHECK_OPTIONAL_IE(a, b, priority, _EQ_CHECK_INT); + _EQ_CHECK_OPTIONAL_IE(a, b, origin, _EQ_CHECK_INT); + _EQ_CHECK_INT(a->n_cells, b->n_cells); + if ((a->n_cells > 0 && (a->cells == NULL || b->cells == NULL))) + return false; + for (int i = 0; i < a->n_cells; i++) { + if (!eq_f1ap_plmn(&a->cells[i].plmn, &b->cells[i].plmn)) + return false; + _EQ_CHECK_LONG(a->cells[i].nr_cellid, b->cells[i].nr_cellid); + } + return true; +} + +/** @brief deep copy F1 Paging */ +f1ap_paging_t cp_f1ap_paging(const f1ap_paging_t *orig) +{ + DevAssert(orig); + f1ap_paging_t cp = {0}; + cp.ue_identity_index_value = orig->ue_identity_index_value; + cp.identity_type = orig->identity_type; + cp.identity = orig->identity; + if (orig->drx != NULL) { + cp.drx = malloc_or_fail(sizeof(*cp.drx)); + *cp.drx = *orig->drx; + } + if (orig->priority != NULL) { + cp.priority = malloc_or_fail(sizeof(*cp.priority)); + *cp.priority = *orig->priority; + } + if (orig->origin != NULL) { + cp.origin = malloc_or_fail(sizeof(*cp.origin)); + *cp.origin = *orig->origin; + } + cp.n_cells = orig->n_cells; + if (cp.n_cells > 0) { + DevAssert(orig->cells != NULL); + cp.cells = calloc_or_fail(cp.n_cells, sizeof(*cp.cells)); + for (int i = 0; i < cp.n_cells; i++) { + cp.cells[i] = orig->cells[i]; + } + } + return cp; +} diff --git a/openair2/F1AP/lib/f1ap_paging.h b/openair2/F1AP/lib/f1ap_paging.h new file mode 100644 index 0000000000..6ecdb3daa2 --- /dev/null +++ b/openair2/F1AP/lib/f1ap_paging.h @@ -0,0 +1,37 @@ +/* + * 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 + */ + +#ifndef F1AP_PAGING_H_ +#define F1AP_PAGING_H_ + +#include +#include "f1ap_messages_types.h" + +struct F1AP_F1AP_PDU; + +/* F1 Paging */ +struct F1AP_F1AP_PDU *encode_f1ap_paging(const f1ap_paging_t *msg); +bool decode_f1ap_paging(f1ap_paging_t *out, const struct F1AP_F1AP_PDU *pdu); +void free_f1ap_paging(f1ap_paging_t *msg); +bool eq_f1ap_paging(const f1ap_paging_t *a, const f1ap_paging_t *b); +f1ap_paging_t cp_f1ap_paging(const f1ap_paging_t *orig); + +#endif /* F1AP_PAGING_H_ */ diff --git a/openair2/F1AP/tests/f1ap_lib_test.c b/openair2/F1AP/tests/f1ap_lib_test.c index e60d768b94..552db0e355 100644 --- a/openair2/F1AP/tests/f1ap_lib_test.c +++ b/openair2/F1AP/tests/f1ap_lib_test.c @@ -44,6 +44,7 @@ #include "lib/f1ap_interface_management.h" #include "lib/f1ap_ue_context.h" #include "lib/f1ap_positioning.h" +#include "lib/f1ap_paging.h" void exit_function(const char *file, const char *function, const int line, const char *s, const int assert) { @@ -2383,6 +2384,79 @@ static void test_f1ap_positioning_measurement_update() printf("%s() successful\n", __func__); } +/** @brief Test F1 Paging encode/decode roundtrip and copy operations */ +static void test_f1ap_paging_rt(f1ap_paging_t *orig) +{ + F1AP_F1AP_PDU_t *f1enc = encode_f1ap_paging(orig); + F1AP_F1AP_PDU_t *f1dec = f1ap_encode_decode(f1enc); + f1ap_msg_free(f1enc); + + f1ap_paging_t decoded = {0}; + bool ret = decode_f1ap_paging(&decoded, f1dec); + AssertFatal(ret, "decode_f1ap_paging(): could not decode message\n"); + f1ap_msg_free(f1dec); + + ret = eq_f1ap_paging(orig, &decoded); + AssertFatal(ret, "eq_f1ap_paging(): decoded message doesn't match\n"); + free_f1ap_paging(&decoded); + + f1ap_paging_t cp = cp_f1ap_paging(orig); + ret = eq_f1ap_paging(orig, &cp); + AssertFatal(ret, "eq_f1ap_paging(): copied message doesn't match\n"); + free_f1ap_paging(&cp); +} + +/** @brief Test F1 Paging encoding/decoding */ +static void test_f1ap_paging(void) +{ + // Test with CN UE Paging Identity (5G-S-TMSI) + { + f1ap_paging_t orig = {0}; + orig.ue_identity_index_value = 123; + orig.identity_type = F1AP_PAGING_IDENTITY_CN_UE; + orig.identity.cn_ue_paging_identity = 0x1234567890AB; + orig.drx = malloc_or_fail(sizeof(*orig.drx)); + *orig.drx = F1AP_PAGING_DRX_32; + orig.n_cells = 2; + orig.cells = calloc_or_fail(orig.n_cells, sizeof(*orig.cells)); + orig.cells[0].plmn.mcc = 208; + orig.cells[0].plmn.mnc = 95; + orig.cells[0].plmn.mnc_digit_length = 2; + orig.cells[0].nr_cellid = 123456; + orig.cells[1].plmn.mcc = 208; + orig.cells[1].plmn.mnc = 93; + orig.cells[1].plmn.mnc_digit_length = 2; + orig.cells[1].nr_cellid = 789012; + + test_f1ap_paging_rt(&orig); + free_f1ap_paging(&orig); + } + + // Test with RAN UE Paging Identity (I-RNTI) + { + f1ap_paging_t orig = {0}; + orig.ue_identity_index_value = 456; + orig.identity_type = F1AP_PAGING_IDENTITY_RAN_UE; + orig.identity.ran_ue_paging_identity = 0x1234567890; + orig.drx = NULL; + orig.priority = malloc_or_fail(sizeof(*orig.priority)); + *orig.priority = F1AP_PAGING_PRIO_LEVEL3; + orig.origin = malloc_or_fail(sizeof(*orig.origin)); + *orig.origin = F1AP_PAGING_ORIGIN_NON_3GPP; + orig.n_cells = 1; + orig.cells = calloc_or_fail(orig.n_cells, sizeof(*orig.cells)); + orig.cells[0].plmn.mcc = 1; + orig.cells[0].plmn.mnc = 1; + orig.cells[0].plmn.mnc_digit_length = 2; + orig.cells[0].nr_cellid = 987654; + + test_f1ap_paging_rt(&orig); + free_f1ap_paging(&orig); + } + + printf("%s() successful\n", __func__); +} + int main() { test_initial_ul_rrc_message_transfer(); @@ -2427,5 +2501,6 @@ int main() test_f1ap_positioning_measurement_abort(); test_f1ap_positioning_measurement_failure_indication(); test_f1ap_positioning_measurement_update(); + test_f1ap_paging(); return 0; } diff --git a/openair3/UTILS/conversions.h b/openair3/UTILS/conversions.h index ec65f1727a..b545d8eb8d 100644 --- a/openair3/UTILS/conversions.h +++ b/openair3/UTILS/conversions.h @@ -548,6 +548,18 @@ do { \ (bITsTRING)->bits_unused = 0; \ } while(0) +#define IRNTI_TO_BIT_STRING(mACRO, bITsTRING) \ +do { \ + (bITsTRING)->buf = calloc(5, sizeof(uint8_t)); \ + (bITsTRING)->buf[0] = ((mACRO) >> 32) & 0xff; \ + (bITsTRING)->buf[1] = ((mACRO) >> 24) & 0xff; \ + (bITsTRING)->buf[2] = ((mACRO) >> 16) & 0xff; \ + (bITsTRING)->buf[3] = ((mACRO) >> 8) & 0xff; \ + (bITsTRING)->buf[4] = ((mACRO) & 0xff); \ + (bITsTRING)->size = 5; \ + (bITsTRING)->bits_unused = 0; \ +} while (0) + /* Used to format an uint32_t containing an ipv4 address */ #define IPV4_ADDR "%u.%u.%u.%u" #define IPV4_ADDR_FORMAT(aDDRESS) \