Files
openairinterface5g/openair3/NGAP/ngap_gNB_paging.c
Robert Schmidt 8107939f08 Change OAI license to CSSL v1.0 (and others)
- all RAN code, CI code, configuration files, dockerfiles, in CSSL v1.0
- all deployment code (openshift, charts, ancillary files like shell
  scripts), in MIT
- documentation in CC-BY-4.0
- exceptions might apply and are listed in NOTICE
- there is a new LICENSES folder with all licenses
- CONTRIBUTIONS.md has been updated accordingly

For automated changes based on OAI PL v1.1:

    perl -i~ -0pe 's/\/\*.*Licensed to the OpenAirInterface.*openairinterface.org\n#?/\/*\n * SPDX-License-Identifier: LicenseRef-CSSL-1.0\n/s' **/*.{c,h,cpp}
    perl -i~ -0pe 's/\/\*.*Licensed to the OpenAirInterface.*openairinterface.org\n#?/\/*\n * SPDX-License-Identifier: LicenseRef-CSSL-1.0\n/s' **/*.ts
    perl -i~ -0pe 's/<!--.*Licensed to the OpenAirInterface.*openairinterface.org\n.*-->/<!-- SPDX-License-Identifier: LicenseRef-CSSL-1.0 -->/s' **/*.xml

The rest (cmake, files with missing license, cmake) manually.
2026-03-27 16:36:37 +01:00

314 lines
13 KiB
C

/*
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
*/
#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;
}