Paging (NGAP): add paging encode/decode library and unit tests (3GPP TS 38.413)

- Refactor ngap_paging_ind_t (openair2/COMMON/ngap_messages_types.h):
  - optional paging_drx and paging_priority as pointers
  - TAI list as tai_list[] and n_tai
  - add optional IEs: Paging Origin, Paging Attempt Information,
    UE Radio Capability for Paging.
  - Introduce nr_tai_t, ngap_paging_attempt_info_t, ngap_paging_origin_t;
  - generate paging enums via FOREACH macros.
- Add NGAP Paging encode/decode/eq/free in openair3/NGAP/ngap_gNB_paging.c
  (encode_ng_paging, decode_ng_paging, eq_ng_paging, free_ng_paging).
- Replace inline Paging decode in ngap_gNB_handle_paging() with
  decode_ng_paging() and add error handling; add eq_ngap_plmn() in
  ngap_common.
- Update rrc_gNB_NGAP.c to use tai_list/n_tai and optional paging_drx
  when forwarding paging to F1AP.
- Add ASN.1 includes for Paging in ngap_msg_includes.h; register
  ngap_gNB_paging.c in root CMakeLists.txt.
- Add NGAP unit test (openair3/NGAP/tests/ngap_lib_test.c): encode/decode
  roundtrip for Paging; tests subdirectory and executable under
  ENABLE_TESTS in openair3/NGAP/CMakeLists.txt.
