From 539d17203f947c1a6bf0246bd0e58ebd97661934 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Thu, 8 May 2025 17:10:50 +0200 Subject: [PATCH] Fix handling of selected PLMN in NGAP selectedPLMN-Identity IE in RRCSetupComplete is an Index (long) of the PLMN selected by the UE from the plmn-IdentityInfoList (SIB1) (see 3GPP TS 38.331) while Selected PLMN Identity in NGAP INITIAL UE MESSAGE Indicates the selected PLMN id for the non-3GPP access (PLMN Identity ID carrying MNC, MCC and MNC size, 9.2.5.1 3GPP TS 38.413) The legacy code was setting Selected PLMN Identity in the latter to an ID value as the former, however in NGAP the IE to be encoded is PLMN Identity 9.3.3.5 therefore: * set plmn type plmn_id_t in ngap_nas_first_req_t * check whether received selectedPLMN-Identity is within bounds * select PLMN from gNB_RrcConfigurationReq by selectedPLMN-Identity ID note: this commit is assuming that the plmn-IdentityInfoList in SIB1 is matching the PLMN list in gNB_RrcConfigurationReq * in NGAP: store the PLMN in the NGAP UE context, no need to fetch the PLMN in the NGAP instance since the UE context already tells what PLMN was selected Closes #801 --- openair2/COMMON/ngap_messages_types.h | 20 ++++++--------- openair2/RRC/NR/rrc_gNB_NGAP.c | 27 +++++++++++++------- openair3/NGAP/ngap_gNB_nas_procedures.c | 33 ++++++++++--------------- openair3/NGAP/ngap_gNB_nnsf.c | 10 +++----- openair3/NGAP/ngap_gNB_nnsf.h | 4 +-- openair3/NGAP/ngap_gNB_ue_context.h | 2 +- 6 files changed, 45 insertions(+), 51 deletions(-) diff --git a/openair2/COMMON/ngap_messages_types.h b/openair2/COMMON/ngap_messages_types.h index e54bc46d09..52cd919338 100644 --- a/openair2/COMMON/ngap_messages_types.h +++ b/openair2/COMMON/ngap_messages_types.h @@ -490,22 +490,16 @@ typedef struct ngap_deregistered_gnb_ind_s { * will be the unique identifier used between RRC and NGAP. */ typedef struct ngap_nas_first_req_s { - /* UE id for initial connection to NGAP */ + // RAN UE NGAP ID (mandatory) uint32_t gNB_ue_ngap_id; - - /* the chosen PLMN identity as index, see TS 36.331 6.2.2 RRC Connection - * Setup Complete. This index here is zero-based, unlike the standard! */ - int selected_plmn_identity; - - /* Establishment cause as sent by UE */ + /* PLMN: Selected PLMN Identity (optional) + * User Location Information (mandatory) */ + plmn_id_t plmn; + // RRC Establishment Cause (mandatory) ngap_rrc_establishment_cause_t establishment_cause; - - /* NAS PDU */ + // NAS-PDU (mandatory) byte_array_t nas_pdu; - - /* If this flag is set NGAP layer is expecting the GUAMI. If = 0, - * the temporary s-tmsi is used. - */ + // UE identity: 5G-S-TMSI, GUAMI ngap_ue_identity_t ue_identity; } ngap_nas_first_req_t; diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index 879141144e..5dc101411c 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -149,12 +149,15 @@ void nr_rrc_pdcp_config_security(gNB_RRC_UE_t *UE, bool enable_ciphering) } /** @brief Process AMF Identifier and fill GUAMI struct members */ -static nr_guami_t get_guami(const uint32_t amf_Id) +static nr_guami_t get_guami(const uint32_t amf_Id, const plmn_id_t plmn) { nr_guami_t guami = {0}; guami.amf_region_id = (amf_Id >> 16) & 0xff; guami.amf_set_id = (amf_Id >> 6) & 0x3ff; guami.amf_pointer = amf_Id & 0x3f; + guami.mcc = plmn.mcc; + guami.mnc = plmn.mnc; + guami.mnc_len = plmn.mnc_digit_length; return guami; } @@ -179,13 +182,19 @@ void rrc_gNB_send_NGAP_NAS_FIRST_REQ(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, NR_RRC // NAS-PDU req->nas_pdu = create_byte_array(rrcSetupComplete->dedicatedNAS_Message.size, rrcSetupComplete->dedicatedNAS_Message.buf); - // Selected PLMN Identity: indicates the selected PLMN id for the non-3GPP access - /* selected_plmn_identity: IE is 1-based, convert to 0-based (C array) */ - int selected_plmn_identity = rrcSetupComplete->selectedPLMN_Identity - 1; - if (selected_plmn_identity != 0) - LOG_E(NGAP, "UE %u: sent selected PLMN identity %ld, but only one PLMN supported\n", req->gNB_ue_ngap_id, rrcSetupComplete->selectedPLMN_Identity); - - req->selected_plmn_identity = 0; /* always zero because we only support one */ + /* Selected PLMN Identity (Optional) + * selectedPLMN-Identity in RRCSetupComplete: Index of the PLMN selected by the UE from the plmn-IdentityInfoList (SIB1) + * Selected PLMN Identity in INITIAL UE MESSAGE: Indicates the selected PLMN id for the non-3GPP access.*/ + if (rrcSetupComplete->selectedPLMN_Identity > rrc->configuration.num_plmn) { + LOG_E(NGAP, + "Failed to send Initial UE Message: selected PLMN (%ld) identity is out of bounds (%d)\n", + rrcSetupComplete->selectedPLMN_Identity, + rrc->configuration.num_plmn); + return; + } + int selected_plmn_identity = rrcSetupComplete->selectedPLMN_Identity - 1; // Convert 1-based PLMN Identity IE to 0-based index + req->plmn = rrc->configuration.plmn[selected_plmn_identity]; // Select from the stored list + LOG_I(NGAP, "Selected PLMN in the NG Initial UE Message: MCC %u, MNC %u\n", req->plmn.mcc, req->plmn.mnc); /* 5G-S-TMSI */ if (UE->Initialue_identity_5g_s_TMSI.presence) { @@ -204,7 +213,7 @@ void rrc_gNB_send_NGAP_NAS_FIRST_REQ(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, NR_RRC NR_RegisteredAMF_t *r_amf = rrcSetupComplete->registeredAMF; req->ue_identity.presenceMask |= NGAP_UE_IDENTITIES_guami; uint32_t amf_Id = BIT_STRING_to_uint32(&r_amf->amf_Identifier); - UE->ue_guami = req->ue_identity.guami = get_guami(amf_Id); + UE->ue_guami = req->ue_identity.guami = get_guami(amf_Id, req->plmn); LOG_I(NGAP, "GUAMI in NGAP_NAS_FIRST_REQ (UE %04x): AMF Set ID %u, Region ID %u, Pointer %u\n", UE->rnti, diff --git a/openair3/NGAP/ngap_gNB_nas_procedures.c b/openair3/NGAP/ngap_gNB_nas_procedures.c index 36e9934384..7729ffa4f2 100644 --- a/openair3/NGAP/ngap_gNB_nas_procedures.c +++ b/openair3/NGAP/ngap_gNB_nas_procedures.c @@ -107,16 +107,15 @@ static ngap_gNB_amf_data_t *select_amf(ngap_gNB_instance_t *instance_p, const ng if (amf == NULL) { if (msg->ue_identity.presenceMask & NGAP_UE_IDENTITIES_FiveG_s_tmsi) { const fiveg_s_tmsi_t *fgs_tmsi = &msg->ue_identity.s_tmsi; - amf = ngap_gNB_nnsf_select_amf_by_amf_setid(instance_p, msg->establishment_cause, msg->selected_plmn_identity, fgs_tmsi->amf_set_id); + amf = ngap_gNB_nnsf_select_amf_by_amf_setid(instance_p, msg->establishment_cause, msg->plmn, fgs_tmsi->amf_set_id); if (amf) { - NGAP_INFO("UE %d: Chose AMF '%s' (assoc_id %d) through S-TMSI AMFSI %d and selected PLMN Identity index %d MCC %d MNC %d\n", + NGAP_INFO("UE %d: Chose AMF '%s' (assoc_id %d) through S-TMSI AMFSI %d and selected PLMN MCC %d MNC %d\n", msg->gNB_ue_ngap_id, amf->amf_name, amf->assoc_id, fgs_tmsi->amf_set_id, - msg->selected_plmn_identity, - instance_p->plmn[msg->selected_plmn_identity].plmn.mcc, - instance_p->plmn[msg->selected_plmn_identity].plmn.mnc); + msg->plmn.mcc, + msg->plmn.mnc); return amf; } } @@ -125,15 +124,14 @@ static ngap_gNB_amf_data_t *select_amf(ngap_gNB_instance_t *instance_p, const ng // No UE identity (5G-S-TMSI or GUAMI) is present if (msg->ue_identity.presenceMask == 0) { // Select the AMF based on the selected PLMN identity received through RRCSetupComplete - amf = ngap_gNB_nnsf_select_amf_by_plmn_id(instance_p, msg->establishment_cause, msg->selected_plmn_identity); + amf = ngap_gNB_nnsf_select_amf_by_plmn_id(instance_p, msg->establishment_cause, msg->plmn); if (amf) { - NGAP_INFO("UE %d: Chose AMF '%s' (assoc_id %d) through selected PLMN Identity index %d MCC %d MNC %d\n", + NGAP_INFO("UE %d: Chose AMF '%s' (assoc_id %d) through selected PLMN MCC %d MNC %d\n", msg->gNB_ue_ngap_id, amf->amf_name, amf->assoc_id, - msg->selected_plmn_identity, - instance_p->plmn[msg->selected_plmn_identity].plmn.mcc, - instance_p->plmn[msg->selected_plmn_identity].plmn.mnc); + msg->plmn.mcc, + msg->plmn.mnc); return amf; } else { // Select the AMF with the highest capacity @@ -185,7 +183,7 @@ int ngap_gNB_handle_nas_first_req(instance_t instance, ngap_nas_first_req_t *UEf ue_desc_p->amf_ref = amf; ue_desc_p->gNB_ue_ngap_id = UEfirstReq->gNB_ue_ngap_id; ue_desc_p->gNB_instance = instance_p; - ue_desc_p->selected_plmn_identity = UEfirstReq->selected_plmn_identity; + ue_desc_p->selected_plmn_identity = UEfirstReq->plmn; ngap_store_ue_context(ue_desc_p); // RAN UE NGAP ID (M) @@ -220,7 +218,7 @@ int ngap_gNB_handle_nas_first_req(instance_t instance, ngap_nas_first_req_t *UEf 0, // Cell ID &userinfo_nr_p->nR_CGI.nRCellIdentity); - plmn_id_t *plmn = &instance_p->plmn[ue_desc_p->selected_plmn_identity].plmn; + plmn_id_t *plmn = &ue_desc_p->selected_plmn_identity; MCC_MNC_TO_TBCD(plmn->mcc, plmn->mnc, plmn->mnc_digit_length, &userinfo_nr_p->nR_CGI.pLMNIdentity); /* In case of network sharing, @@ -444,17 +442,12 @@ int ngap_gNB_nas_uplink(instance_t instance, ngap_uplink_nas_t *ngap_uplink_nas_ MACRO_GNB_ID_TO_CELL_IDENTITY(ngap_gNB_instance_p->gNB_id, 0, // Cell ID &userinfo_nr_p->nR_CGI.nRCellIdentity); - MCC_MNC_TO_TBCD(ngap_gNB_instance_p->plmn[ue_context_p->selected_plmn_identity].plmn.mcc, - ngap_gNB_instance_p->plmn[ue_context_p->selected_plmn_identity].plmn.mnc, - ngap_gNB_instance_p->plmn[ue_context_p->selected_plmn_identity].plmn.mnc_digit_length, - &userinfo_nr_p->nR_CGI.pLMNIdentity); + plmn_id_t *plmn = &ue_context_p->selected_plmn_identity; + MCC_MNC_TO_TBCD(plmn->mcc, plmn->mnc, plmn->mnc_digit_length, &userinfo_nr_p->nR_CGI.pLMNIdentity); /* Set TAI */ INT24_TO_OCTET_STRING(ngap_gNB_instance_p->tac, &userinfo_nr_p->tAI.tAC); - MCC_MNC_TO_PLMNID(ngap_gNB_instance_p->plmn[ue_context_p->selected_plmn_identity].plmn.mcc, - ngap_gNB_instance_p->plmn[ue_context_p->selected_plmn_identity].plmn.mnc, - ngap_gNB_instance_p->plmn[ue_context_p->selected_plmn_identity].plmn.mnc_digit_length, - &userinfo_nr_p->tAI.pLMNIdentity); + MCC_MNC_TO_PLMNID(plmn->mcc, plmn->mnc, plmn->mnc_digit_length, &userinfo_nr_p->tAI.pLMNIdentity); } if (ngap_gNB_encode_pdu(&pdu, &buffer, &length) < 0) { NGAP_ERROR("Failed to encode uplink NAS transport\n"); diff --git a/openair3/NGAP/ngap_gNB_nnsf.c b/openair3/NGAP/ngap_gNB_nnsf.c index 648d455bea..77cc7866e1 100644 --- a/openair3/NGAP/ngap_gNB_nnsf.c +++ b/openair3/NGAP/ngap_gNB_nnsf.c @@ -86,7 +86,7 @@ ngap_gNB_amf_data_t *ngap_gNB_nnsf_select_amf(ngap_gNB_instance_t *instance_p, c ngap_gNB_amf_data_t *ngap_gNB_nnsf_select_amf_by_plmn_id(ngap_gNB_instance_t *instance_p, const ngap_rrc_establishment_cause_t cause, - const int selected_plmn_identity) + const plmn_id_t selected_plmn_identity) { struct ngap_gNB_amf_data_s *amf_data_p = NULL; struct ngap_gNB_amf_data_s *amf_highest_capacity_p = NULL; @@ -132,8 +132,7 @@ ngap_gNB_amf_data_t *ngap_gNB_nnsf_select_amf_by_plmn_id(ngap_gNB_instance_t *in /* Looking for served GUAMI PLMN Identity selected matching the one provided by the UE */ STAILQ_FOREACH(guami_p, &amf_data_p->served_guami, next) { STAILQ_FOREACH(served_plmn_p, &guami_p->served_plmns, next) { - if ((served_plmn_p->mcc == instance_p->plmn[selected_plmn_identity].plmn.mcc) - && (served_plmn_p->mnc == instance_p->plmn[selected_plmn_identity].plmn.mnc)) { + if ((served_plmn_p->mcc == selected_plmn_identity.mcc) && (served_plmn_p->mnc == selected_plmn_identity.mnc)) { break; } } @@ -155,7 +154,7 @@ ngap_gNB_amf_data_t *ngap_gNB_nnsf_select_amf_by_plmn_id(ngap_gNB_instance_t *in ngap_gNB_amf_data_t *ngap_gNB_nnsf_select_amf_by_amf_setid(ngap_gNB_instance_t *instance_p, const ngap_rrc_establishment_cause_t cause, - const int selected_plmn_identity, + const plmn_id_t selected_plmn_identity, uint8_t amf_setid) { struct ngap_gNB_amf_data_s *amf_data_p = NULL; @@ -203,8 +202,7 @@ ngap_gNB_amf_data_t *ngap_gNB_nnsf_select_amf_by_amf_setid(ngap_gNB_instance_t * struct plmn_identity_s *served_plmn_p = NULL; STAILQ_FOREACH(served_plmn_p, &guami_p->served_plmns, next) { - if ((served_plmn_p->mcc == instance_p->plmn[selected_plmn_identity].plmn.mcc) - && (served_plmn_p->mnc == instance_p->plmn[selected_plmn_identity].plmn.mnc)) { + if ((served_plmn_p->mcc == selected_plmn_identity.mcc) && (served_plmn_p->mnc == selected_plmn_identity.mnc)) { break; } } diff --git a/openair3/NGAP/ngap_gNB_nnsf.h b/openair3/NGAP/ngap_gNB_nnsf.h index 074b163f75..8a87ecfce2 100644 --- a/openair3/NGAP/ngap_gNB_nnsf.h +++ b/openair3/NGAP/ngap_gNB_nnsf.h @@ -38,11 +38,11 @@ ngap_gNB_amf_data_t *ngap_gNB_nnsf_select_amf(ngap_gNB_instance_t *instance_p, c ngap_gNB_amf_data_t *ngap_gNB_nnsf_select_amf_by_plmn_id(ngap_gNB_instance_t *instance_p, const ngap_rrc_establishment_cause_t cause, - const int selected_plmn_identity); + const plmn_id_t selected_plmn_identity); ngap_gNB_amf_data_t *ngap_gNB_nnsf_select_amf_by_amf_setid(ngap_gNB_instance_t *instance_p, const ngap_rrc_establishment_cause_t cause, - const int selected_plmn_identity, + const plmn_id_t selected_plmn_identity, uint8_t amf_setid); ngap_gNB_amf_data_t *ngap_gNB_nnsf_select_amf_by_guami(ngap_gNB_instance_t *instance_p, diff --git a/openair3/NGAP/ngap_gNB_ue_context.h b/openair3/NGAP/ngap_gNB_ue_context.h index 2ca75141ef..4c0c63239f 100644 --- a/openair3/NGAP/ngap_gNB_ue_context.h +++ b/openair3/NGAP/ngap_gNB_ue_context.h @@ -72,7 +72,7 @@ typedef struct ngap_gNB_ue_context_s { /* Signaled by the UE in RRC Connection Setup Complete and used in NAS Uplink * to route NAS messages correctly. 0-based, not 1-based as in TS 36.331 * 6.2.2 RRC Connection Setup Complete! */ - int selected_plmn_identity; + plmn_id_t selected_plmn_identity; /* Reference to gNB data this UE is attached to */ ngap_gNB_instance_t *gNB_instance;