Paging (RRC): refactor NGAP Paging handler in CUCP

RRC now builds F1AP paging messages directly by calling F1AP/direct
callbacks and aligning the CU/DU intergace with the F1AP design in the CU.
Messages are forwarded based on TAI-based cell matching across DUs.

- Remove F1AP_PAGING_IND message def and f1ap_paging_ind_t.
  RRC no longer sends ITTI F1AP_PAGING_IND; it builds f1ap_paging_t
  and passes it to the DU path.

- rrc_gNB_process_PAGING_IND: new signature (rrc, instance, msg)
  with const where appropriate; call in rrc_gnb_task() updated.

- NGAP handler logic: For each TAI, match (PLMN + TAC) to
  rrc->configuration; collect matching TAIs. Build one f1ap_paging_t
  (5G-S-TMSI via nr_construct_5g_s_tmsi(), identity type
  F1AP_PAGING_IDENTITY_CN_UE, optional drx/priority/origin copied
  from ngap_paging_ind_t), call rrc_send_paging_to_dus(), then
  free_f1ap_paging(). Remove the previous AssertFatal stub, CC loop,
  NODE_IS_CU branch, and UE lookup logic: it is now handled by
  F1AP/direct CU callbacks.

- rrc_gNB_du: Add rrc_send_paging_to_dus(rrc, tais, n_tai, msg).
  Iterate DUs with RB_FOREACH; for each DU, fill msg->cells from
  du->cells whose (PLMN + TAC) matches any requested TAI, set
  n_cells, then call rrc->mac_rrc.paging_transfer(du->assoc_id,
  msg). Skip DUs with no matching cells. Prevents duplicate paging
  when multiple TAIs map to the same PLMN.
