Skip to content

Commit

Permalink
Add support for NGAP Handover
Browse files Browse the repository at this point in the history
Signed-off-by: Valentin <[email protected]>
  • Loading branch information
linouxis9 committed Dec 28, 2023
1 parent 1c66eba commit 79c3b8f
Show file tree
Hide file tree
Showing 19 changed files with 920 additions and 66 deletions.
5 changes: 3 additions & 2 deletions cmd/packetrusher.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ func main() {
&cli.IntFlag{Name: "number-of-ues", Value: 1, Aliases: []string{"n"}},
&cli.IntFlag{Name: "timeBetweenRegistration", Value: 500, Aliases: []string{"tr"}, Usage: "The time in ms, between UE registration."},
&cli.IntFlag{Name: "timeBeforeDeregistration", Value: 0, Aliases: []string{"td"}, Usage: "The time in ms, before a UE deregisters once it has been registered. 0 to disable auto-deregistration."},
&cli.IntFlag{Name: "timeBeforeHandover", Value: 0, Aliases: []string{"th"}, Usage: "The time in ms, before triggering a UE handover. 0 to disable handover. This requires at least two gNodeB, eg: two N2/N3 IPs."},
&cli.IntFlag{Name: "timeBeforeNgapHandover", Value: 0, Aliases: []string{"ngh"}, Usage: "The time in ms, before triggering a UE handover using NGAP Handover. 0 to disable handover. This requires at least two gNodeB, eg: two N2/N3 IPs."},
&cli.IntFlag{Name: "timeBeforeXnHandover", Value: 0, Aliases: []string{"xnh"}, Usage: "The time in ms, before triggering a UE handover using Xn Handover. 0 to disable handover. This requires at least two gNodeB, eg: two N2/N3 IPs."},
&cli.IntFlag{Name: "numPduSessions", Value: 1, Aliases: []string{"nPdu"}, Usage: "The number of PDU Sessions to create"},
&cli.BoolFlag{Name: "loop", Aliases: []string{"l"}, Usage: "Enable the creation of the GTP-U tunnel interface."},
&cli.BoolFlag{Name: "tunnel", Aliases: []string{"t"}, Usage: "Enable the creation of the GTP-U tunnel interface."},
Expand Down Expand Up @@ -120,7 +121,7 @@ func main() {
pcap.CaptureTraffic(c.Path("pcap"))
}

templates.TestMultiUesInQueue(numUes, c.Bool("tunnel"), c.Bool("dedicatedGnb"), c.Bool("loop"), c.Int("timeBetweenRegistration"), c.Int("timeBeforeDeregistration"), c.Int("timeBeforeHandover"), c.Int("numPduSessions"))
templates.TestMultiUesInQueue(numUes, c.Bool("tunnel"), c.Bool("dedicatedGnb"), c.Bool("loop"), c.Int("timeBetweenRegistration"), c.Int("timeBeforeDeregistration"), c.Int("timeBeforeNgapHandover"), c.Int("timeBeforeXnHandover"), c.Int("numPduSessions"))

return nil
},
Expand Down
31 changes: 23 additions & 8 deletions internal/common/tools/tools.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@ type UESimulationConfig struct {
Cfg config.Config
ScenarioChan chan procedures.UeTesterMessage
TimeBeforeDeregistration int
TimeBeforeHandover int
TimeBeforeNgapHandover int
TimeBeforeXnHandover int
NumPduSessions int
}

