Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 22 additions & 10 deletions context/amf_ue.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2022-present Intel Corporation
// Copyright (c) 2022-present Intel Corporation
// SPDX-FileCopyrightText: 2021 Open Networking Foundation <info@opennetworking.org>
// Copyright 2019 free5GC.org
Comment thread
gab-arrobo marked this conversation as resolved.
//
Expand Down Expand Up @@ -507,18 +507,30 @@ func (ue *AmfUe) DetachRanUe(anType models.AccessType) {

func (ue *AmfUe) AttachRanUe(ranUe *RanUe) {
/* detach any RanUe associated to it */
oldRanUe := ue.RanUe[ranUe.Ran.AnType]
ue.RanUe[ranUe.Ran.AnType] = ranUe
anType := ranUe.Ran.AnType
oldRanUe := ue.RanUe[anType]
if oldRanUe == ranUe {
ranUe.AmfUe = ue
ue.updateAttachedRanUeLogs(ranUe)
return
}
ue.RanUe[anType] = ranUe
ranUe.AmfUe = ue

go func() {
time.Sleep(time.Second * 2)
if oldRanUe != nil {
logger.ContextLog.Infof("detached UeContext from OldRanUe %v", oldRanUe.AmfUeNgapId)
oldRanUe.AmfUe = nil
}
}()
if oldRanUe != nil {
go func(oldRanUe *RanUe, anType models.AccessType) {
time.Sleep(time.Second * 2)
if oldRanUe.AmfUe == ue && ue.RanUe[anType] != oldRanUe {
logger.ContextLog.Infof("detached UeContext from OldRanUe %v", oldRanUe.AmfUeNgapId)
oldRanUe.AmfUe = nil
Comment thread
gab-arrobo marked this conversation as resolved.
Outdated
}
Comment thread
gab-arrobo marked this conversation as resolved.
}(oldRanUe, anType)
}

ue.updateAttachedRanUeLogs(ranUe)
}

func (ue *AmfUe) updateAttachedRanUeLogs(ranUe *RanUe) {
// set log information
ue.NASLog = logger.NasLog.With(logger.FieldAmfUeNgapID, fmt.Sprintf("AMF_UE_NGAP_ID:%d", ranUe.AmfUeNgapId))
ue.GmmLog = logger.GmmLog.With(logger.FieldAmfUeNgapID, fmt.Sprintf("AMF_UE_NGAP_ID:%d", ranUe.AmfUeNgapId))
Expand Down
11 changes: 10 additions & 1 deletion ngap/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -1030,7 +1030,16 @@ func HandleUEContextReleaseComplete(ctx ctxt.Context, ran *context.AmfRan, messa
ranUe.Ran = ran
amfUe := ranUe.AmfUe
if amfUe == nil {
ran.Log.Infof("Release UE Context : RanUe[AmfUeNgapId: %d]", ranUe.AmfUeNgapId)
ran.Log.Infof("release UE Context: RanUe[AmfUeNgapId: %d]", ranUe.AmfUeNgapId)
err := ranUe.Remove()
if err != nil {
ran.Log.Errorln(err.Error())
}
return
Comment thread
gab-arrobo marked this conversation as resolved.
}
if currentRanUe := amfUe.RanUe[ran.AnType]; currentRanUe != nil && currentRanUe != ranUe {
Comment thread
gab-arrobo marked this conversation as resolved.
Outdated
ran.Log.Infof("release UE Context for stale RanUe[AmfUeNgapId: %d]; keeping current RanUe[AmfUeNgapId: %d]",
ranUe.AmfUeNgapId, currentRanUe.AmfUeNgapId)
Comment thread
gab-arrobo marked this conversation as resolved.
err := ranUe.Remove()
if err != nil {
ran.Log.Errorln(err.Error())
Expand Down
173 changes: 171 additions & 2 deletions ngap/handler_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
// Copyright (C) 2026 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//

package ngap

Expand All @@ -10,7 +8,9 @@ import (
"testing"

"github.com/omec-project/amf/context"
"github.com/omec-project/amf/logger"
"github.com/omec-project/ngap/v2/ngapType"
"github.com/omec-project/openapi/v2/models"
"go.uber.org/zap"
)

Expand Down Expand Up @@ -114,3 +114,172 @@ func TestPrintAndGetCauseIgnoresNilCauseMembers(t *testing.T) {
printAndGetCause(ran, nil)
printAndGetCause(ran, &ngapType.Cause{Present: ngapType.CausePresentProtocol})
}

func TestHandleUEContextReleaseCompleteRemovesStaleRanUe(t *testing.T) {
self := context.AMF_Self()
oldRan := &context.AmfRan{AnType: models.ACCESSTYPE__3_GPP_ACCESS, Log: zap.NewNop().Sugar()}
newRan := &context.AmfRan{AnType: models.ACCESSTYPE__3_GPP_ACCESS, Log: zap.NewNop().Sugar()}
amfUe := self.NewAmfUe("")
oldRanUe, err := oldRan.NewRanUe(1)
if err != nil {
t.Fatalf("unexpected error creating old RanUe: %v", err)
}
newRanUe, err := newRan.NewRanUe(2)
if err != nil {
t.Fatalf("unexpected error creating new RanUe: %v", err)
}
oldRanUe.Log = logger.NgapLog
newRanUe.Log = logger.NgapLog

amfUe.AttachRanUe(oldRanUe)
amfUe.AttachRanUe(newRanUe)

Comment thread
gab-arrobo marked this conversation as resolved.
pdu := &ngapType.NGAPPDU{
Present: ngapType.NGAPPDUPresentSuccessfulOutcome,
SuccessfulOutcome: &ngapType.SuccessfulOutcome{
ProcedureCode: ngapType.ProcedureCode{Value: ngapType.ProcedureCodeUEContextRelease},
Value: ngapType.SuccessfulOutcomeValue{
Present: ngapType.SuccessfulOutcomePresentUEContextReleaseComplete,
UEContextReleaseComplete: &ngapType.UEContextReleaseComplete{
ProtocolIEs: ngapType.ProtocolIEContainerUEContextReleaseCompleteIEs{
List: []ngapType.UEContextReleaseCompleteIEs{
{
Id: ngapType.ProtocolIEID{Value: ngapType.ProtocolIEIDAMFUENGAPID},
Value: ngapType.UEContextReleaseCompleteIEsValue{
Present: ngapType.UEContextReleaseCompleteIEsPresentAMFUENGAPID,
AMFUENGAPID: &ngapType.AMFUENGAPID{Value: oldRanUe.AmfUeNgapId},
},
},
{
Id: ngapType.ProtocolIEID{Value: ngapType.ProtocolIEIDRANUENGAPID},
Value: ngapType.UEContextReleaseCompleteIEsValue{
Present: ngapType.UEContextReleaseCompleteIEsPresentRANUENGAPID,
RANUENGAPID: &ngapType.RANUENGAPID{Value: oldRanUe.RanUeNgapId},
},
},
},
},
},
},
},
}

defer func() {
if recovered := recover(); recovered != nil {
t.Fatalf("HandleUEContextReleaseComplete panicked for stale RanUe: %v", recovered)
}
if leftover := self.RanUeFindByAmfUeNgapIDLocal(newRanUe.AmfUeNgapId); leftover != nil {
if err := leftover.Remove(); err != nil {
t.Fatalf("cleanup current RanUe failed: %v", err)
}
}
if leftover := self.RanUeFindByAmfUeNgapIDLocal(oldRanUe.AmfUeNgapId); leftover != nil {
if err := leftover.Remove(); err != nil {
t.Fatalf("cleanup stale RanUe failed: %v", err)
}
}
}()

HandleUEContextReleaseComplete(ctxt.Background(), oldRan, pdu)

if amfUe.RanUe[models.ACCESSTYPE__3_GPP_ACCESS] != newRanUe {
t.Fatal("expected current RanUe association to remain attached")
}
if oldRanUe.AmfUe != nil {
t.Fatal("expected stale RanUe association to be cleared")
}
if self.RanUeFindByAmfUeNgapIDLocal(oldRanUe.AmfUeNgapId) != nil {
t.Fatal("expected stale RanUe to be removed from the pool")
}
if self.RanUeFindByAmfUeNgapIDLocal(newRanUe.AmfUeNgapId) != newRanUe {
t.Fatal("expected current RanUe to remain in the pool")
}
}

func TestHandleUEContextReleaseCompleteHandoverPromotesTargetRanUe(t *testing.T) {
self := context.AMF_Self()
sourceRan := &context.AmfRan{AnType: models.ACCESSTYPE__3_GPP_ACCESS, Log: zap.NewNop().Sugar()}
targetRan := &context.AmfRan{AnType: models.ACCESSTYPE__3_GPP_ACCESS, Log: zap.NewNop().Sugar()}
amfUe := self.NewAmfUe("")
sourceRanUe, err := sourceRan.NewRanUe(10)
if err != nil {
t.Fatalf("unexpected error creating source RanUe: %v", err)
}
targetRanUe, err := targetRan.NewRanUe(20)
if err != nil {
t.Fatalf("unexpected error creating target RanUe: %v", err)
}
sourceRanUe.Log = logger.NgapLog
targetRanUe.Log = logger.NgapLog
sourceRanUe.ReleaseAction = context.UeContextReleaseHandover

amfUe.AttachRanUe(sourceRanUe)
context.AttachSourceUeTargetUe(sourceRanUe, targetRanUe)

pdu := &ngapType.NGAPPDU{
Present: ngapType.NGAPPDUPresentSuccessfulOutcome,
SuccessfulOutcome: &ngapType.SuccessfulOutcome{
ProcedureCode: ngapType.ProcedureCode{Value: ngapType.ProcedureCodeUEContextRelease},
Value: ngapType.SuccessfulOutcomeValue{
Present: ngapType.SuccessfulOutcomePresentUEContextReleaseComplete,
UEContextReleaseComplete: &ngapType.UEContextReleaseComplete{
ProtocolIEs: ngapType.ProtocolIEContainerUEContextReleaseCompleteIEs{
List: []ngapType.UEContextReleaseCompleteIEs{
{
Id: ngapType.ProtocolIEID{Value: ngapType.ProtocolIEIDAMFUENGAPID},
Value: ngapType.UEContextReleaseCompleteIEsValue{
Present: ngapType.UEContextReleaseCompleteIEsPresentAMFUENGAPID,
AMFUENGAPID: &ngapType.AMFUENGAPID{Value: sourceRanUe.AmfUeNgapId},
},
},
{
Id: ngapType.ProtocolIEID{Value: ngapType.ProtocolIEIDRANUENGAPID},
Value: ngapType.UEContextReleaseCompleteIEsValue{
Present: ngapType.UEContextReleaseCompleteIEsPresentRANUENGAPID,
RANUENGAPID: &ngapType.RANUENGAPID{Value: sourceRanUe.RanUeNgapId},
},
},
},
},
},
},
},
}

defer func() {
if recovered := recover(); recovered != nil {
t.Fatalf("HandleUEContextReleaseComplete panicked for handover release: %v", recovered)
}
if leftover := self.RanUeFindByAmfUeNgapIDLocal(targetRanUe.AmfUeNgapId); leftover != nil {
if err := leftover.Remove(); err != nil {
t.Fatalf("cleanup target RanUe failed: %v", err)
}
}
if leftover := self.RanUeFindByAmfUeNgapIDLocal(sourceRanUe.AmfUeNgapId); leftover != nil {
if err := leftover.Remove(); err != nil {
t.Fatalf("cleanup source RanUe failed: %v", err)
}
}
}()

HandleUEContextReleaseComplete(ctxt.Background(), sourceRan, pdu)

if amfUe.RanUe[models.ACCESSTYPE__3_GPP_ACCESS] != targetRanUe {
Comment thread
gab-arrobo marked this conversation as resolved.
Outdated
t.Fatal("expected target RanUe to become the current association after handover release")
}
if sourceRanUe.AmfUe != nil {
t.Fatal("expected source RanUe to be detached after handover release")
}
if sourceRanUe.TargetUe != nil {
t.Fatal("expected source-target link to be cleared after handover release")
}
if targetRanUe.SourceUe != nil {
t.Fatal("expected target-source link to be cleared after handover release")
}
if self.RanUeFindByAmfUeNgapIDLocal(sourceRanUe.AmfUeNgapId) != nil {
t.Fatal("expected source RanUe to be removed from the pool")
}
if self.RanUeFindByAmfUeNgapIDLocal(targetRanUe.AmfUeNgapId) != targetRanUe {
t.Fatal("expected target RanUe to remain in the pool")
}
}
Loading