From c90d7f7f2623c2aa759cc98ba1761cbe1a772689 Mon Sep 17 00:00:00 2001 From: Aswanth KC Date: Mon, 2 Mar 2026 22:45:12 -0500 Subject: [PATCH] fix: issue 911 - OAI gNB hangs when AMF connection goes down - Extended fix to support multiple UEs instead of single UE - Actions now trigger upon AMF reconnection Per 3GPP TS 123 502 V16.7.0 (2021-01) Section 4.2.6 (AN Release): When NG-AP signalling connection is lost due to (R)AN or AMF failure, AN release is performed locally by AMF or (R)AN without relying on signalling between (R)AN and AMF. This deactivates all UP connections. --- openair2/COMMON/ngap_messages_def.h | 1 + openair3/NGAP/ngap_gNB.c | 41 +++++++++++++++++++++++++ openair3/NGAP/ngap_gNB_defs.h | 2 ++ openair3/NGAP/ngap_gNB_handlers.c | 2 ++ openair3/NGAP/ngap_gNB_nas_procedures.c | 2 +- openair3/NGAP/ngap_gNB_ue_context.c | 30 ++++++++++++++++++ openair3/NGAP/ngap_gNB_ue_context.h | 1 + 7 files changed, 78 insertions(+), 1 deletion(-) diff --git a/openair2/COMMON/ngap_messages_def.h b/openair2/COMMON/ngap_messages_def.h index 09e893f874..36528357f1 100644 --- a/openair2/COMMON/ngap_messages_def.h +++ b/openair2/COMMON/ngap_messages_def.h @@ -73,6 +73,7 @@ MESSAGE_DEF(NGAP_HANDOVER_NOTIFY, MESSAGE_PRIORITY_MED, ngap_handover_notify_t, MESSAGE_DEF(NGAP_HANDOVER_CANCEL, MESSAGE_PRIORITY_MED, ngap_handover_cancel_t, ngap_handover_cancel) MESSAGE_DEF(NGAP_UL_RAN_STATUS_TRANSFER, MESSAGE_PRIORITY_MED, ngap_ran_status_transfer_t, ngap_ul_ran_status_transfer) MESSAGE_DEF(NGAP_DL_RAN_STATUS_TRANSFER, MESSAGE_PRIORITY_MED, ngap_ran_status_transfer_t, ngap_dl_ran_status_transfer) +MESSAGE_DEF(NGAP_RECONNECT_TIMER, MESSAGE_PRIORITY_MED, IttiMsgText, ngap_gNB_amf_data) /* NGAP -> RRC messages */ MESSAGE_DEF(NGAP_DOWNLINK_NAS , MESSAGE_PRIORITY_MED, ngap_downlink_nas_t , ngap_downlink_nas ) diff --git a/openair3/NGAP/ngap_gNB.c b/openair3/NGAP/ngap_gNB.c index 257ca25f1a..e8fee7f34a 100644 --- a/openair3/NGAP/ngap_gNB.c +++ b/openair3/NGAP/ngap_gNB.c @@ -93,6 +93,38 @@ uint32_t ngap_generate_gNB_id(void) return gNB_id; } +static void ngap_gNB_redial_amf(ngap_gNB_amf_data_t *amf_desc_p) +{ + NGAP_INFO("AMF cnx_id = %u\n", amf_desc_p->cnx_id); + NGAP_INFO("AMF state = %d\n", amf_desc_p->state); + /*reconnect with the amf */ + MessageDef *message_p = itti_alloc_new_message(TASK_NGAP, 0, SCTP_NEW_ASSOCIATION_REQ); + sctp_new_association_req_t *req = &message_p->ittiMsg.sctp_new_association_req; + + /*amf descriptor */ + req->port = NGAP_PORT_NUMBER; + req->ppid = NGAP_SCTP_PPID; + req->in_streams = amf_desc_p->in_streams; + req->out_streams = amf_desc_p->out_streams; + req->ulp_cnx_id = amf_desc_p->cnx_id; + + ngap_gNB_instance_t *inst = amf_desc_p->ngap_gNB_instance; + + memcpy(&req->remote_address, &amf_desc_p->amf_s1_ip, sizeof(req->remote_address)); + memcpy(&req->local_address, &inst->gNB_ng_ip, sizeof(req->local_address)); + + NGAP_INFO("[gNB %ld] Re-dial AMF cnx_id=%u (streams in=%u out=%u)\n", + inst->instance, + amf_desc_p->cnx_id, + req->in_streams, + req->out_streams); + + inst->ngap_amf_nb++; + inst->ngap_amf_pending_nb++; + + itti_send_msg_to_task(TASK_SCTP, inst->instance, message_p); +} + static void ngap_gNB_register_amf(ngap_gNB_instance_t *instance_p, net_ip_address_t *amf_ip_address, net_ip_address_t *local_ip_addr, @@ -550,6 +582,15 @@ void *ngap_gNB_process_itti_msg(void *notUsed) { ngap_gNB_handle_sctp_association_resp(instance, &received_msg->ittiMsg.sctp_new_association_resp); break; + case TIMER_HAS_EXPIRED: { + timer_has_expired_t *th = &received_msg->ittiMsg.timer_has_expired; + ngap_gNB_amf_data_t *amf_desc_p = (ngap_gNB_amf_data_t *)th->arg; + if (amf_desc_p) { + ngap_gNB_redial_amf(amf_desc_p); + } + break; + } + case SCTP_DATA_IND: ngap_gNB_handle_sctp_data_ind(&received_msg->ittiMsg.sctp_data_ind); break; diff --git a/openair3/NGAP/ngap_gNB_defs.h b/openair3/NGAP/ngap_gNB_defs.h index 1a1e5768ff..b7c5dc3f64 100644 --- a/openair3/NGAP/ngap_gNB_defs.h +++ b/openair3/NGAP/ngap_gNB_defs.h @@ -161,6 +161,8 @@ struct ngap_gNB_instance_s; /* This structure describes association of a gNB to a AMF */ typedef struct ngap_gNB_amf_data_s { + long t_reconnect; // ITTI timer id (or equivalent) + /* AMF descriptors tree, ordered by sctp assoc id */ RB_ENTRY(ngap_gNB_amf_data_s) entry; diff --git a/openair3/NGAP/ngap_gNB_handlers.c b/openair3/NGAP/ngap_gNB_handlers.c index 9113f850ca..be0361afbd 100644 --- a/openair3/NGAP/ngap_gNB_handlers.c +++ b/openair3/NGAP/ngap_gNB_handlers.c @@ -77,6 +77,8 @@ void ngap_handle_ng_setup_message(ngap_gNB_amf_data_t *amf_desc_p, int sctp_shut /* Decrease associated AMF number */ amf_desc_p->ngap_gNB_instance->ngap_amf_associated_nb --; } + /* Release UE context and start reconnection process*/ + ngap_release_ues_for_amf(amf_desc_p); /* If there are no more associated AMF, inform gNB app */ if (amf_desc_p->ngap_gNB_instance->ngap_amf_associated_nb == 0) { diff --git a/openair3/NGAP/ngap_gNB_nas_procedures.c b/openair3/NGAP/ngap_gNB_nas_procedures.c index 2d4bd3eefa..9e341494c6 100644 --- a/openair3/NGAP/ngap_gNB_nas_procedures.c +++ b/openair3/NGAP/ngap_gNB_nas_procedures.c @@ -59,7 +59,7 @@ #include "s1ap_messages_types.h" #include "xer_encoder.h" #include "ds/byte_array.h" - +#include "intertask_interface.h" /** @brief Selects the AMF instance for a given UE based on identity information. * It attempts to select an AMF using the following prioritized criteria: * 1. GUAMI, if provided and valid (via Region ID, Set ID, Pointer). diff --git a/openair3/NGAP/ngap_gNB_ue_context.c b/openair3/NGAP/ngap_gNB_ue_context.c index 994271d822..e50726380f 100644 --- a/openair3/NGAP/ngap_gNB_ue_context.c +++ b/openair3/NGAP/ngap_gNB_ue_context.c @@ -34,6 +34,8 @@ #include "common/utils/T/T.h" #include "ngap_common.h" #include "tree.h" +#include "intertask_interface.h" +#define SCTP_LOOKUP_TIMER_TIMEOUT_SEC 30 /* Tree of UE ordered by gNB_ue_ngap_id's * NO INSTANCE, the 32 bits id is large enough to handle all UEs, regardless the cell, gNB, ... @@ -92,3 +94,31 @@ struct ngap_gNB_ue_context_s *ngap_detach_ue_context(uint32_t gNB_ue_ngap_id) RB_REMOVE(ngap_ue_map, &ngap_ue_head, tmp); return tmp; } + +void ngap_release_ues_for_amf(ngap_gNB_amf_data_t *amf_desc_p) +{ + /* Release all UE contexts for this AMF */ + ngap_gNB_ue_context_t *ue = NULL; + ngap_gNB_ue_context_t *next = NULL; + RB_FOREACH_SAFE(ue, ngap_ue_map, &ngap_ue_head, next) { + if (ue->amf_ref == amf_desc_p) { + LOG_I(NGAP, "Releasing UE context: gNB UE NGAP ID: %u | AMF UE NGAP ID: %lu\n", ue->gNB_ue_ngap_id, ue->amf_ue_ngap_id); + MessageDef *msg_p = itti_alloc_new_message(TASK_NGAP, 0, NGAP_UE_CONTEXT_RELEASE_COMMAND); + NGAP_UE_CONTEXT_RELEASE_COMMAND(msg_p).gNB_ue_ngap_id = ue->gNB_ue_ngap_id; + itti_send_msg_to_task(TASK_RRC_GNB, amf_desc_p->ngap_gNB_instance->instance, msg_p); + } + } + /* Reset AMF connection and set reconnect timer */ + /* SCTP lookup timer */ + long tid; + int rc = timer_setup(SCTP_LOOKUP_TIMER_TIMEOUT_SEC, + 0, + TASK_NGAP, + amf_desc_p->ngap_gNB_instance->instance, + TIMER_ONE_SHOT, + amf_desc_p, + &tid); + if (rc == 0) { + amf_desc_p->t_reconnect = tid; + } +} diff --git a/openair3/NGAP/ngap_gNB_ue_context.h b/openair3/NGAP/ngap_gNB_ue_context.h index 8053e8f8c6..1ca458968f 100644 --- a/openair3/NGAP/ngap_gNB_ue_context.h +++ b/openair3/NGAP/ngap_gNB_ue_context.h @@ -74,6 +74,7 @@ typedef struct ngap_gNB_ue_context_s { } ngap_gNB_ue_context_t; void ngap_store_ue_context(const ngap_gNB_ue_context_t *ue_desc_p); +void ngap_release_ues_for_amf(ngap_gNB_amf_data_t *amf_desc_p); ngap_gNB_ue_context_t *ngap_get_ue_context(uint32_t gNB_ue_ngap_id); ngap_gNB_ue_context_t *ngap_get_ue_context_from_amf_ue_ngap_id(uint32_t amf_ue_ngap_id);