Expand All @@ -113,7 +114,9 @@ func SimulateSingleUE(simConfig UESimulationConfig, wg *sync.WaitGroup) {
ueCfg.Ue.Msin = IncrementMsin(simConfig.UeId, simConfig.Cfg.Ue.Msin)
log.Info("[TESTER] TESTING REGISTRATION USING IMSI ", ueCfg.Ue.Msin, " UE")

gnbId := gnbIdGenerator(simConfig.UeId%numGnb, ueCfg.GNodeB.PlmnList.GnbId)
gnbIdGen := func(index int) string {
return gnbIdGenerator((simConfig.UeId+index)%numGnb, ueCfg.GNodeB.PlmnList.GnbId)
}

// Launch a coroutine to handle UE's individual scenario
go func(scenarioChan chan procedures.UeTesterMessage, ueId int) {
Expand All @@ -123,7 +126,7 @@ func SimulateSingleUE(simConfig UESimulationConfig, wg *sync.WaitGroup) {

// Create a new UE coroutine
// ue.NewUE returns context of the new UE
ueTx := ue.NewUE(ueCfg, ueId, ueRx, simConfig.Gnbs[gnbId], wg)
ueTx := ue.NewUE(ueCfg, ueId, ueRx, simConfig.Gnbs[gnbIdGen(0)], wg)

// We tell the UE to perform a registration
ueRx <- procedures.UeTesterMessage{Type: procedures.Registration}
Expand All @@ -132,11 +135,19 @@ func SimulateSingleUE(simConfig UESimulationConfig, wg *sync.WaitGroup) {
if simConfig.TimeBeforeDeregistration != 0 {
deregistrationChannel = time.After(time.Duration(simConfig.TimeBeforeDeregistration) * time.Millisecond)
}
var handoverChannel <-chan time.Time = nil
if simConfig.TimeBeforeHandover != 0 {
handoverChannel = time.After(time.Duration(simConfig.TimeBeforeHandover) * time.Millisecond)

nextHandoverId := 0
var ngapHandoverChannel <-chan time.Time = nil
if simConfig.TimeBeforeNgapHandover != 0 {
ngapHandoverChannel = time.After(time.Duration(simConfig.TimeBeforeNgapHandover) * time.Millisecond)
}
var xnHandoverChannel <-chan time.Time = nil
if simConfig.TimeBeforeXnHandover != 0 {
xnHandoverChannel = time.After(time.Duration(simConfig.TimeBeforeXnHandover) * time.Millisecond)
}

log.Error(simConfig.TimeBeforeNgapHandover)
log.Error(simConfig.TimeBeforeXnHandover)
loop := true
state := ueCtx.MM5G_NULL
for loop {
Expand All @@ -146,8 +157,12 @@ func SimulateSingleUE(simConfig UESimulationConfig, wg *sync.WaitGroup) {
ueRx <- procedures.UeTesterMessage{Type: procedures.Terminate}
ueRx = nil
}
case <-handoverChannel:
trigger.TriggerHandover(simConfig.Gnbs[gnbId], simConfig.Gnbs[gnbIdGenerator((ueId+1)%numGnb, ueCfg.GNodeB.PlmnList.GnbId)], int64(ueId))
case <-ngapHandoverChannel:
trigger.TriggerNgapHandover(simConfig.Gnbs[gnbIdGen(nextHandoverId)], simConfig.Gnbs[gnbIdGen(nextHandoverId+1)], int64(ueId))
nextHandoverId++
case <-xnHandoverChannel:
trigger.TriggerXnHandover(simConfig.Gnbs[gnbIdGen(nextHandoverId)], simConfig.Gnbs[gnbIdGen(nextHandoverId+1)], int64(ueId))
nextHandoverId++
case msg := <-scenarioChan:
if ueRx != nil {
ueRx <- msg
Expand Down
24 changes: 23 additions & 1 deletion internal/control_test_engine/gnb/context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import (
"fmt"
"sync"

"github.com/free5gc/aper"
"github.com/free5gc/ngap/ngapConvert"
"github.com/free5gc/ngap/ngapType"
"github.com/free5gc/openapi/models"
"github.com/ishidawataru/sctp"
log "github.com/sirupsen/logrus"
gtpv1 "github.com/wmnsk/go-gtp/gtpv1"
Expand Down Expand Up @@ -99,7 +103,9 @@ func (gnb *GNBContext) NewGnBUe(gnbTx chan UEMessage, gnbRx chan UEMessage, prUe

// store UE in the UE Pool of GNB.
gnb.uePool.Store(ranId, ue)
gnb.prUePool.Store(prUeId, ue)
if prUeId != 0 {
gnb.prUePool.Store(prUeId, ue)
}

// select AMF with Capacity is more than 0.
amf := gnb.selectAmFByActive()
Expand Down Expand Up @@ -399,6 +405,22 @@ func (gnb *GNBContext) GetSliceInBytes() ([]byte, []byte) {
return sstBytes, nil
}

func (gnb *GNBContext) GetPLMNIdentity() ngapType.PLMNIdentity {
return ngapConvert.PlmnIdToNgap(models.PlmnId{Mcc: gnb.controlInfo.mcc, Mnc: gnb.controlInfo.mnc})
}

func (gnb *GNBContext) GetNRCellIdentity() ngapType.NRCellIdentity {
nci := gnb.GetGnbIdInBytes()
var slice = make([]byte, 2)

return ngapType.NRCellIdentity{
Value: aper.BitString{
Bytes: append(nci, slice...),
BitLength: 36,
},
}
}

func (gnb *GNBContext) GetMccAndMnc() (string, string) {
return gnb.controlInfo.mcc, gnb.controlInfo.mnc
}
Expand Down
1 change: 1 addition & 0 deletions internal/control_test_engine/gnb/context/msg.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ type UEMessage struct {
Mcc string
Mnc string
UEContext *GNBUe
IsHandover bool
}
9 changes: 9 additions & 0 deletions internal/control_test_engine/gnb/context/ue.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type GNBUe struct {
pRueId int64 // PacketRusher unique UE ID
context Context
lock sync.Mutex
newGnb *GNBContext
}

type Context struct {
Expand Down Expand Up @@ -204,6 +205,14 @@ func (ue *GNBUe) SetStateDown() {
ue.state = Down
}

func (ue *GNBUe) SetHandoverGnodeB(gnb *GNBContext) {
ue.newGnb = gnb
}

func (ue *GNBUe) GetHandoverGnodeB() *GNBContext {
return ue.newGnb
}

func (ue *GNBUe) GetGnbRx() chan UEMessage {
return ue.gnbRx
}
Expand Down
41 changes: 31 additions & 10 deletions internal/control_test_engine/gnb/nas/service/service.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
/**
* SPDX-License-Identifier: Apache-2.0
* © Copyright 2023 Hewlett Packard Enterprise Development LP
* © Copyright 2023 Valentin D'Emmanuele
*/
package service

import (
"my5G-RANTester/internal/control_test_engine/gnb/context"
"my5G-RANTester/internal/control_test_engine/gnb/nas"
"my5G-RANTester/internal/control_test_engine/gnb/nas/message/sender"
"my5G-RANTester/internal/control_test_engine/gnb/ngap/trigger"

log "github.com/sirupsen/logrus"
Expand All @@ -29,17 +31,36 @@ func gnbListen(gnb *context.GNBContext) {
// store UE connection
// select AMF and get sctp association
// make a tun interface
ue := gnb.NewGnBUe(message.GNBTx, message.GNBRx, message.PrUeId)
if message.UEContext == nil {
log.Info("[GNB] Received incoming connection from new UE")
mcc, mnc := gnb.GetMccAndMnc()
message.GNBTx <- context.UEMessage{Mcc: mcc, Mnc: mnc}
ue.SetPduSessions(message.GNBPduSessions)
} else {
log.Info("[GNB] Received incoming handover for UE from another gNodeB")
ue, _ := gnb.GetGnbUeByPrUeId(message.PrUeId)
if ue != nil && message.IsHandover {
// We already have a context for this UE since it was sent to us by the AMF from a NGAP Handover
// Notify the AMF that the UE has succesfully been handed over to US
ue.SetGnbRx(message.GNBRx)
ue.SetGnbTx(message.GNBTx)

// We enable the new PDU Session handed over to us
msg := context.UEMessage{GNBPduSessions: ue.GetPduSessions(), GnbIp: gnb.GetN3GnbIp()}
sender.SendMessageToUe(ue, msg)

ue.SetStateReady()
ue.CopyFromPreviousContext(message.UEContext)
trigger.SendPathSwitchRequest(gnb, ue)

trigger.SendHandoverNotify(gnb, ue)
} else {
ue = gnb.NewGnBUe(message.GNBTx, message.GNBRx, message.PrUeId)
if message.UEContext != nil && message.IsHandover {
// Xn Handover
log.Info("[GNB] Received incoming handover for UE from another gNodeB")
ue.SetStateReady()
ue.CopyFromPreviousContext(message.UEContext)
trigger.SendPathSwitchRequest(gnb, ue)

} else {
// Usual first UE connection to a gNodeB
log.Info("[GNB] Received incoming connection from new UE")
mcc, mnc := gnb.GetMccAndMnc()
message.GNBTx <- context.UEMessage{Mcc: mcc, Mnc: mnc}
ue.SetPduSessions(message.GNBPduSessions)
}
}

if ue == nil {
Expand Down
10 changes: 10 additions & 0 deletions internal/control_test_engine/gnb/ngap/dispatcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ func Dispatch(amf *context.GNBAmf, gnb *context.GNBContext, message []byte) {
log.Info("[GNB][NGAP] Receive AMF Configuration Update")
handler.HandlerAmfConfigurationUpdate(amf, gnb, ngapMsg)

case ngapType.ProcedureCodeHandoverResourceAllocation:
// handler NGAP Handover REquest
log.Info("[GNB][NGAP] Receive Handover Request")
handler.HandlerHandoverRequest(amf, gnb, ngapMsg)

case ngapType.ProcedureCodeErrorIndication:
// handler Error Indicator
log.Error("[GNB][NGAP] Receive Error Indication")
Expand All @@ -89,6 +94,11 @@ func Dispatch(amf *context.GNBAmf, gnb *context.GNBContext, message []byte) {
log.Info("[GNB][NGAP] Receive PathSwitchRequestAcknowledge")
handler.HandlerPathSwitchRequestAcknowledge(gnb, ngapMsg)

case ngapType.ProcedureCodeHandoverPreparation:
// handler NGAP AMF Handover Command
log.Info("[GNB][NGAP] Receive Handover Command")
handler.HandlerHandoverCommand(amf, gnb, ngapMsg)

default:
log.Info("[GNB][NGAP] Received unknown NGAP message")
}
Expand Down
Loading

0 comments on commit 79c3b8f

Please sign in to comment.