Skip to content

Commit

Permalink
(wip) Handover; close #12; close #13; close #14
Browse files Browse the repository at this point in the history
  • Loading branch information
louisroyer committed Dec 23, 2024
1 parent 08cd8b5 commit 2cec65a
Show file tree
Hide file tree
Showing 13 changed files with 306 additions and 50 deletions.
8 changes: 4 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ require (
github.com/adrg/xdg v0.5.3
github.com/gin-gonic/gin v1.10.0
github.com/nextmn/go-pfcp-networking v0.0.41
github.com/nextmn/json-api v0.0.14
github.com/nextmn/json-api v0.0.15-0.20241223194957-c7cdfaa6388e
github.com/nextmn/logrus-formatter v0.0.1
github.com/sirupsen/logrus v1.9.3
github.com/urfave/cli/v2 v2.27.5
Expand All @@ -15,7 +15,7 @@ require (
)

require (
github.com/bytedance/sonic v1.12.5 // indirect
github.com/bytedance/sonic v1.12.6 // indirect
github.com/bytedance/sonic/loader v0.2.1 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect
Expand All @@ -39,8 +39,8 @@ require (
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
golang.org/x/arch v0.12.0 // indirect
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/net v0.32.0 // indirect
golang.org/x/net v0.33.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
google.golang.org/protobuf v1.35.2 // indirect
google.golang.org/protobuf v1.36.0 // indirect
)
32 changes: 24 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78=
github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ=
github.com/bytedance/sonic v1.12.5 h1:hoZxY8uW+mT+OpkcUWw4k0fDINtOcVavEsGfzwzFU/w=
github.com/bytedance/sonic v1.12.5/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
github.com/bytedance/sonic v1.12.6 h1:/isNmCUF2x3Sh8RAp/4mh4ZGkcFAX/hLrzrK3AvpRzk=
github.com/bytedance/sonic v1.12.6/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/bytedance/sonic/loader v0.2.1 h1:1GgorWTqf12TA8mma4DDSbaQigE2wOgQo7iCjjJv3+E=
github.com/bytedance/sonic/loader v0.2.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
Expand Down Expand Up @@ -50,8 +50,24 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/nextmn/go-pfcp-networking v0.0.41 h1:vA8TfjlxZMxZIZiJ3eEdllwYjmBurvZvD9J4xcCPheo=
github.com/nextmn/go-pfcp-networking v0.0.41/go.mod h1:KYoKLiltDmHL2YMU5mz2k/E1xMoz4TpmzTz6Nr5u5gA=
github.com/nextmn/json-api v0.0.14 h1:m4uHOVcXsxkXoxbrhqemLTRG4T86eYkejjirew1nDUU=
github.com/nextmn/json-api v0.0.14/go.mod h1:CQXeNPj9MDGsEExtnqJFIGjLgZAKsmOoO2fy+mep7Ak=
github.com/nextmn/json-api v0.0.15-0.20241218142156-a64418a36b7d h1:lhybNMDI+qjJB+rKDgJiHrXuhkoR7FWhQ4nEfpqZy1g=
github.com/nextmn/json-api v0.0.15-0.20241218142156-a64418a36b7d/go.mod h1:CQXeNPj9MDGsEExtnqJFIGjLgZAKsmOoO2fy+mep7Ak=
github.com/nextmn/json-api v0.0.15-0.20241223150142-2189fb1dc3af h1:/YGOPznGEQ0x/2E1Yk9HZ/3FosWtK5FouI0NDSiN2sE=
github.com/nextmn/json-api v0.0.15-0.20241223150142-2189fb1dc3af/go.mod h1:CQXeNPj9MDGsEExtnqJFIGjLgZAKsmOoO2fy+mep7Ak=
github.com/nextmn/json-api v0.0.15-0.20241223151312-9ece63d06e84 h1:eMjBBW9K21ffZ0CmbY7nKZd8d7O41DMB10t2HOTkUHw=
github.com/nextmn/json-api v0.0.15-0.20241223151312-9ece63d06e84/go.mod h1:CQXeNPj9MDGsEExtnqJFIGjLgZAKsmOoO2fy+mep7Ak=
github.com/nextmn/json-api v0.0.15-0.20241223184721-e18ca5cd5f80 h1:bCHBRPROQR9S31nyqTSMuC3GQ0sENbv0976+v28IdnY=
github.com/nextmn/json-api v0.0.15-0.20241223184721-e18ca5cd5f80/go.mod h1:CQXeNPj9MDGsEExtnqJFIGjLgZAKsmOoO2fy+mep7Ak=
github.com/nextmn/json-api v0.0.15-0.20241223190836-9214d6edc562 h1:J7A9dVo41ZUc2Df+Ri58I5fwNdmlPmy0/WfNHi0nmjE=
github.com/nextmn/json-api v0.0.15-0.20241223190836-9214d6edc562/go.mod h1:CQXeNPj9MDGsEExtnqJFIGjLgZAKsmOoO2fy+mep7Ak=
github.com/nextmn/json-api v0.0.15-0.20241223192051-3fc04f155386 h1:RP6Vc+ITbrH6JhBKLGn41eIcttP46gI2g6Inu65BvRY=
github.com/nextmn/json-api v0.0.15-0.20241223192051-3fc04f155386/go.mod h1:CQXeNPj9MDGsEExtnqJFIGjLgZAKsmOoO2fy+mep7Ak=
github.com/nextmn/json-api v0.0.15-0.20241223192440-30b4537ace9e h1:yDqGoYWiSbG5Ca869nEQN7i9OY77JowwGruBmn2fn+w=
github.com/nextmn/json-api v0.0.15-0.20241223192440-30b4537ace9e/go.mod h1:CQXeNPj9MDGsEExtnqJFIGjLgZAKsmOoO2fy+mep7Ak=
github.com/nextmn/json-api v0.0.15-0.20241223194733-8c8aa22fc73e h1:eGNJ2MhENNLkUfE/Z6a/mcq9sy5ypTTZD0RESuPm+30=
github.com/nextmn/json-api v0.0.15-0.20241223194733-8c8aa22fc73e/go.mod h1:CQXeNPj9MDGsEExtnqJFIGjLgZAKsmOoO2fy+mep7Ak=
github.com/nextmn/json-api v0.0.15-0.20241223194957-c7cdfaa6388e h1:Uobts9GoyC1wBbZnH0nyMPuQTQHgKI1MSTc2sR4AbkQ=
github.com/nextmn/json-api v0.0.15-0.20241223194957-c7cdfaa6388e/go.mod h1:CQXeNPj9MDGsEExtnqJFIGjLgZAKsmOoO2fy+mep7Ak=
github.com/nextmn/logrus-formatter v0.0.1 h1:Bsf78jjiEESc+rV8xE6IyKj4frDPGMwXFNrLQzm6A1E=
github.com/nextmn/logrus-formatter v0.0.1/go.mod h1:vdSZ+sIcSna8vjbXkSFxsnsKHqRwaUEed4JCPcXoGyM=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
Expand Down Expand Up @@ -86,16 +102,16 @@ golang.org/x/arch v0.12.0 h1:UsYJhbzPYGsT0HbEdmYcqtCv8UNGvnaL561NnIUvaKg=
golang.org/x/arch v0.12.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/protobuf v1.36.0 h1:mjIs9gYtt56AzC4ZaffQuh88TZurBGhIJMBZGSxNerQ=
google.golang.org/protobuf v1.36.0/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Expand Down
3 changes: 3 additions & 0 deletions internal/amf/amf.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ func NewAmf(bindAddr netip.AddrPort, control jsonapi.ControlURI, userAgent strin
// PDU Sessions
r.POST("/ps/establishment-request", amf.EstablishmentRequest)
r.POST("/ps/n2-establishment-response", amf.N2EstablishmentResponse)
r.POST("/ps/handover-requered", amf.HandoverRequired)
r.POST("/ps/handover-request-ack", amf.HandoverRequestAck)
r.POST("/ps/handover-notify", amf.HandoverNotify)

logrus.WithFields(logrus.Fields{"http-addr": bindAddr}).Info("HTTP Server created")
amf.srv = &http.Server{
Expand Down
3 changes: 1 addition & 2 deletions internal/amf/establishment_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,7 @@ func (amf *Amf) HandleEstablishmentRequest(ps n1n2.PduSessionEstabReqMsg) {
Header: ps,
Addr: pduSession.UeIpAddr,
},
Upf: pduSession.UplinkFteid.Addr,
UplinkTeid: pduSession.UplinkFteid.Teid,
UplinkFteid: *pduSession.UplinkFteid,
}
reqBody, err := json.Marshal(n2PsReq)
if err != nil {
Expand Down
39 changes: 39 additions & 0 deletions internal/amf/handover_notify.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright 2024 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 amf

import (
"net/http"

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

"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)

func (amf *Amf) HandoverNotify(c *gin.Context) {
var m n1n2.HandoverNotify
if err := c.BindJSON(&m); err != nil {
logrus.WithError(err).Error("could not deserialize")
c.JSON(http.StatusBadRequest, jsonapi.MessageWithError{Message: "could not deserialize", Error: err})
return
}
logrus.WithFields(logrus.Fields{
"ue": m.UeCtrl.String(),
"gnb-target": m.TargetGnb.String(),
}).Info("New Handover Confirm")
go amf.HandleHandoverNotify(m)
c.JSON(http.StatusAccepted, jsonapi.Message{Message: "please refer to logs for more information"})
}

func (amf *Amf) HandleHandoverNotify(m n1n2.HandoverNotify) {
// TODO:
// 1. update upf-i with pdu-session-n3.NextDownlinkFteid
// 2. update pdu-session-n3 with Downlink NextDownlinkFteid, and remove the NextDownlinkFteid field

logrus.Error("Handover Notify: Not implemented")
}
83 changes: 83 additions & 0 deletions internal/amf/handover_request_ack.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Copyright 2024 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 amf

import (
"bytes"
"encoding/json"
"net/http"

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

"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)

func (amf *Amf) HandoverRequestAck(c *gin.Context) {
var m n1n2.HandoverRequestAck
if err := c.BindJSON(&m); err != nil {
logrus.WithError(err).Error("could not deserialize")
c.JSON(http.StatusBadRequest, jsonapi.MessageWithError{Message: "could not deserialize", Error: err})
return
}
logrus.WithFields(logrus.Fields{
"ue": m.UeCtrl.String(),
"gnb-source": m.SourcegNB.String(),
"gnb-target": m.TargetgNB.String(),
}).Info("New Handover Request Ack")
go amf.HandleHandoverRequestAck(m)
c.JSON(http.StatusAccepted, jsonapi.Message{Message: "please refer to logs for more information"})
}

func (amf *Amf) HandleHandoverRequestAck(m n1n2.HandoverRequestAck) {
// TODO: if UPF-i change, push new DL rules

// send handover command to source with forwarding rule to targetGNB
sessions := make([]n1n2.Session, len(m.Sessions))
for i, s := range m.Sessions {
dl, err := amf.smf.GetSessionDownlinkFteid(m.UeCtrl, s.Addr, s.Dnn)
if err != nil {
// TODO: notify of failure
continue
}
sessions[i] = n1n2.Session{
Addr: s.Addr,
Dnn: s.Dnn,
UplinkFteid: s.UplinkFteid,
DownlinkFteid: dl,
ForwardDownlinkFteid: s.DownlinkFteid,
}
if err := amf.smf.StoreNextDownlinkFteid(m.UeCtrl, s.Addr, s.Dnn, s.DownlinkFteid); err != nil {
// TODO: notify of failure
continue
}
}

resp := n1n2.HandoverCommand{
Cp: m.Cp,
TargetGnb: m.TargetgNB,
SourceGnb: m.SourcegNB,
UeCtrl: m.UeCtrl,
Sessions: sessions,
}
ctx := amf.Context()
reqBody, err := json.Marshal(resp)
if err != nil {
logrus.WithError(err).Error("Could not marshal n1n2.HandoverRequest")
return
}
req, err := http.NewRequestWithContext(ctx, http.MethodPost, m.SourcegNB.JoinPath("ps/handover-command").String(), bytes.NewBuffer(reqBody))
if err != nil {
logrus.WithError(err).Error("Could not create request for ps/handover-command")
return
}
req.Header.Set("User-Agent", amf.userAgent)
req.Header.Set("Content-Type", "application/json; charset=UTF-8")
if _, err := amf.client.Do(req); err != nil {
logrus.WithError(err).Error("Could not send ps/handover-command")
}
}
85 changes: 85 additions & 0 deletions internal/amf/handover_required.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright 2024 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 amf

import (
"bytes"
"encoding/json"
"net/http"

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

"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)

func (amf *Amf) HandoverRequired(c *gin.Context) {
var m n1n2.HandoverRequired
if err := c.BindJSON(&m); err != nil {
logrus.WithError(err).Error("could not deserialize")
c.JSON(http.StatusBadRequest, jsonapi.MessageWithError{Message: "could not deserialize", Error: err})
return
}
logrus.WithFields(logrus.Fields{
"ue": m.Ue.String(),
"gnb-source": m.SourcegNB.String(),
"gnb-target": m.TargetgNB.String(),
}).Info("New Handover Required")
go amf.HandleHandoverRequired(m)
c.JSON(http.StatusAccepted, jsonapi.Message{Message: "please refer to logs for more information"})
}

func (amf *Amf) HandleHandoverRequired(m n1n2.HandoverRequired) {
// TODO: if UPF-i change, push new UL rules

// send handover-request to target with UPF-i FTEID
ctx := amf.Context()

sessions := make([]n1n2.Session, len(m.Sessions))
for i, s := range m.Sessions {
uplinkfteid, err := amf.smf.GetSessionUplinkFteid(m.Ue, s.Addr, s.Dnn)
if err != nil {
// TODO: notify gnb of failure
logrus.WithError(err).WithFields(logrus.Fields{
"ue": s.Addr,
"dnn": s.Dnn,
}).Error("Could not find Uplink FTEID for handover")
continue
}
sessions[i] = n1n2.Session{
Addr: s.Addr,
Dnn: s.Dnn,
UplinkFteid: uplinkfteid,
}

}
// send PseAccept to UE
resp := n1n2.HandoverRequest{
// Header
UeCtrl: m.Ue,
Cp: m.Cp,
TargetgNB: m.TargetgNB,

// Handover Request
Sessions: sessions,
}
reqBody, err := json.Marshal(resp)
if err != nil {
logrus.WithError(err).Error("Could not marshal n1n2.HandoverRequest")
return
}
req, err := http.NewRequestWithContext(ctx, http.MethodPost, m.TargetgNB.JoinPath("ps/handover-request").String(), bytes.NewBuffer(reqBody))
if err != nil {
logrus.WithError(err).Error("Could not create request for ps/handover-request")
return
}
req.Header.Set("User-Agent", amf.userAgent)
req.Header.Set("Content-Type", "application/json; charset=UTF-8")
if _, err := amf.client.Do(req); err != nil {
logrus.WithError(err).Error("Could not send ps/handover-request")
}
}
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.Gnb, ps.DownlinkTeid)
pduSession, err := amf.smf.CreateSessionDownlinkContext(ctx, ps.UeInfo.Header.Ue, ps.UeInfo.Addr, ps.UeInfo.Header.Dnn, ps.DownlinkFteid.Addr, ps.DownlinkFteid.Teid)
if err != nil {
logrus.WithError(err).WithFields(logrus.Fields{
"ue-ip-addr": ps.UeInfo.Addr,
Expand Down
15 changes: 0 additions & 15 deletions internal/smf/fteid.go

This file was deleted.

9 changes: 6 additions & 3 deletions internal/smf/pdu-session-n3.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ package smf

import (
"net/netip"

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

type PduSessionN3 struct {
UeIpAddr netip.Addr
UplinkFteid *Fteid
DownlinkFteid *Fteid
UeIpAddr netip.Addr
UplinkFteid *jsonapi.Fteid
DownlinkFteid *jsonapi.Fteid
NextDownlinkFteid *jsonapi.Fteid // Handover
}
13 changes: 13 additions & 0 deletions internal/smf/sessions.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,16 @@ func (s *SessionsMap) Add(ueCtrl jsonapi.ControlURI, session *PduSessionN3) {
m.s = append(m.s, session)
}
}

func (s *SessionsMap) SetNextDownlinkFteid(ueCtrl jsonapi.ControlURI, ueAddr netip.Addr, fteid *jsonapi.Fteid) error {
s.Lock()
defer s.Unlock()
if sessions, ok := s.m[ueCtrl]; ok {
for _, session := range sessions.s {
if session.UeIpAddr == ueAddr {
session.NextDownlinkFteid = fteid
}
}
}
return ErrPDUSessionNotFound
}
Loading

0 comments on commit 2cec65a

Please sign in to comment.