This commit is contained in:
Guido Casati
2026-02-17 12:25:58 +01:00
parent 41942c96d2
commit b026e6b4ee
13 changed files with 636 additions and 118 deletions
+4
View File
@@ -434,6 +434,7 @@ add_library(ngap
${NGAP_DIR}/ngap_gNB_handlers.c
${NGAP_DIR}/ngap_gNB_itti_messaging.c
${NGAP_DIR}/ngap_gNB_management_procedures.c
${NGAP_DIR}/ngap_gNB_paging.c
${NGAP_DIR}/ngap_gNB_nas_procedures.c
${NGAP_DIR}/ngap_gNB_pdu_session_management.c
${NGAP_DIR}/ngap_gNB_nnsf.c
@@ -443,6 +444,9 @@ add_library(ngap
)
target_link_libraries(ngap PUBLIC asn1_ngap)
target_link_libraries(ngap PRIVATE asn1_lte_rrc_hdrs asn1_nr_rrc_hdrs)
if(ENABLE_TESTS)
target_compile_definitions(ngap PRIVATE ENABLE_TESTS)
endif()
#M2AP
##############
+71 -53
View File
@@ -32,7 +32,6 @@
#include "common/5g_platform_types.h"
#include "common/platform_constants.h"
#include "common/platform_types.h"
#include "common/5g_platform_types.h"
#include "s1ap_messages_types.h"
#include "ds/byte_array.h"
#include "utils.h"
@@ -89,39 +88,46 @@
#define NGAP_MAX_NB_AMF_IP_ADDRESS 10
#define NGAP_MAX_NO_TAI_PAGING 16 // 9.2.4.1 3GPP TS 38.413
/* Security key length used within gNB
* Even if only 16 bytes will be effectively used,
* the key length is 32 bytes (256 bits)
*/
#define SECURITY_KEY_LENGTH 32
typedef enum ngap_paging_drx_e {
NGAP_PAGING_DRX_32 = 0x0,
NGAP_PAGING_DRX_64 = 0x1,
NGAP_PAGING_DRX_128 = 0x2,
NGAP_PAGING_DRX_256 = 0x3
} ngap_paging_drx_t;
#define NGAP_MAX_NO_TAI_PAGING 16 // 9.2.4.1 3GPP TS 38.413
/* Lower value codepoint
* indicates higher priority.
*/
typedef enum ngap_paging_priority_s {
NGAP_PAGING_PRIO_LEVEL1 = 0,
NGAP_PAGING_PRIO_LEVEL2 = 1,
NGAP_PAGING_PRIO_LEVEL3 = 2,
NGAP_PAGING_PRIO_LEVEL4 = 3,
NGAP_PAGING_PRIO_LEVEL5 = 4,
NGAP_PAGING_PRIO_LEVEL6 = 5,
NGAP_PAGING_PRIO_LEVEL7 = 6,
NGAP_PAGING_PRIO_LEVEL8 = 7
} ngap_paging_priority_t;
/* Paging DRX values (3GPP TS 38.413) */
#define FOREACH_PAGING_DRX(DRX_DEF) \
DRX_DEF(NGAP_PAGING_DRX_32, 0x0) \
DRX_DEF(NGAP_PAGING_DRX_64, 0x1) \
DRX_DEF(NGAP_PAGING_DRX_128, 0x2) \
DRX_DEF(NGAP_PAGING_DRX_256, 0x3)
typedef enum ngap_cn_domain_s {
NGAP_CN_DOMAIN_PS = 1,
NGAP_CN_DOMAIN_CS = 2
} ngap_cn_domain_t;
/* Lower value codepoint indicates higher priority (3GPP TS 38.413) */
#define FOREACH_PAGING_PRIORITY(PRIO_DEF) \
PRIO_DEF(NGAP_PAGING_PRIO_LEVEL1, 0) \
PRIO_DEF(NGAP_PAGING_PRIO_LEVEL2, 1) \
PRIO_DEF(NGAP_PAGING_PRIO_LEVEL3, 2) \
PRIO_DEF(NGAP_PAGING_PRIO_LEVEL4, 3) \
PRIO_DEF(NGAP_PAGING_PRIO_LEVEL5, 4) \
PRIO_DEF(NGAP_PAGING_PRIO_LEVEL6, 5) \
PRIO_DEF(NGAP_PAGING_PRIO_LEVEL7, 6) \
PRIO_DEF(NGAP_PAGING_PRIO_LEVEL8, 7)
/* Paging Origin (9.3.3.22 of 3GPP TS 38.413) */
#define FOREACH_PAGING_ORIGIN(ORIGIN_DEF) ORIGIN_DEF(NGAP_PAGING_ORIGIN_NON_3GPP, 0)
static const text_info_t paging_drx_text[] = {FOREACH_PAGING_DRX(TO_TEXT)};
static const text_info_t paging_prio_text[] = {FOREACH_PAGING_PRIORITY(TO_TEXT)};
static const text_info_t paging_origin_text[] = {FOREACH_PAGING_ORIGIN(TO_TEXT)};
typedef enum { FOREACH_PAGING_DRX(TO_ENUM) } ngap_paging_drx_t;
typedef enum { FOREACH_PAGING_PRIORITY(TO_ENUM) } ngap_paging_priority_t;
typedef enum { FOREACH_PAGING_ORIGIN(TO_ENUM) } ngap_paging_origin_t;
typedef struct ngap_net_ip_address_s {
unsigned ipv4:1;
@@ -171,10 +177,6 @@ typedef struct fiveg_s_tmsi_s {
uint32_t m_tmsi;
} fiveg_s_tmsi_t;
typedef struct ngap_ue_paging_identity_s {
fiveg_s_tmsi_t s_tmsi;
} ngap_ue_paging_identity_t;
typedef enum ngap_ue_identities_presenceMask_e {
NGAP_UE_IDENTITIES_FiveG_s_tmsi = 1 << 1,
NGAP_UE_IDENTITIES_guami = 1 << 2,
@@ -797,29 +799,6 @@ typedef struct ngap_initial_context_setup_req_s {
byte_array_t nas_pdu;
} ngap_initial_context_setup_req_t;
typedef struct ngap_paging_ind_s {
/* UE paging identity */
ngap_ue_paging_identity_t ue_paging_identity;
/* Indicates origin of paging */
ngap_cn_domain_t cn_domain;
/* PLMN_identity in TAI of Paging*/
plmn_id_t plmn_identity[NGAP_MAX_NO_TAI_PAGING];
/* TAC in TAIList of Paging*/
int16_t tac[NGAP_MAX_NO_TAI_PAGING];
/* size of TAIList*/
int16_t tai_size;
/* Optional fields */
ngap_paging_drx_t paging_drx;
ngap_paging_priority_t paging_priority;
} ngap_paging_ind_t;
typedef struct ngap_pdusession_setup_req_s {
/* UE id for initial connection to NGAP */
uint32_t gNB_ue_ngap_id;
@@ -946,6 +925,45 @@ typedef struct ngap_pdusession_release_resp_s {
pdusession_release_t pdusession_release[NGAP_MAX_PDU_SESSION];
} ngap_pdusession_release_resp_t;
/** NG PAGING PROCEDURES (9.2.4. of 3GPP TS 38.413) */
typedef struct {
plmn_id_t plmn;
uint16_t tac;
} nr_tai_t;
typedef struct ngap_ue_paging_identity_s {
fiveg_s_tmsi_t s_tmsi;
} ngap_ue_paging_identity_t;
/** 9.3.1.72 Paging Attempt Information (3GPP TS 38.413) */
typedef struct {
/* Paging Attempt Count */
uint8_t paging_attempt_count;
/* Intended Number of Paging Attempts */
uint8_t intended_paging_attempts;
} ngap_paging_attempt_info_t;
/** 9.2.4.1 3GPP TS 38.413 */
typedef struct {
/* UE paging identity */
ngap_ue_paging_identity_t ue_paging_identity;
/* TAI List for Paging */
nr_tai_t tai_list[NGAP_MAX_NO_TAI_PAGING];
int16_t n_tai;
/* Optional fields */
ngap_paging_drx_t *paging_drx;
ngap_paging_priority_t *paging_priority;
/* UE Radio Capability for Paging (optional) */
byte_array_t *ue_radio_capability;
/* Paging Origin (optional) */
ngap_paging_origin_t *origin;
/* Assistance Data for Paging (optional) */
ngap_paging_attempt_info_t *paging_attempt_info;
} ngap_paging_ind_t;
/** 9.2.3.14 Uplink RAN Status Transfer (3GPP TS 38.413)
* COUNT value used for both UL and DL PDCP SN + HFN (12-bit or 18-bit SN) */
+6 -5
View File
@@ -1632,19 +1632,20 @@ int rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND(ngap_pdusession_release_comm
int rrc_gNB_process_PAGING_IND(MessageDef *msg_p, instance_t instance)
{
ngap_paging_ind_t *msg = &NGAP_PAGING_IND(msg_p);
for (uint16_t tai_size = 0; tai_size < msg->tai_size; tai_size++) {
plmn_id_t *p = &msg->plmn_identity[tai_size];
for (uint16_t tai_idx = 0; tai_idx < msg->n_tai; tai_idx++) {
nr_tai_t *tai = &msg->tai_list[tai_idx];
plmn_id_t *p = &tai->plmn;
LOG_I(NR_RRC,
"[gNB %ld] TAI List for Paging: MCC=%03d, MNC=%0*d, TAC=%d\n",
instance,
p->mcc,
p->mnc_digit_length,
p->mnc,
msg->tac[tai_size]);
tai->tac);
nr_rrc_config_t *req = &RC.nrrrc[instance]->configuration;
for (uint8_t j = 0; j < req->num_plmn; j++) {
plmn_id_t *req_plmn = &req->plmn[j];
if (req_plmn->mcc == p->mcc && req_plmn->mnc == p->mnc && req->tac == msg->tac[tai_size]) {
if (req_plmn->mcc == p->mcc && req_plmn->mnc == p->mnc && req->tac == tai->tac) {
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)) {
@@ -1669,7 +1670,7 @@ int rrc_gNB_process_PAGING_IND(MessageDef *msg_p, instance_t instance)
F1AP_PAGING_IND(m).nr_cellid = cell->info.cell_id;
F1AP_PAGING_IND(m).ueidentityindexvalue = (uint16_t)(msg->ue_paging_identity.s_tmsi.m_tmsi % 1024);
F1AP_PAGING_IND(m).fiveg_s_tmsi = msg->ue_paging_identity.s_tmsi.m_tmsi;
F1AP_PAGING_IND(m).paging_drx = msg->paging_drx;
F1AP_PAGING_IND(m).paging_drx = msg->paging_drx ? (uint8_t)*msg->paging_drx : 0;
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 {
+4
View File
@@ -1 +1,5 @@
add_subdirectory(MESSAGES)
if(ENABLE_TESTS)
add_subdirectory(tests)
endif()
+12
View File
@@ -27,6 +27,7 @@
* \version 0.1
*/
#include <stdbool.h>
#include <stdint.h>
#include <arpa/inet.h>
#include "conversions.h"
@@ -331,3 +332,14 @@ byte_array_t encode_ngap_pdusession_setup_response_transfer(const pdusession_set
out.len = res.result.encoded;
return out;
}
bool eq_ngap_plmn(const plmn_id_t *a, const plmn_id_t *b)
{
if (a == NULL || b == NULL) {
return false;
}
_EQ_CHECK_INT(a->mcc, b->mcc);
_EQ_CHECK_INT(a->mnc, b->mnc);
_EQ_CHECK_INT(a->mnc_digit_length, b->mnc_digit_length);
return true;
}
+2
View File
@@ -63,5 +63,7 @@ void *decode_pdusession_transfer(const asn_TYPE_descriptor_t *td, const OCTET_ST
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);
bool eq_ngap_plmn(const plmn_id_t *a, const plmn_id_t *b);
/** @}*/
#endif /* NGAP_COMMON_H_ */
+11 -60
View File
@@ -49,6 +49,7 @@
#include "ngap_gNB_mobility_management.h"
#include "ngap_gNB_pdu_session_management.h"
#include "ngap_gNB_nas_procedures.h"
#include "ngap_gNB_paging.h"
#include "ngap_gNB_trace.h"
#include "ngap_gNB_ue_context.h"
#include "ngap_messages_types.h"
@@ -1101,10 +1102,7 @@ static int ngap_gNB_handle_paging(sctp_assoc_t assoc_id, uint32_t stream, NGAP_N
{
ngap_gNB_amf_data_t *amf_desc_p = NULL;
ngap_gNB_instance_t *ngap_gNB_instance = NULL;
NGAP_Paging_t *container;
NGAP_PagingIEs_t *ie;
DevAssert(pdu != NULL);
container = &pdu->choice.initiatingMessage->value.choice.Paging;
// received Paging Message from AMF
NGAP_DEBUG("[SCTP %u] Received Paging Message From AMF\n",assoc_id);
@@ -1130,67 +1128,20 @@ static int ngap_gNB_handle_paging(sctp_assoc_t assoc_id, uint32_t stream, NGAP_N
}
MessageDef *message_p = itti_alloc_new_message(TASK_NGAP, 0, NGAP_PAGING_IND);
ngap_paging_ind_t * msg=&NGAP_PAGING_IND(message_p);
memset(msg, 0, sizeof(*msg));
ngap_paging_ind_t *msg = &NGAP_PAGING_IND(message_p);
/* convert NGAP_PagingIEs_t to ngap_paging_ind_t */
/* id-UEIdentityIndexValue : convert UE Identity Index value */
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PagingIEs_t, ie, container, NGAP_ProtocolIE_ID_id_UEPagingIdentity, true);
struct NGAP_FiveG_S_TMSI *fiveG_S_TMSI = ie->value.choice.UEPagingIdentity.choice.fiveG_S_TMSI;
OCTET_STRING_TO_INT16(&fiveG_S_TMSI->aMFSetID, msg->ue_paging_identity.s_tmsi.amf_set_id);
OCTET_STRING_TO_INT8(&fiveG_S_TMSI->aMFPointer, msg->ue_paging_identity.s_tmsi.amf_pointer);
OCTET_STRING_TO_INT32(&fiveG_S_TMSI->fiveG_TMSI, msg->ue_paging_identity.s_tmsi.m_tmsi);
NGAP_DEBUG("[SCTP %u] Received Paging Identity amf_set_id %d, amf_pointer %d, m_tmsi %d\n",
assoc_id,
msg->ue_paging_identity.s_tmsi.amf_set_id,
msg->ue_paging_identity.s_tmsi.amf_pointer,
msg->ue_paging_identity.s_tmsi.m_tmsi);
msg->paging_drx = NGAP_PAGING_DRX_256;
/* id-pagingDRX */
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PagingIEs_t, ie, container, NGAP_ProtocolIE_ID_id_PagingDRX, false);
/* optional */
if (ie) {
msg->paging_drx = ie->value.choice.PagingDRX;
} else {
msg->paging_drx = NGAP_PAGING_DRX_256;
/* Decode NGAP Paging message */
if (!decode_ng_paging(msg, pdu)) {
NGAP_ERROR("[SCTP %u] Failed to decode NGAP Paging message\n", assoc_id);
free_ng_paging(msg);
itti_free(TASK_NGAP, message_p);
return -1;
}
/* id-TAIList */
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PagingIEs_t, ie, container,
NGAP_ProtocolIE_ID_id_TAIListForPaging, true);
/* send message to RRC */
itti_send_msg_to_task(TASK_RRC_GNB, ngap_gNB_instance->instance, message_p);
NGAP_INFO("[SCTP %u] Received Paging taiList For Paging: count %d\n", assoc_id, ie->value.choice.TAIListForPaging.list.count);
for (int i = 0; i < ie->value.choice.TAIListForPaging.list.count; i++) {
NGAP_TAIListForPagingItem_t *item_p;
item_p = (NGAP_TAIListForPagingItem_t *)ie->value.choice.TAIListForPaging.list.array[i];
TBCD_TO_MCC_MNC(&(item_p->tAI.pLMNIdentity), msg->plmn_identity[i].mcc, msg->plmn_identity[i].mnc, msg->plmn_identity[i].mnc_digit_length);
OCTET_STRING_TO_INT16(&(item_p->tAI.tAC), msg->tac[i]);
msg->tai_size++;
plmn_id_t *p = &msg->plmn_identity[i];
LOG_D(NGAP,
"[SCTP %u] PLMN in TAI list for Paging: MCC=%03d, MNC=%0*d, TAC=%d\n",
assoc_id,
p->mcc,
p->mnc_digit_length,
p->mnc,
msg->tac[i]);
}
//paging parameter values
NGAP_DEBUG("[SCTP %u] Received Paging parameters: Paging Identity amf_set_id %d amf_pointer %d m_tmsi %d paging_drx %d paging_priority %d\n",assoc_id,
msg->ue_paging_identity.s_tmsi.amf_set_id,
msg->ue_paging_identity.s_tmsi.amf_pointer,
msg->ue_paging_identity.s_tmsi.m_tmsi,
msg->paging_drx, msg->paging_priority);
/* send message to RRC */
itti_send_msg_to_task(TASK_RRC_GNB, ngap_gNB_instance->instance, message_p);
return 0;
return 0;
}
static bool decodePDUSessionResourceModify(pdusession_transfer_t *out, const OCTET_STRING_t in)
+330
View File
@@ -0,0 +1,330 @@
/*
* 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 Software Alliance:
* contact@openairinterface.org
*/
#include <string.h>
#include "assertions.h"
#include "common/utils/utils.h"
#include "common/utils/ds/byte_array.h"
#include "conversions.h"
#include "ngap_common.h"
#include "ngap_gNB_paging.h"
#include "ngap_msg_includes.h"
#include "oai_asn1.h"
/* Paging Attempt range (TS 38.413 §9.3.1.72) */
#define NGAP_PAGING_ATTEMPT_MIN 1
#define NGAP_PAGING_ATTEMPT_MAX 16
/** @brief Return text label at index in text_info array */
static const char *text_info_at(int index, const text_info_t *info_array, size_t array_size)
{
DevAssert(index >= 0 && index < array_size);
return info_array[index].text;
}
/** @brief Encode NGAP Paging message (9.2.4.1 of 3GPP TS 38.413)
* @param msg Pointer to paging indication structure to encode
* @return Pointer to allocated NGAP_NGAP_PDU_t */
NGAP_NGAP_PDU_t *encode_ng_paging(const ngap_paging_ind_t *msg)
{
DevAssert(msg != NULL);
NGAP_NGAP_PDU_t *pdu = calloc_or_fail(1, sizeof(NGAP_NGAP_PDU_t));
pdu->present = NGAP_NGAP_PDU_PR_initiatingMessage;
asn1cCalloc(pdu->choice.initiatingMessage, head);
head->procedureCode = NGAP_ProcedureCode_id_Paging;
head->criticality = NGAP_Criticality_ignore;
head->value.present = NGAP_InitiatingMessage__value_PR_Paging;
NGAP_Paging_t *container = &head->value.choice.Paging;
// UE Paging Identity (M)
asn1cSequenceAdd(container->protocolIEs.list, NGAP_PagingIEs_t, ie);
ie->id = NGAP_ProtocolIE_ID_id_UEPagingIdentity;
ie->criticality = NGAP_Criticality_reject;
ie->value.present = NGAP_PagingIEs__value_PR_UEPagingIdentity;
ie->value.choice.UEPagingIdentity.present = NGAP_UEPagingIdentity_PR_fiveG_S_TMSI;
asn1cCalloc(ie->value.choice.UEPagingIdentity.choice.fiveG_S_TMSI, fiveg_s_tmsi);
const fiveg_s_tmsi_t *s_tmsi = &msg->ue_paging_identity.s_tmsi;
AMF_SETID_TO_BIT_STRING(s_tmsi->amf_set_id, &fiveg_s_tmsi->aMFSetID);
AMF_POINTER_TO_BIT_STRING(s_tmsi->amf_pointer, &fiveg_s_tmsi->aMFPointer);
M_TMSI_TO_OCTET_STRING(s_tmsi->m_tmsi, &fiveg_s_tmsi->fiveG_TMSI);
// TAI List for Paging (M)
asn1cSequenceAdd(container->protocolIEs.list, NGAP_PagingIEs_t, ie1);
ie1->id = NGAP_ProtocolIE_ID_id_TAIListForPaging;
ie1->criticality = NGAP_Criticality_reject;
ie1->value.present = NGAP_PagingIEs__value_PR_TAIListForPaging;
for (int i = 0; i < msg->n_tai; i++) {
asn1cSequenceAdd(ie1->value.choice.TAIListForPaging.list, NGAP_TAIListForPagingItem_t, item);
NGAP_TAI_t *tai = &item->tAI;
const plmn_id_t *plmn = &msg->tai_list[i].plmn;
MCC_MNC_TO_TBCD(plmn->mcc, plmn->mnc, plmn->mnc_digit_length, &tai->pLMNIdentity);
INT24_TO_OCTET_STRING(msg->tai_list[i].tac, &tai->tAC);
}
// Paging DRX (O)
if (msg->paging_drx != NULL) {
asn1cSequenceAdd(container->protocolIEs.list, NGAP_PagingIEs_t, ie2);
ie2->id = NGAP_ProtocolIE_ID_id_PagingDRX;
ie2->criticality = NGAP_Criticality_ignore;
ie2->value.present = NGAP_PagingIEs__value_PR_PagingDRX;
ie2->value.choice.PagingDRX = *msg->paging_drx;
}
// Paging Priority (O)
if (msg->paging_priority != NULL) {
asn1cSequenceAdd(container->protocolIEs.list, NGAP_PagingIEs_t, ie3);
ie3->id = NGAP_ProtocolIE_ID_id_PagingPriority;
ie3->criticality = NGAP_Criticality_ignore;
ie3->value.present = NGAP_PagingIEs__value_PR_PagingPriority;
ie3->value.choice.PagingPriority = *msg->paging_priority;
}
// UERadioCapabilityForPaging (O)
if (msg->ue_radio_capability != NULL && msg->ue_radio_capability->buf != NULL) {
const byte_array_t *ue_radio_cap_ba = msg->ue_radio_capability;
asn1cSequenceAdd(container->protocolIEs.list, NGAP_PagingIEs_t, ie4);
ie4->id = NGAP_ProtocolIE_ID_id_UERadioCapabilityForPaging;
ie4->criticality = NGAP_Criticality_ignore;
ie4->value.present = NGAP_PagingIEs__value_PR_UERadioCapabilityForPaging;
NGAP_UERadioCapabilityForPaging_t *ue_radio_cap = &ie4->value.choice.UERadioCapabilityForPaging;
// UERadioCapabilityForPaging is a SEQUENCE with optional OCTET_STRING fields (Encode as NR)
asn1cCalloc(ue_radio_cap->uERadioCapabilityForPagingOfNR, nr_field);
OCTET_STRING_fromBuf(nr_field, (const char *)ue_radio_cap_ba->buf, ue_radio_cap_ba->len);
}
// PagingOrigin (O)
if (msg->origin != NULL) {
asn1cSequenceAdd(container->protocolIEs.list, NGAP_PagingIEs_t, ie5);
ie5->id = NGAP_ProtocolIE_ID_id_PagingOrigin;
ie5->criticality = NGAP_Criticality_ignore;
ie5->value.present = NGAP_PagingIEs__value_PR_PagingOrigin;
ie5->value.choice.PagingOrigin = *msg->origin;
}
return pdu;
}
/** @brief Free memory allocated for NGAP paging indication
* @param msg Pointer to paging indication structure to free */
void free_ng_paging(ngap_paging_ind_t *msg)
{
DevAssert(msg != NULL);
free(msg->paging_drx);
free(msg->paging_priority);
if (msg->ue_radio_capability != NULL) {
free_byte_array(*msg->ue_radio_capability);
free(msg->ue_radio_capability);
}
free(msg->origin);
free(msg->paging_attempt_info);
}
/** @brief Decode NGAP Paging message (9.2.4.1 of 3GPP TS 38.413)
* @param out Pointer to output structure to fill
* @param pdu Pointer to NGAP PDU containing the Paging message
* @return true on success, false on failure */
bool decode_ng_paging(ngap_paging_ind_t *out, const NGAP_NGAP_PDU_t *pdu)
{
DevAssert(pdu != NULL);
DevAssert(out != NULL);
if (pdu->present != NGAP_NGAP_PDU_PR_initiatingMessage) {
NGAP_ERROR("Invalid PDU present value for Paging message\n");
return false;
}
const NGAP_Paging_t *container = &pdu->choice.initiatingMessage->value.choice.Paging;
NGAP_PagingIEs_t *ie;
fiveg_s_tmsi_t *s_tmsi = &out->ue_paging_identity.s_tmsi;
// Reset output structure
memset(out, 0, sizeof(*out));
// UE Paging Identity (M)
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PagingIEs_t, ie, container, NGAP_ProtocolIE_ID_id_UEPagingIdentity, true);
if (ie->value.choice.UEPagingIdentity.present == NGAP_UEPagingIdentity_PR_fiveG_S_TMSI) {
const struct NGAP_FiveG_S_TMSI *fiveG_S_TMSI = ie->value.choice.UEPagingIdentity.choice.fiveG_S_TMSI;
s_tmsi->amf_set_id = BIT_STRING_to_uint16(&fiveG_S_TMSI->aMFSetID);
s_tmsi->amf_pointer = BIT_STRING_to_uint8(&fiveG_S_TMSI->aMFPointer);
OCTET_STRING_TO_INT32(&fiveG_S_TMSI->fiveG_TMSI, s_tmsi->m_tmsi);
} else {
NGAP_ERROR("Unsupported UE Paging Identity type\n");
return false;
}
// TAI List for Paging (M)
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PagingIEs_t, ie, container, NGAP_ProtocolIE_ID_id_TAIListForPaging, true);
// Validate TAI list count
int tai_count = ie->value.choice.TAIListForPaging.list.count;
if (tai_count > NGAP_MAX_NO_TAI_PAGING) {
NGAP_ERROR("Invalid TAI list count: %d (must be <= %d)\n", tai_count, NGAP_MAX_NO_TAI_PAGING);
return false;
}
out->n_tai = 0;
for (int i = 0; i < tai_count; i++) {
const NGAP_TAIListForPagingItem_t *item_p = ie->value.choice.TAIListForPaging.list.array[i];
DevAssert(item_p != NULL);
const NGAP_TAI_t *tai = &item_p->tAI;
nr_tai_t *tai_out = &out->tai_list[i];
TBCD_TO_MCC_MNC(&tai->pLMNIdentity, tai_out->plmn.mcc, tai_out->plmn.mnc, tai_out->plmn.mnc_digit_length);
OCTET_STRING_TO_INT24(&tai->tAC, tai_out->tac);
NGAP_DEBUG("TAI[%d] for paging: MCC=%03d, MNC=%0*d, TAC=%d\n",
i,
tai_out->plmn.mcc,
tai_out->plmn.mnc_digit_length,
tai_out->plmn.mnc,
tai_out->tac);
out->n_tai++;
}
// Paging DRX (O)
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PagingIEs_t, ie, container, NGAP_ProtocolIE_ID_id_PagingDRX, false);
if (ie != NULL) {
out->paging_drx = malloc_or_fail(sizeof(ngap_paging_drx_t));
*out->paging_drx = ie->value.choice.PagingDRX;
const char *s = text_info_at(*out->paging_drx, paging_drx_text, sizeofArray(paging_drx_text));
NGAP_DEBUG("Decoded Paging DRX: %s", s);
}
// Paging Priority (O)
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PagingIEs_t, ie, container, NGAP_ProtocolIE_ID_id_PagingPriority, false);
if (ie != NULL) {
out->paging_priority = malloc_or_fail(sizeof(ngap_paging_priority_t));
*out->paging_priority = ie->value.choice.PagingPriority;
const char *s = text_info_at(*out->paging_priority, paging_prio_text, sizeofArray(paging_prio_text));
NGAP_DEBUG("Decoded Paging Priority: %s", s);
}
// UERadioCapabilityForPaging (O)
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PagingIEs_t, ie, container, NGAP_ProtocolIE_ID_id_UERadioCapabilityForPaging, false);
if (ie != NULL) {
const NGAP_UERadioCapabilityForPaging_t *ue_radio_cap = &ie->value.choice.UERadioCapabilityForPaging;
const OCTET_STRING_t *nr_field = ue_radio_cap->uERadioCapabilityForPagingOfNR;
const OCTET_STRING_t *eutra_field = ue_radio_cap->uERadioCapabilityForPagingOfEUTRA;
if (nr_field != NULL) {
out->ue_radio_capability = malloc_or_fail(sizeof(byte_array_t));
*out->ue_radio_capability = create_byte_array(nr_field->size, nr_field->buf);
NGAP_DEBUG("Decoded UERadioCapabilityForPaging (NR): %zu bytes\n", nr_field->size);
} else if (eutra_field != NULL) {
out->ue_radio_capability = malloc_or_fail(sizeof(byte_array_t));
*out->ue_radio_capability = create_byte_array(eutra_field->size, eutra_field->buf);
NGAP_DEBUG("Decoded UERadioCapabilityForPaging (EUTRA): %zu bytes\n", eutra_field->size);
} else {
NGAP_WARN("UERadioCapabilityForPaging present but both NR and EUTRA fields are NULL\n");
}
}
// PagingOrigin (O)
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PagingIEs_t, ie, container, NGAP_ProtocolIE_ID_id_PagingOrigin, false);
if (ie != NULL) {
// PagingOrigin shall be transferred to UE according to TS 38.331 and TS 36.331
out->origin = calloc_or_fail(1, sizeof(*out->origin));
*out->origin = ie->value.choice.PagingOrigin;
}
// AssistanceDataForPaging (O) - contains PagingAttemptInformation
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PagingIEs_t, ie, container, NGAP_ProtocolIE_ID_id_AssistanceDataForPaging, false);
if (ie != NULL) {
const NGAP_AssistanceDataForPaging_t *assistance_data = &ie->value.choice.AssistanceDataForPaging;
// Extract PagingAttemptInformation if present (TS 38.413 §9.3.1.72)
if (assistance_data->pagingAttemptInformation != NULL) {
const NGAP_PagingAttemptInformation_t *paging_attempt_info = assistance_data->pagingAttemptInformation;
out->paging_attempt_info = calloc_or_fail(1, sizeof(ngap_paging_attempt_info_t));
// Paging Attempt Count (mandatory)
uint8_t attempt_count = paging_attempt_info->pagingAttemptCount;
DevAssert(attempt_count >= NGAP_PAGING_ATTEMPT_MIN && attempt_count <= NGAP_PAGING_ATTEMPT_MAX);
out->paging_attempt_info->paging_attempt_count = attempt_count;
NGAP_DEBUG("Decoded Paging Attempt Count: %u\n", out->paging_attempt_info->paging_attempt_count);
// Intended Number of Paging Attempts (mandatory)
uint8_t intended_attempts = paging_attempt_info->intendedNumberOfPagingAttempts;
DevAssert(intended_attempts >= NGAP_PAGING_ATTEMPT_MIN && intended_attempts <= NGAP_PAGING_ATTEMPT_MAX);
out->paging_attempt_info->intended_paging_attempts = intended_attempts;
NGAP_DEBUG("Decoded Intended Number of Paging Attempts: %u\n", out->paging_attempt_info->intended_paging_attempts);
}
}
NGAP_DEBUG("Decoded Paging: m_tmsi=0x%08x, TAI=%d", s_tmsi->m_tmsi, out->n_tai);
return true;
}
/** @brief Check equality of two NGAP paging indication structures
* @param a First structure
* @param b Second structure
* @return true if equal, false otherwise */
bool eq_ng_paging(const ngap_paging_ind_t *a, const ngap_paging_ind_t *b)
{
if (a == NULL || b == NULL) {
return a == b;
}
// UE Paging Identity
const fiveg_s_tmsi_t *a_tmsi = &a->ue_paging_identity.s_tmsi;
const fiveg_s_tmsi_t *b_tmsi = &b->ue_paging_identity.s_tmsi;
_EQ_CHECK_INT(a_tmsi->amf_set_id, b_tmsi->amf_set_id);
_EQ_CHECK_INT(a_tmsi->amf_pointer, b_tmsi->amf_pointer);
_EQ_CHECK_UINT32(a_tmsi->m_tmsi, b_tmsi->m_tmsi);
// TAI List
_EQ_CHECK_INT(a->n_tai, b->n_tai);
for (int i = 0; i < a->n_tai; i++) {
if (!eq_ngap_plmn(&a->tai_list[i].plmn, &b->tai_list[i].plmn)) {
return false;
}
_EQ_CHECK_INT(a->tai_list[i].tac, b->tai_list[i].tac);
}
// Paging DRX (optional)
_EQ_CHECK_OPTIONAL_IE(a, b, paging_drx, _EQ_CHECK_INT);
// Paging Priority (optional)
_EQ_CHECK_OPTIONAL_IE(a, b, paging_priority, _EQ_CHECK_INT);
// UERadioCapabilityForPaging (optional)
_EQ_CHECK_OPTIONAL_PTR(a, b, ue_radio_capability);
if (a->ue_radio_capability != NULL && b->ue_radio_capability != NULL) {
if (!eq_byte_array(a->ue_radio_capability, b->ue_radio_capability)) {
NGAP_ERROR("Equality condition 'eq_byte_array' failed: byte array contents differ\n");
return false;
}
}
// PagingOrigin (optional)
_EQ_CHECK_OPTIONAL_IE(a, b, origin, _EQ_CHECK_INT);
// Assistance Data for Paging (optional)
_EQ_CHECK_OPTIONAL_PTR(a, b, paging_attempt_info);
if (a->paging_attempt_info != NULL && b->paging_attempt_info != NULL) {
_EQ_CHECK_INT(a->paging_attempt_info->paging_attempt_count, b->paging_attempt_info->paging_attempt_count);
_EQ_CHECK_INT(a->paging_attempt_info->intended_paging_attempts, b->paging_attempt_info->intended_paging_attempts);
}
return true;
}
+50
View File
@@ -0,0 +1,50 @@
/*
* 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 Software Alliance:
* contact@openairinterface.org
*/
#ifndef NGAP_GNB_PAGING_H_
#define NGAP_GNB_PAGING_H_
#include <stdbool.h>
#include <stdint.h>
#include "ngap_messages_types.h"
/** @brief Encode NGAP Paging message (9.2.4.1 of 3GPP TS 38.413)
* @param msg Pointer to paging indication structure to encode
* @return Pointer to allocated NGAP_NGAP_PDU_t, or NULL on failure */
NGAP_NGAP_PDU_t *encode_ng_paging(const ngap_paging_ind_t *msg);
/** @brief Decode NGAP Paging message (9.2.4.1 of 3GPP TS 38.413)
* @param out Pointer to output structure to fill
* @param pdu Pointer to NGAP PDU containing the Paging message
* @return true on success, false on failure */
bool decode_ng_paging(ngap_paging_ind_t *out, const NGAP_NGAP_PDU_t *pdu);
/** @brief Free memory allocated for NGAP paging indication
* @param msg Pointer to paging indication structure to free */
void free_ng_paging(ngap_paging_ind_t *msg);
/** @brief Check equality of two NGAP paging indication structures
* @param a First structure
* @param b Second structure
* @return true if equal, false otherwise */
bool eq_ng_paging(const ngap_paging_ind_t *a, const ngap_paging_ind_t *b);
#endif /* NGAP_GNB_PAGING_H_ */
+6
View File
@@ -41,6 +41,12 @@
#include "NGAP_GTPTunnel.h"
#include "NGAP_InitiatingMessage.h"
#include "NGAP_NGAP-PDU.h"
#include "NGAP_Paging.h"
#include "NGAP_AssistanceDataForPaging.h"
#include "NGAP_PagingAttemptInformation.h"
#include "NGAP_FiveG-S-TMSI.h"
#include "NGAP_TAI.h"
#include "NGAP_UEPagingIdentity.h"
#include "NGAP_PDUSessionResourceFailedToModifyItemModRes.h"
#include "NGAP_PDUSessionResourceFailedToSetupItemCxtRes.h"
#include "NGAP_PDUSessionResourceFailedToSetupItemSURes.h"
+1
View File
@@ -27,6 +27,7 @@
#include <string.h>
#include "common/utils/LOG/log.h"
#include "assertions.h"
#include "common/utils/eq_check.h"
#define NGAP_UE_ID_FMT "0x%06" PRIX32
+6
View File
@@ -0,0 +1,6 @@
add_executable(ngap_lib_test ngap_lib_test.c)
add_dependencies(tests ngap_lib_test)
add_test(NAME ngap_lib_test COMMAND ngap_lib_test)
target_link_libraries(ngap_lib_test PRIVATE ngap asn1_ngap asn1_lte_rrc_hdrs UTIL ds)
target_include_directories(ngap_lib_test PRIVATE ${CMAKE_SOURCE_DIR})
+133
View File
@@ -0,0 +1,133 @@
/*
* 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 Software Alliance:
* contact@openairinterface.org
*/
#include <stdlib.h> // abort
#include <stdint.h> // uint*_t
#include <stdio.h> // printf
#include <stdbool.h> // bool
#include "common/utils/assertions.h" // AssertFatal, DevAssert
#include "common/utils/utils.h" // malloc_or_fail, calloc_or_fail
#include "common/utils/ds/byte_array.h" // create_byte_array
#include "conversions.h" // TBCD_TO_MCC_MNC, OCTET_STRING_TO_INT*
#include "ngap_common.h" // NGAP_ERROR, NGAP_DEBUG, ngap_messages_types.h
#include "ngap_gNB_paging.h" // encode_ng_paging, decode_ng_paging, free_ng_paging
#include "ngap_msg_includes.h" // NGAP_NGAP_PDU_t, NGAP_Paging_t, etc.
#include "oai_asn1.h" // ASN_STRUCT_FREE, asn_DEF_NGAP_NGAP_PDU
#include "config/config_load_configmodule.h" // configmodule_interface_t
#include "common/utils/LOG/log.h" // log_init
void exit_function(const char *file, const char *function, const int line, const char *s, const int assert)
{
printf("detected error at %s:%d:%s: %s\n", file, line, function, s);
abort();
}
static NGAP_NGAP_PDU_t *ngap_encode_decode(const NGAP_NGAP_PDU_t *enc_pdu)
{
DevAssert(enc_pdu != NULL);
char errbuf[1024];
size_t errlen = sizeof(errbuf);
int ret = asn_check_constraints(&asn_DEF_NGAP_NGAP_PDU, enc_pdu, errbuf, &errlen);
AssertFatal(ret == 0, "asn_check_constraints() failed: %s\n", errbuf);
uint8_t msgbuf[16384];
asn_enc_rval_t enc = aper_encode_to_buffer(&asn_DEF_NGAP_NGAP_PDU, NULL, enc_pdu, msgbuf, sizeof(msgbuf));
AssertFatal(enc.encoded > 0, "aper_encode_to_buffer() failed\n");
NGAP_NGAP_PDU_t *dec_pdu = NULL;
asn_codec_ctx_t st = {.max_stack_size = 100 * 1000};
asn_dec_rval_t dec = aper_decode(&st, &asn_DEF_NGAP_NGAP_PDU, (void **)&dec_pdu, msgbuf, enc.encoded, 0, 0);
AssertFatal(dec.code == RC_OK, "aper_decode() failed\n");
return dec_pdu;
}
static void ngap_msg_free(NGAP_NGAP_PDU_t *pdu)
{
ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, pdu);
}
/** @brief Test NGAP Paging encoding/decoding roundtrip */
static void test_ngap_paging_encode_decode(void)
{
// Create test paging indication
ngap_paging_ind_t orig = {0};
orig.ue_paging_identity.s_tmsi.amf_set_id = 123; // Valid range: 0-1023 (10 bits)
orig.ue_paging_identity.s_tmsi.amf_pointer = 45; // Valid range: 0-63 (6 bits)
orig.ue_paging_identity.s_tmsi.m_tmsi = 0x12345678;
orig.n_tai = 2;
orig.tai_list[0].plmn.mcc = 208;
orig.tai_list[0].plmn.mnc = 95;
orig.tai_list[0].plmn.mnc_digit_length = 2;
orig.tai_list[0].tac = 12345;
orig.tai_list[1].plmn.mcc = 001;
orig.tai_list[1].plmn.mnc = 01;
orig.tai_list[1].plmn.mnc_digit_length = 2;
orig.tai_list[1].tac = 23456;
orig.paging_drx = malloc_or_fail(sizeof(ngap_paging_drx_t));
*orig.paging_drx = NGAP_PAGING_DRX_128;
orig.paging_priority = malloc_or_fail(sizeof(ngap_paging_priority_t));
*orig.paging_priority = NGAP_PAGING_PRIO_LEVEL2;
uint8_t ue_cap_data[] = {0x01, 0x02, 0x03, 0x04};
orig.ue_radio_capability = malloc_or_fail(sizeof(byte_array_t));
*orig.ue_radio_capability = create_byte_array(sizeof(ue_cap_data), ue_cap_data);
orig.origin = calloc_or_fail(1, sizeof(*orig.origin));
*orig.origin = NGAP_PAGING_ORIGIN_NON_3GPP;
// Encode
NGAP_NGAP_PDU_t *enc_pdu = encode_ng_paging(&orig);
AssertFatal(enc_pdu != NULL, "encode_ng_paging(): failed to encode message\n");
LOG_A(NGAP, "encoded: m_tmsi=0x%x, n_tai=%d\n", orig.ue_paging_identity.s_tmsi.m_tmsi, orig.n_tai);
// Encode/decode roundtrip
NGAP_NGAP_PDU_t *dec_pdu = ngap_encode_decode(enc_pdu);
ngap_msg_free(enc_pdu);
// Decode
ngap_paging_ind_t decoded = {0};
bool decode_ret = decode_ng_paging(&decoded, dec_pdu);
AssertFatal(decode_ret, "decode_ng_paging(): could not decode message\n");
ngap_msg_free(dec_pdu);
LOG_A(NGAP, "decoded: m_tmsi=0x%x, n_tai=%d\n", decoded.ue_paging_identity.s_tmsi.m_tmsi, decoded.n_tai);
// Verify decoded values using equality function
bool eq_ret = eq_ng_paging(&orig, &decoded);
AssertFatal(eq_ret, "eq_ng_paging(): decoded message doesn't match original\n");
LOG_A(NGAP, "eq_ng_paging(): decoded message matches original\n");
free_ng_paging(&decoded);
free_ng_paging(&orig);
}
// Stub for uniqCfg required by logging/config system
configmodule_interface_t *uniqCfg = NULL;
int main(void)
{
// Initialize logging system
logInit();
set_log(NGAP, OAILOG_INFO);
test_ngap_paging_encode_decode();
LOG_A(NGAP, "All NGAP paging tests passed!\n");
return 0;
}