This commit is contained in:
Guido Casati
2026-02-17 12:25:58 +01:00
parent 6022ac8bf1
commit 8209dcb5d9
7 changed files with 135 additions and 62 deletions
-1
View File
@@ -65,4 +65,3 @@ MESSAGE_DEF(F1AP_UE_CONTEXT_MODIFICATION_REFUSE, MESSAGE_PRIORITY_MED, f1ap_ue_c
/* CU -> DU*/ /* CU -> DU*/
MESSAGE_DEF(F1AP_PAGING, MESSAGE_PRIORITY_MED, f1ap_paging_t, f1ap_paging) MESSAGE_DEF(F1AP_PAGING, MESSAGE_PRIORITY_MED, f1ap_paging_t, f1ap_paging)
MESSAGE_DEF(F1AP_PAGING_IND, MESSAGE_PRIORITY_MED, f1ap_paging_ind_t, f1ap_paging_ind)
-10
View File
@@ -68,7 +68,6 @@
#define F1AP_UE_CONTEXT_RELEASE_CMD(mSGpTR) (mSGpTR)->ittiMsg.f1ap_ue_context_release_cmd #define F1AP_UE_CONTEXT_RELEASE_CMD(mSGpTR) (mSGpTR)->ittiMsg.f1ap_ue_context_release_cmd
#define F1AP_UE_CONTEXT_RELEASE_COMPLETE(mSGpTR) (mSGpTR)->ittiMsg.f1ap_ue_context_release_complete #define F1AP_UE_CONTEXT_RELEASE_COMPLETE(mSGpTR) (mSGpTR)->ittiMsg.f1ap_ue_context_release_complete
#define F1AP_PAGING_IND(mSGpTR) (mSGpTR)->ittiMsg.f1ap_paging_ind
#define F1AP_PAGING(mSGpTR) (mSGpTR)->ittiMsg.f1ap_paging #define F1AP_PAGING(mSGpTR) (mSGpTR)->ittiMsg.f1ap_paging
/* Length of the transport layer address string /* Length of the transport layer address string
@@ -633,15 +632,6 @@ typedef struct f1ap_ue_context_rel_cplt_t {
uint32_t gNB_DU_ue_id; uint32_t gNB_DU_ue_id;
} f1ap_ue_context_rel_cplt_t; } f1ap_ue_context_rel_cplt_t;
typedef struct f1ap_paging_ind_s {
uint16_t ueidentityindexvalue;
uint64_t fiveg_s_tmsi;
uint8_t fiveg_s_tmsi_length;
plmn_id_t plmn;
uint64_t nr_cellid;
uint8_t paging_drx;
} f1ap_paging_ind_t;
typedef struct f1ap_paging_cell_item_s { typedef struct f1ap_paging_cell_item_s {
plmn_id_t plmn; plmn_id_t plmn;
uint64_t nr_cellid; uint64_t nr_cellid;
+1 -1
View File
@@ -3523,7 +3523,7 @@ void *rrc_gnb_task(void *args_p) {
break; break;
case NGAP_PAGING_IND: case NGAP_PAGING_IND:
rrc_gNB_process_PAGING_IND(msg_p, instance); rrc_gNB_process_PAGING_IND(RC.nrrrc[instance], instance, &NGAP_PAGING_IND(msg_p));
break; break;
case NGAP_HANDOVER_REQUEST: case NGAP_HANDOVER_REQUEST:
+60 -49
View File
@@ -84,6 +84,7 @@
#include "nr_pdcp/nr_pdcp_oai_api.h" #include "nr_pdcp/nr_pdcp_oai_api.h"
#include "oai_asn1.h" #include "oai_asn1.h"
#include "openair2/F1AP/f1ap_ids.h" #include "openair2/F1AP/f1ap_ids.h"
#include "openair2/F1AP/lib/f1ap_paging.h"
#include "openair3/SECU/key_nas_deriver.h" #include "openair3/SECU/key_nas_deriver.h"
#include "rrc_messages_types.h" #include "rrc_messages_types.h"
#include "s1ap_messages_types.h" #include "s1ap_messages_types.h"
@@ -1629,57 +1630,67 @@ int rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND(ngap_pdusession_release_comm
return 0; return 0;
} }
int rrc_gNB_process_PAGING_IND(MessageDef *msg_p, instance_t instance) /** @brief Handle NGAP Paging Indication from the AMF.
* For each TAI in the message, matches (PLMN + TAC) against the gNB configuration; on match,
* builds an F1AP Paging message and distributes it to all DUs that serve cells in that PLMN. */
int rrc_gNB_process_PAGING_IND(gNB_RRC_INST *rrc, const instance_t instance, const ngap_paging_ind_t *msg)
{ {
ngap_paging_ind_t *msg = &NGAP_PAGING_IND(msg_p); DevAssert(rrc);
for (uint16_t tai_idx = 0; tai_idx < msg->n_tai; tai_idx++) { DevAssert(msg);
nr_tai_t *tai = &msg->tai_list[tai_idx];
plmn_id_t *p = &tai->plmn; // Construct 5G-S-TMSI from paging identity components
LOG_I(NR_RRC, const fiveg_s_tmsi_t *s_tmsi = &msg->ue_paging_identity.s_tmsi;
"[gNB %ld] TAI List for Paging: MCC=%03d, MNC=%0*d, TAC=%d\n", const uint64_t fiveg_s_tmsi = nr_construct_5g_s_tmsi(s_tmsi->amf_set_id, s_tmsi->amf_pointer, s_tmsi->m_tmsi);
const nr_rrc_config_t *req = &rrc->configuration;
DevAssert(req->num_plmn > 0);
LOG_D(NR_RRC, "[gNB %ld] Processing NGAP Paging Indication for %d TAIs\n", instance, msg->n_tai);
// Paging Attempt Information (TS 38.413 §9.3.1.72, TS 38.300 §9.2.5)
// AMF provides attempt count and intended attempts for paging optimization (area expansion, retransmission logic).
if (msg->paging_attempt_info != NULL) {
LOG_D(NR_RRC,
"[gNB %ld] Paging Attempt Count: %u, Intended Number of Attempts: %u\n",
instance, instance,
p->mcc, msg->paging_attempt_info->paging_attempt_count,
p->mnc_digit_length, msg->paging_attempt_info->intended_paging_attempts);
p->mnc, }
tai->tac);
nr_rrc_config_t *req = &RC.nrrrc[instance]->configuration; if (msg->n_tai > NGAP_MAX_NO_TAI_PAGING) {
for (uint8_t j = 0; j < req->num_plmn; j++) { LOG_E(NR_RRC,
plmn_id_t *req_plmn = &req->plmn[j]; "[gNB %ld] Paging message error: n_tai (%d) exceeds NGAP_MAX_NO_TAI_PAGING (%d)\n",
if (req_plmn->mcc == p->mcc && req_plmn->mnc == p->mnc && req->tac == tai->tac) { instance,
for (uint8_t CC_id = 0; CC_id < MAX_NUM_CCs; CC_id++) { msg->n_tai,
AssertFatal(false, "to be implemented properly\n"); NGAP_MAX_NO_TAI_PAGING);
if (NODE_IS_CU(RC.nrrrc[instance]->node_type)) { return -1;
MessageDef *m = itti_alloc_new_message(TASK_RRC_GNB, 0, F1AP_PAGING_IND); }
F1AP_PAGING_IND(m).plmn = *req_plmn;
// Construct 5G-S-TMSI-Part1 from paging identity to find UE // Build F1AP paging message once for all TAIs
const fiveg_s_tmsi_t *s_tmsi = &msg->ue_paging_identity.s_tmsi; f1ap_paging_t f1ap_msg = {0};
uint64_t s_tmsi_part1 = nr_construct_5g_s_tmsi_part1(s_tmsi->amf_set_id, s_tmsi->amf_pointer, s_tmsi->m_tmsi); f1ap_msg.ue_identity_index_value = s_tmsi->m_tmsi % 1024;
// Try to find UE by 5G-S-TMSI-Part1 f1ap_msg.identity_type = F1AP_PAGING_IDENTITY_CN_UE;
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_ue_context_5g_s_tmsi_exist(RC.nrrrc[instance], s_tmsi_part1); f1ap_msg.identity.cn_ue_paging_identity = fiveg_s_tmsi;
if (ue_context_p == NULL) { if (msg->paging_drx != NULL) {
LOG_W(NR_RRC, "UE not found by paging identity\n"); /* Mapping 1:1 between NGAP and F1AP Paging DRX */
return -1; f1ap_msg.drx = malloc_or_fail(sizeof(*f1ap_msg.drx));
} *f1ap_msg.drx = (f1ap_paging_drx_t)*msg->paging_drx;
// UE found: use its associated cell }
gNB_RRC_UE_t *UE = &ue_context_p->ue_context; if (msg->paging_priority != NULL) {
nr_rrc_cell_container_t *cell = rrc_get_pcell_for_ue(RC.nrrrc[instance], UE); /* Mapping 1:1 between NGAP and F1AP Paging Priority */
if (cell == NULL) { f1ap_msg.priority = malloc_or_fail(sizeof(*f1ap_msg.priority));
LOG_W(NR_RRC, "UE %d (rnti %04x) found by paging identity but has no valid cell\n", UE->rrc_ue_id, UE->rnti); *f1ap_msg.priority = (f1ap_paging_priority_t)*msg->paging_priority;
return -1; }
} if (msg->origin != NULL) {
F1AP_PAGING_IND(m).nr_cellid = cell->info.cell_id; /* Mapping 1:1 between NGAP and F1AP Paging Origin */
F1AP_PAGING_IND(m).ueidentityindexvalue = (uint16_t)(msg->ue_paging_identity.s_tmsi.m_tmsi % 1024); f1ap_msg.origin = malloc_or_fail(sizeof(*f1ap_msg.origin));
F1AP_PAGING_IND(m).fiveg_s_tmsi = msg->ue_paging_identity.s_tmsi.m_tmsi; *f1ap_msg.origin = (f1ap_paging_origin_t)*msg->origin;
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); // For each DU, collect cells matching any AMF TAI (PLMN+TAC) and send
} else { // one Paging with that cell list.
//rrc_gNB_generate_pcch_msg(NGAP_PAGING_IND(msg_p).ue_paging_identity.s_tmsi.m_tmsi,(uint8_t)NGAP_PAGING_IND(msg_p).paging_drx, instance, CC_id); rrc_send_paging_to_dus(rrc, msg->tai_list, msg->n_tai, &f1ap_msg);
} // end of nodetype check
} // end of cc loop free_f1ap_paging(&f1ap_msg);
} // end of mcc mnc check
} // end of num_plmn
} // end of tai size
return 0; return 0;
} }
+1 -1
View File
@@ -84,7 +84,7 @@ void rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE(gNB_RRC_INST *rrc, gNB_RRC_UE
void nr_rrc_pdcp_config_security(gNB_RRC_UE_t *UE, bool enable_ciphering); void nr_rrc_pdcp_config_security(gNB_RRC_UE_t *UE, bool enable_ciphering);
int rrc_gNB_process_PAGING_IND(MessageDef *msg_p, instance_t instance); int rrc_gNB_process_PAGING_IND(gNB_RRC_INST *rrc, const instance_t instance, const ngap_paging_ind_t *msg);
void rrc_gNB_send_NGAP_HANDOVER_REQUIRED(gNB_RRC_INST *rrc, void rrc_gNB_send_NGAP_HANDOVER_REQUIRED(gNB_RRC_INST *rrc,
gNB_RRC_UE_t *UE, gNB_RRC_UE_t *UE,
+68
View File
@@ -820,3 +820,71 @@ void trigger_f1_reset(gNB_RRC_INST *rrc, sctp_assoc_t du_assoc_id)
}; };
rrc->mac_rrc.f1_reset(du_assoc_id, &reset); rrc->mac_rrc.f1_reset(du_assoc_id, &reset);
} }
/** @brief Distribute paging to DUs serving cells matching any of the requested TAIs.
* Loops DUs once, collects matching cells per DU, sends one message per DU.
* Each cell is added at most once per DU (cells are unique by cell_id within a DU).
* Per 3GPP TS 23.003, TAI is uniquely identified by (PLMN, TAC) combination.
* @param rrc RRC instance
* @param tais Array of TAIs (PLMN + TAC) to match against
* @param n_tai Number of TAIs in the array (must be > 0)
* @param msg Pre-filled F1AP paging message */
void rrc_send_paging_to_dus(gNB_RRC_INST *rrc, const nr_tai_t tais[], uint8_t n_tai, f1ap_paging_t *msg)
{
DevAssert(msg && !msg->cells);
DevAssert(n_tai > 0);
nr_rrc_du_container_t *du = NULL;
// Loop over DUs once, for each DU, collects matching cells
RB_FOREACH (du, rrc_du_tree, &rrc->dus) {
RETURN_IF_INVALID_ASSOC_ID(du->assoc_id);
// Stack-allocated cell list for this DU iteration
f1ap_paging_cell_item_t cells[F1AP_MAX_NB_CELLS] = {0};
msg->cells = cells;
msg->n_cells = 0;
// For each cell in this DU, check if it matches any of the matching TAIs
FOR_EACH_SEQ_ARR (nr_rrc_cell_container_t **, cell, &du->cells) {
if (msg->n_cells >= F1AP_MAX_NB_CELLS) {
LOG_W(F1AP,
"[gNB] Paging: DU assoc_id %d has more than %d matching cells, skipping next cells\n",
du->assoc_id,
F1AP_MAX_NB_CELLS);
break;
}
const nr_rrc_cell_info_t *info = &(*cell)->info;
const plmn_id_t *cell_plmn = &info->plmn;
const uint16_t cell_tac = info->tac;
// Check if this cell's TAI (PLMN + TAC) matches any of the matching TAIs
for (uint8_t j = 0; j < n_tai; j++) {
const nr_tai_t *req_tai = &tais[j];
const plmn_id_t *req_plmn = &req_tai->plmn;
const uint16_t req_tac = req_tai->tac;
if (cell_plmn->mcc == req_plmn->mcc && cell_plmn->mnc == req_plmn->mnc
&& cell_plmn->mnc_digit_length == req_plmn->mnc_digit_length && cell_tac == req_tac) {
f1ap_paging_cell_item_t *paging_cell = &msg->cells[msg->n_cells++];
paging_cell->plmn = *cell_plmn;
paging_cell->nr_cellid = info->cell_id;
break; // Found match, no need to check other TAIs
}
}
}
// Skip DUs with no matching cells
if (msg->n_cells == 0)
continue;
DevAssert(msg->n_cells <= F1AP_MAX_NB_CELLS);
LOG_I(F1AP,
"[gNB] Paging transfer to DU assoc_id %d: ue_identity_index_value=%u, paging_identity=%lu, n_cells=%u\n",
du->assoc_id,
msg->ue_identity_index_value,
msg->identity.cn_ue_paging_identity,
msg->n_cells);
// Send one message to the DU with all matching cells
rrc->mac_rrc.paging_transfer(du->assoc_id, msg);
}
msg->cells = NULL; // Stack allocation, no need to free
}
+5
View File
@@ -34,6 +34,9 @@ struct nr_rrc_cell_container_t;
struct f1ap_gnb_du_configuration_update_s; struct f1ap_gnb_du_configuration_update_s;
struct f1ap_served_cell_info_t; struct f1ap_served_cell_info_t;
#include "f1ap_messages_types.h"
#include "ngap_messages_types.h"
void rrc_gNB_process_f1_setup_req(struct f1ap_setup_req_s *req, sctp_assoc_t assoc_id); void rrc_gNB_process_f1_setup_req(struct f1ap_setup_req_s *req, sctp_assoc_t assoc_id);
void rrc_CU_process_f1_lost_connection(struct gNB_RRC_INST_s *rrc, struct f1ap_lost_connection_t *lc, sctp_assoc_t assoc_id); void rrc_CU_process_f1_lost_connection(struct gNB_RRC_INST_s *rrc, struct f1ap_lost_connection_t *lc, sctp_assoc_t assoc_id);
void rrc_gNB_process_f1_du_configuration_update(struct f1ap_gnb_du_configuration_update_s *conf_up, sctp_assoc_t assoc_id); void rrc_gNB_process_f1_du_configuration_update(struct f1ap_gnb_du_configuration_update_s *conf_up, sctp_assoc_t assoc_id);
@@ -58,4 +61,6 @@ struct nr_rrc_du_container_t *find_target_du(struct gNB_RRC_INST_s *rrc, sctp_as
void trigger_f1_reset(struct gNB_RRC_INST_s *rrc, sctp_assoc_t du_assoc_id); void trigger_f1_reset(struct gNB_RRC_INST_s *rrc, sctp_assoc_t du_assoc_id);
void rrc_send_paging_to_dus(struct gNB_RRC_INST_s *rrc, const nr_tai_t tais[], uint8_t n_tai, f1ap_paging_t *f1ap_msg);
#endif /* RRC_GNB_DU_H_ */ #endif /* RRC_GNB_DU_H_ */