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
2025-10-23 12:32:26 +02:00
parent 6022ac8bf1
commit 8209dcb5d9
7 changed files with 135 additions and 62 deletions

View File

@@ -65,4 +65,3 @@ MESSAGE_DEF(F1AP_UE_CONTEXT_MODIFICATION_REFUSE, MESSAGE_PRIORITY_MED, f1ap_ue_c
/* CU -> DU*/
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)

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_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
/* 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;
} 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 {
plmn_id_t plmn;
uint64_t nr_cellid;

View File

@@ -3523,7 +3523,7 @@ void *rrc_gnb_task(void *args_p) {
break;
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;
case NGAP_HANDOVER_REQUEST:

View File

@@ -84,6 +84,7 @@
#include "nr_pdcp/nr_pdcp_oai_api.h"
#include "oai_asn1.h"
#include "openair2/F1AP/f1ap_ids.h"
#include "openair2/F1AP/lib/f1ap_paging.h"
#include "openair3/SECU/key_nas_deriver.h"
#include "rrc_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;
}
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);
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",
DevAssert(rrc);
DevAssert(msg);
// Construct 5G-S-TMSI from paging identity components
const fiveg_s_tmsi_t *s_tmsi = &msg->ue_paging_identity.s_tmsi;
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,
p->mcc,
p->mnc_digit_length,
p->mnc,
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 == 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)) {
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
const fiveg_s_tmsi_t *s_tmsi = &msg->ue_paging_identity.s_tmsi;
uint64_t s_tmsi_part1 = nr_construct_5g_s_tmsi_part1(s_tmsi->amf_set_id, s_tmsi->amf_pointer, s_tmsi->m_tmsi);
// Try to find UE by 5G-S-TMSI-Part1
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_ue_context_5g_s_tmsi_exist(RC.nrrrc[instance], s_tmsi_part1);
if (ue_context_p == NULL) {
LOG_W(NR_RRC, "UE not found by paging identity\n");
return -1;
}
// UE found: use its associated cell
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
nr_rrc_cell_container_t *cell = rrc_get_pcell_for_ue(RC.nrrrc[instance], UE);
if (cell == NULL) {
LOG_W(NR_RRC, "UE %d (rnti %04x) found by paging identity but has no valid cell\n", UE->rrc_ue_id, UE->rnti);
return -1;
}
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 ? (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 {
//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);
} // end of nodetype check
} // end of cc loop
} // end of mcc mnc check
} // end of num_plmn
} // end of tai size
msg->paging_attempt_info->paging_attempt_count,
msg->paging_attempt_info->intended_paging_attempts);
}
if (msg->n_tai > NGAP_MAX_NO_TAI_PAGING) {
LOG_E(NR_RRC,
"[gNB %ld] Paging message error: n_tai (%d) exceeds NGAP_MAX_NO_TAI_PAGING (%d)\n",
instance,
msg->n_tai,
NGAP_MAX_NO_TAI_PAGING);
return -1;
}
// Build F1AP paging message once for all TAIs
f1ap_paging_t f1ap_msg = {0};
f1ap_msg.ue_identity_index_value = s_tmsi->m_tmsi % 1024;
f1ap_msg.identity_type = F1AP_PAGING_IDENTITY_CN_UE;
f1ap_msg.identity.cn_ue_paging_identity = fiveg_s_tmsi;
if (msg->paging_drx != NULL) {
/* Mapping 1:1 between NGAP and F1AP Paging DRX */
f1ap_msg.drx = malloc_or_fail(sizeof(*f1ap_msg.drx));
*f1ap_msg.drx = (f1ap_paging_drx_t)*msg->paging_drx;
}
if (msg->paging_priority != NULL) {
/* Mapping 1:1 between NGAP and F1AP Paging Priority */
f1ap_msg.priority = malloc_or_fail(sizeof(*f1ap_msg.priority));
*f1ap_msg.priority = (f1ap_paging_priority_t)*msg->paging_priority;
}
if (msg->origin != NULL) {
/* Mapping 1:1 between NGAP and F1AP Paging Origin */
f1ap_msg.origin = malloc_or_fail(sizeof(*f1ap_msg.origin));
*f1ap_msg.origin = (f1ap_paging_origin_t)*msg->origin;
}
// For each DU, collect cells matching any AMF TAI (PLMN+TAC) and send
// one Paging with that cell list.
rrc_send_paging_to_dus(rrc, msg->tai_list, msg->n_tai, &f1ap_msg);
free_f1ap_paging(&f1ap_msg);
return 0;
}

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);
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,
gNB_RRC_UE_t *UE,

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);
}
/** @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
}

View File

@@ -34,6 +34,9 @@ struct nr_rrc_cell_container_t;
struct f1ap_gnb_du_configuration_update_s;
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_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);
@@ -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 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_ */