mirror of
https://gitlab.eurecom.fr/oai/openairinterface5g.git
synced 2026-07-13 04:30:28 +00:00
- 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.
314 lines
13 KiB
C
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;
|
|
}
|