Skip to content

Commit

Permalink
Add RAN Areas
Browse files Browse the repository at this point in the history
  • Loading branch information
louisroyer committed Jan 16, 2025
1 parent 1b6ae12 commit 7f3a343
Show file tree
Hide file tree
Showing 11 changed files with 150 additions and 29 deletions.
20 changes: 19 additions & 1 deletion config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,29 @@ slices:
nextmn-lite:
pool: "10.0.0.0/24"
upfs:
- node-id: "203.0.113.2" # only the first upf is used for now
- node-id: "203.0.113.2" # srgw1
interfaces:
- type: "N3"
addr: "198.51.100.11"
- node-id: "203.0.113.3" # srgw2
interfaces:
- type: "N3"
addr: "198.51.100.12"

areas: # RAN areas
area1:
gnbs: # list of gnbs in the area
- "http://192.0.2.2:8080" # gnb1
- "http://192.0.2.4.8080" # gnb2
paths: # define one path per slice
nextmn-lite:
- "203.0.113.2" # srgw1
area2:
gnbs:
- "http://192.0.2.5:8080" # gnb3
paths:
nextmn-lite:
- "203.0.113.3" # srgw2

logger:
level: "trace"
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ require (

require (
github.com/bytedance/sonic v1.12.7 // indirect
github.com/bytedance/sonic/loader v0.2.2 // indirect
github.com/bytedance/sonic/loader v0.2.3 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ github.com/bytedance/sonic v1.12.7/go.mod h1:tnbal4mxOMju17EGfknm2XyYcpyCnIROYOE
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/bytedance/sonic/loader v0.2.2 h1:jxAJuN9fOot/cyz5Q6dUuMJF5OqQ6+5GfA8FjjQ0R4o=
github.com/bytedance/sonic/loader v0.2.2/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
github.com/bytedance/sonic/loader v0.2.3 h1:yctD0Q3v2NOGfSWPLPvG2ggA2kV6TS6s4wioyEqssH0=
github.com/bytedance/sonic/loader v0.2.3/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
Expand Down
4 changes: 3 additions & 1 deletion internal/amf/handover_notify.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ func (amf *Amf) HandoverNotify(c *gin.Context) {
logrus.WithFields(logrus.Fields{
"ue": m.UeCtrl.String(),
"gnb-target": m.TargetGnb.String(),
"gbn-source": m.SourceGnb.String(),
}).Info("New Handover Confirm")
go amf.HandleHandoverNotify(m)
c.JSON(http.StatusAccepted, jsonapi.Message{Message: "please refer to logs for more information"})
Expand All @@ -40,11 +41,12 @@ func (amf *Amf) HandleHandoverNotify(m n1n2.HandoverNotify) {
ctx := amf.Context()
for _, session := range m.Sessions {
// Note: for the moment, we are only doing direct forwarding (step 2)
if err := amf.smf.UpdateSessionDownlinkContext(ctx, m.UeCtrl, session.Addr, session.Dnn); err != nil {
if err := amf.smf.UpdateSessionDownlinkContext(ctx, m.UeCtrl, session.Addr, session.Dnn, m.SourceGnb); err != nil {
logrus.WithError(err).WithFields(logrus.Fields{
"ue": m.UeCtrl.String(),
"pdu-session": session.Addr,
"dnn": session.Dnn,
"gnb-source": m.SourceGnb,
}).Error("Handover Notify: could not update session downlink path")
}

Expand Down
2 changes: 1 addition & 1 deletion internal/amf/n2_establishment_response.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func (amf *Amf) N2EstablishmentResponse(c *gin.Context) {

func (amf *Amf) HandleN2EstablishmentResponse(ps n1n2.N2PduSessionRespMsg) {
ctx := amf.Context()
pduSession, err := amf.smf.CreateSessionDownlinkContext(ctx, ps.UeInfo.Header.Ue, ps.UeInfo.Addr, ps.UeInfo.Header.Dnn, ps.DownlinkFteid.Addr, ps.DownlinkFteid.Teid)
pduSession, err := amf.smf.CreateSessionDownlinkContext(ctx, ps.UeInfo.Header.Ue, ps.UeInfo.Addr, ps.UeInfo.Header.Dnn, ps.UeInfo.Header.Gnb, ps.DownlinkFteid)
if err != nil {
logrus.WithError(err).WithFields(logrus.Fields{
"ue-ip-addr": ps.UeInfo.Addr,
Expand Down
2 changes: 1 addition & 1 deletion internal/app/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ type Setup struct {
}

func NewSetup(config *config.CPConfig) *Setup {
smf := smf.NewSmf(config.Pfcp, config.Slices)
smf := smf.NewSmf(config.Pfcp, config.Slices, config.Areas)
return &Setup{
config: config,
amf: amf.NewAmf(config.Control.BindAddr, config.Control.Uri, "go-github-nextmn-cp-lite", smf),
Expand Down
10 changes: 8 additions & 2 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,10 @@ func ParseConf(file string) (*CPConfig, error) {

type CPConfig struct {
Control Control `yaml:"control"`
Logger *Logger `yaml:"logger,omitempty"`
Slices map[string]Slice `yaml:"slices"`
Pfcp netip.Addr `yaml:"pfcp"`
Slices map[string]Slice `yaml:"slices"`
Areas map[string]Area `yaml:"areas"`
Logger *Logger `yaml:"logger,omitempty"`
}

type Control struct {
Expand All @@ -57,3 +58,8 @@ type Interface struct {
Type string `yaml:"type"`
Addr netip.Addr `yaml:"addr"`
}

type Area struct {
Gnbs []jsonapi.ControlURI `yaml:"gnbs"`
Paths map[string][]netip.Addr `yaml:"paths"`
}
46 changes: 46 additions & 0 deletions internal/smf/area.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright Louis Royer and the NextMN contributors. All rights reserved.
// Use of this source code is governed by a MIT-style license that can be
// found in the LICENSE file.
// SPDX-License-Identifier: MIT

package smf

import (
"slices"

"github.com/nextmn/cp-lite/internal/config"

"github.com/nextmn/json-api/jsonapi"
)

type AreasMap struct {
content map[string][]jsonapi.ControlURI
}

func NewAreasMap(areas map[string]config.Area) AreasMap {
m := AreasMap{
content: make(map[string][]jsonapi.ControlURI),
}
for k, area := range areas {
m.content[k] = area.Gnbs
}
return m
}

func (a AreasMap) Area(gnb jsonapi.ControlURI) (string, bool) {
for name, area := range a.content {
if slices.Contains(area, gnb) {
return name, true
}
}
return "", false
}

func (a AreasMap) Contains(areaName string, gnb jsonapi.ControlURI) bool {
if area, ok := a.content[areaName]; ok {
if slices.Contains(area, gnb) {
return true
}
}
return false
}
2 changes: 2 additions & 0 deletions internal/smf/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (
var (
ErrDnnNotFound = errors.New("DNN not found")
ErrPDUSessionNotFound = errors.New("PDU Session not found")
ErrAreaNotFound = errors.New("RAN Area not found for this gNB")
ErrPathNotFound = errors.New("Path not found for this RAN Area")

ErrUpfNotAssociated = errors.New("UPF not associated")
ErrUpfNotFound = errors.New("UPF not found")
Expand Down
16 changes: 13 additions & 3 deletions internal/smf/slice.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,22 @@ type SlicesMap struct {
sync.Map // slice name: Slice
}

func NewSlicesMap(slices map[string]config.Slice) *SlicesMap {
func NewSlicesMap(slices map[string]config.Slice, areas map[string]config.Area) *SlicesMap {
m := SlicesMap{}
for k, slice := range slices {
upfs := make([]netip.Addr, len(slice.Upfs))
for i, upf := range slice.Upfs {
upfs[i] = upf.NodeID
}
sl := NewSlice(slice.Pool, upfs)

paths := make(map[string][]netip.Addr)
for area_name, area := range areas {
if path, exists := area.Paths[k]; exists {
paths[area_name] = path
}
}

sl := NewSlice(slice.Pool, upfs, paths)
m.Store(k, sl)
}
return &m
Expand All @@ -33,12 +41,14 @@ type Slice struct {
Upfs []netip.Addr
Pool *UeIpPool
sessions *SessionsMap
Paths map[string][]netip.Addr
}

func NewSlice(pool netip.Prefix, upfs []netip.Addr) *Slice {
func NewSlice(pool netip.Prefix, upfs []netip.Addr, paths map[string][]netip.Addr) *Slice {
return &Slice{
Pool: NewUeIpPool(pool),
Upfs: upfs,
sessions: NewSessionsMap(),
Paths: paths,
}
}
73 changes: 54 additions & 19 deletions internal/smf/smf.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type UpfPath []netip.Addr
type Smf struct {
upfs *UpfsMap
slices *SlicesMap
areas AreasMap
srv *pfcp.PFCPEntityCP
started bool
closed chan struct{}
Expand All @@ -32,13 +33,14 @@ type Smf struct {
ctx context.Context
}

func NewSmf(addr netip.Addr, slices map[string]config.Slice) *Smf {
s := NewSlicesMap(slices)
func NewSmf(addr netip.Addr, slices map[string]config.Slice, areas map[string]config.Area) *Smf {
s := NewSlicesMap(slices, areas)
upfs := NewUpfsMap(slices)
return &Smf{
srv: pfcp.NewPFCPEntityCP(addr.String(), addr),
slices: s,
upfs: upfs,
areas: NewAreasMap(areas),
closed: make(chan struct{}),
ctx: nil,
}
Expand Down Expand Up @@ -101,11 +103,11 @@ func (smf *Smf) Context() context.Context {
return context.Background()
}

func (smf *Smf) CreateSessionDownlink(ueCtrl jsonapi.ControlURI, ueIp netip.Addr, dnn string, gnb netip.Addr, gnb_teid uint32) (*PduSessionN3, error) {
return smf.CreateSessionDownlinkContext(smf.ctx, ueCtrl, ueIp, dnn, gnb, gnb_teid)
func (smf *Smf) CreateSessionDownlink(ueCtrl jsonapi.ControlURI, ueIp netip.Addr, dnn string, gnbCtrl jsonapi.ControlURI, gnbFteid jsonapi.Fteid) (*PduSessionN3, error) {
return smf.CreateSessionDownlinkContext(smf.ctx, ueCtrl, ueIp, dnn, gnbCtrl, gnbFteid)
}

func (smf *Smf) CreateSessionDownlinkContext(ctx context.Context, ueCtrl jsonapi.ControlURI, ueIp netip.Addr, dnn string, gnb netip.Addr, gnb_teid uint32) (*PduSessionN3, error) {
func (smf *Smf) CreateSessionDownlinkContext(ctx context.Context, ueCtrl jsonapi.ControlURI, ueIp netip.Addr, dnn string, gnbCtrl jsonapi.ControlURI, gnbFteid jsonapi.Fteid) (*PduSessionN3, error) {
if !smf.started {
return nil, ErrSmfNotStarted
}
Expand All @@ -131,12 +133,23 @@ func (smf *Smf) CreateSessionDownlinkContext(ctx context.Context, ueCtrl jsonapi
if err != nil {
return nil, err
}
session.DownlinkFteid = jsonapi.NewFteid(gnb, gnb_teid)
session.DownlinkFteid = &gnbFteid
if len(slice.Upfs) == 0 {
return nil, ErrUpfNotFound
}
last_fteid := session.DownlinkFteid
for i, upf_ctrl := range slice.Upfs {

area, ok := smf.areas.Area(gnbCtrl)
if !ok {
return nil, ErrAreaNotFound
}

path, ok := slice.Paths[area]
if !ok {
return nil, ErrPathNotFound
}

for i, upf_ctrl := range path {
upf_any, ok := smf.upfs.Load(upf_ctrl)
if !ok {
return nil, ErrUpfNotFound
Expand Down Expand Up @@ -202,18 +215,28 @@ func (smf *Smf) CreateSessionUplinkContext(ctx context.Context, ueCtrl jsonapi.C
}
// create new session
// 1. check path
if len(slice.Upfs) == 0 {
area, ok := smf.areas.Area(gnbCtrl)
if !ok {
return nil, ErrAreaNotFound
}

path, ok := slice.Paths[area]
if !ok {
return nil, ErrPathNotFound
}

if len(path) == 0 {
return nil, ErrUpfNotFound
}
// 2. init anchor
upfa_ctrl := slice.Upfs[len(slice.Upfs)-1]
upfa_ctrl := path[len(path)-1]
upfa_any, ok := smf.upfs.Load(upfa_ctrl)
if !ok {
return nil, ErrUpfNotFound
}
upfa := upfa_any.(*Upf)
var upfa_iface netip.Addr
if len(slice.Upfs) == 1 {
if len(path) == 1 {
upfa_iface, err = upfa.GetN3()
if err != nil {
logrus.WithError(err).WithFields(logrus.Fields{
Expand Down Expand Up @@ -243,8 +266,8 @@ func (smf *Smf) CreateSessionUplinkContext(ctx context.Context, ueCtrl jsonapi.C
}

// 3. init path from anchor
for i := len(slice.Upfs) - 2; i >= 0; i-- {
upf_ctrl := slice.Upfs[i]
for i := len(path) - 2; i >= 0; i-- {
upf_ctrl := path[i]
upf_any, ok := smf.upfs.Load(upf_ctrl)
if !ok {
return nil, ErrUpfNotFound
Expand Down Expand Up @@ -333,25 +356,37 @@ func (smf *Smf) StoreNextDownlinkFteid(ueCtrl jsonapi.ControlURI, ueAddr netip.A
return slice.(*Slice).sessions.SetNextDownlinkFteid(ueCtrl, ueAddr, fteid)
}

func (smf *Smf) UpdateSessionDownlink(ueCtrl jsonapi.ControlURI, ueAddr netip.Addr, dnn string) error {
return smf.UpdateSessionDownlinkContext(smf.ctx, ueCtrl, ueAddr, dnn)
func (smf *Smf) UpdateSessionDownlink(ueCtrl jsonapi.ControlURI, ueAddr netip.Addr, dnn string, oldGnbCtrl jsonapi.ControlURI) error {
return smf.UpdateSessionDownlinkContext(smf.ctx, ueCtrl, ueAddr, dnn, oldGnbCtrl)
}

// Updates Session to NextDownlinkFteid
func (smf *Smf) UpdateSessionDownlinkContext(ctx context.Context, ueCtrl jsonapi.ControlURI, ueAddr netip.Addr, dnn string) error {
slice, ok := smf.slices.Load(dnn)
func (smf *Smf) UpdateSessionDownlinkContext(ctx context.Context, ueCtrl jsonapi.ControlURI, ueAddr netip.Addr, dnn string, oldGnbCtrl jsonapi.ControlURI) error {
s, ok := smf.slices.Load(dnn)
if !ok {
return ErrDnnNotFound
}
session, err := slice.(*Slice).sessions.Get(ueCtrl, ueAddr)
slice := s.(*Slice)

session, err := slice.sessions.Get(ueCtrl, ueAddr)
if err != nil {
return err
}

if len(slice.(*Slice).Upfs) == 0 {
area, ok := smf.areas.Area(oldGnbCtrl)
if !ok {
return ErrAreaNotFound
}

path, ok := slice.Paths[area]
if !ok {
return ErrPathNotFound
}

if len(path) == 0 {
return ErrUpfNotFound
}
upf_ctrl := slice.(*Slice).Upfs[0] // upf-i
upf_ctrl := path[0] // upf-i
upf_any, ok := smf.upfs.Load(upf_ctrl)
if !ok {
return ErrUpfNotFound
Expand Down

0 comments on commit 7f3a343

Please sign in to comment.