From 78276f1325198d592c34f6e7c3dfbf876039fd1d Mon Sep 17 00:00:00 2001 From: Guillaume Belanger Date: Wed, 16 Oct 2024 15:50:03 -0400 Subject: [PATCH 01/13] feat: request upf to allocate f-teid Signed-off-by: Guillaume Belanger --- context/context.go | 8 -- context/datapath.go | 90 ++---------- context/upf.go | 59 +------- pfcp/adapter/adapter.go | 45 ------ pfcp/handler/handler.go | 90 ++++-------- pfcp/handler/handler_test.go | 17 --- .../ies/user_plane_ip_resource_information.go | 112 --------------- ...user_plane_ip_resource_information_test.go | 135 ------------------ 8 files changed, 38 insertions(+), 518 deletions(-) delete mode 100644 pfcp/ies/user_plane_ip_resource_information.go delete mode 100644 pfcp/ies/user_plane_ip_resource_information_test.go diff --git a/context/context.go b/context/context.go index 457782ea..0f422b8c 100644 --- a/context/context.go +++ b/context/context.go @@ -38,7 +38,6 @@ const ( var smfContext SMFContext type DrsmCtxts struct { - TeidPool drsm.DrsmInterface SeidPool drsm.DrsmInterface UeIpPool drsm.DrsmInterface } @@ -439,13 +438,6 @@ func (smfCtxt *SMFContext) InitDrsm() error { return err } - // for local FTEID - if drsmCtxt, err := drsm.InitDRSM("fteid", podId, db, opt); err == nil { - smfCtxt.DrsmCtxts.TeidPool = drsmCtxt - } else { - return err - } - // for IP-Addr // TODO, use UPF based allocation for now diff --git a/context/datapath.go b/context/datapath.go index 3167a165..31f9ccf6 100644 --- a/context/datapath.go +++ b/context/datapath.go @@ -12,7 +12,6 @@ import ( "strconv" "github.com/omec-project/openapi/models" - "github.com/omec-project/smf/factory" "github.com/omec-project/smf/logger" "github.com/omec-project/smf/qos" "github.com/omec-project/smf/util" @@ -154,22 +153,7 @@ func (node *DataPathNode) ActivateUpLinkTunnel(smContext *SMContext) error { return err } - var teid uint32 - var teidErr error - - if factory.SmfConfig.Configuration.EnableDbStore { - var tmp int32 - tmp, teidErr = smfContext.DrsmCtxts.TeidPool.AllocateInt32ID() - teid = uint32(tmp) - } else { - teid, teidErr = destUPF.GenerateTEID() - } - if teidErr != nil { - logger.CtxLog.Errorf("generate uplink TEID fail: %s", teidErr) - return teidErr - } else { - node.UpLinkTunnel.TEID = teid - } + node.UpLinkTunnel.TEID = smContext.Tunnel.FTEID return nil } @@ -213,24 +197,7 @@ func (node *DataPathNode) ActivateDownLinkTunnel(smContext *SMContext) error { return err } - // Generate TEID for Tunnel - var teid uint32 - var teidErr error - - if factory.SmfConfig.Configuration.EnableDbStore { - var tmp int32 - tmp, teidErr = smfContext.DrsmCtxts.TeidPool.AllocateInt32ID() - teid = uint32(tmp) - } else { - teid, teidErr = destUPF.GenerateTEID() - } - if teidErr != nil { - logger.CtxLog.Errorf("generate downlink TEID fail: %s", teidErr) - return teidErr - } else { - node.DownLinkTunnel.TEID = teid - } - + node.UpLinkTunnel.TEID = smContext.Tunnel.FTEID return nil } @@ -274,17 +241,6 @@ func (node *DataPathNode) DeactivateUpLinkTunnel(smContext *SMContext) { } } } - - teid := node.DownLinkTunnel.TEID - var err error - if factory.SmfConfig.Configuration.EnableDbStore { - err = smfContext.DrsmCtxts.TeidPool.ReleaseInt32ID(int32(teid)) - } else { - node.UPF.teidGenerator.FreeID(int64(teid)) - } - if err != nil { - logger.CtxLog.Errorln("deactivated UpLinkTunnel", err) - } node.DownLinkTunnel = >PTunnel{} } @@ -328,17 +284,6 @@ func (node *DataPathNode) DeactivateDownLinkTunnel(smContext *SMContext) { } } } - - teid := node.DownLinkTunnel.TEID - var err error - if factory.SmfConfig.Configuration.EnableDbStore { - err = smfContext.DrsmCtxts.TeidPool.ReleaseInt32ID(int32(teid)) - } else { - node.UPF.teidGenerator.FreeID(int64(teid)) - } - if err != nil { - logger.CtxLog.Errorln("deactivated DownLinkTunnel", err) - } node.DownLinkTunnel = >PTunnel{} } @@ -534,7 +479,6 @@ func (dpNode *DataPathNode) ActivateUpLinkPdr(smContext *SMContext, defQER *QER, curULTunnel := dpNode.UpLinkTunnel for name, ULPDR := range curULTunnel.PDR { - ULDestUPF := curULTunnel.DestEndPoint.UPF ULPDR.QER = append(ULPDR.QER, defQER) // Set Default precedence @@ -542,29 +486,13 @@ func (dpNode *DataPathNode) ActivateUpLinkPdr(smContext *SMContext, defQER *QER, ULPDR.Precedence = defPrecedence } - var iface *UPFInterfaceInfo - if dpNode.IsANUPF() { - iface = ULDestUPF.GetInterface(models.UpInterfaceType_N3, smContext.Dnn) - } else { - iface = ULDestUPF.GetInterface(models.UpInterfaceType_N9, smContext.Dnn) + ULPDR.PDI.SourceInterface = SourceInterface{InterfaceValue: SourceInterfaceAccess} + ULPDR.PDI.LocalFTeid = &FTEID{ + Ch: true, + Teid: curULTunnel.TEID, } - - if upIP, err := iface.IP(smContext.SelectedPDUSessionType); err != nil { - logger.CtxLog.Errorf("activate UpLink PDR[%v] failed %v", name, err) - return err - } else { - ULPDR.PDI.SourceInterface = SourceInterface{InterfaceValue: SourceInterfaceAccess} - ULPDR.PDI.LocalFTeid = &FTEID{ - V4: true, - Ipv4Address: upIP, - Teid: curULTunnel.TEID, - } - - ULPDR.PDI.UEIPAddress = &ueIpAddr - - ULPDR.PDI.NetworkInstance = util_3gpp.Dnn(smContext.Dnn) - } - + ULPDR.PDI.UEIPAddress = &ueIpAddr + ULPDR.PDI.NetworkInstance = util_3gpp.Dnn(smContext.Dnn) ULPDR.OuterHeaderRemoval = &OuterHeaderRemoval{ OuterHeaderRemovalDescription: OuterHeaderRemovalGtpUUdpIpv4, } @@ -591,7 +519,7 @@ func (dpNode *DataPathNode) ActivateUpLinkPdr(smContext *SMContext, defQER *QER, if nextULDest := dpNode.Next(); nextULDest != nil { nextULTunnel := nextULDest.UpLinkTunnel - iface = nextULTunnel.DestEndPoint.UPF.GetInterface(models.UpInterfaceType_N9, smContext.Dnn) + iface := nextULTunnel.DestEndPoint.UPF.GetInterface(models.UpInterfaceType_N9, smContext.Dnn) if upIP, err := iface.IP(smContext.SelectedPDUSessionType); err != nil { logger.CtxLog.Errorf("activate UpLink PDR[%v] failed %v", name, err) diff --git a/context/upf.go b/context/upf.go index f7951941..3a49259c 100644 --- a/context/upf.go +++ b/context/upf.go @@ -11,7 +11,6 @@ import ( "fmt" "math" "net" - "os" "reflect" "strconv" "sync" @@ -23,7 +22,6 @@ import ( "github.com/omec-project/smf/factory" "github.com/omec-project/smf/logger" "github.com/omec-project/util/idgenerator" - "github.com/omec-project/util/util_3gpp" ) var upfPool sync.Map @@ -31,6 +29,7 @@ var upfPool sync.Map type UPTunnel struct { PathIDGenerator *idgenerator.IDGenerator DataPathPool DataPathPool + FTEID uint32 ANInformation struct { IPAddress net.IP TEID uint32 @@ -62,19 +61,6 @@ type RecoveryTimeStamp struct { RecoveryTimeStamp time.Time } -type UserPlaneIPResourceInformation struct { - Ipv4Address net.IP - Ipv6Address net.IP - NetworkInstance util_3gpp.Dnn - Assosi bool - Assoni bool - V6 bool - V4 bool - TeidRange uint8 - Teidri uint8 // 0x00011100 - SourceInterface uint8 // 0x00001111 -} - type UPF struct { SNssaiInfos []SnssaiUPFInfo N3Interfaces []UPFInterfaceInfo @@ -91,11 +77,9 @@ type UPF struct { barIDGenerator *idgenerator.IDGenerator urrIDGenerator *idgenerator.IDGenerator qerIDGenerator *idgenerator.IDGenerator - teidGenerator *idgenerator.IDGenerator RecoveryTimeStamp RecoveryTimeStamp NodeID NodeID - UPIPInfo UserPlaneIPResourceInformation UPFStatus UPFStatus uuid uuid.UUID Port uint16 @@ -241,7 +225,6 @@ func NewUPF(nodeID *NodeID, ifaces []factory.InterfaceUpfInfoItem) (upf *UPF) { upf.barIDGenerator = idgenerator.NewGenerator(1, math.MaxUint8) upf.qerIDGenerator = idgenerator.NewGenerator(1, math.MaxUint32) upf.urrIDGenerator = idgenerator.NewGenerator(1, math.MaxUint32) - upf.teidGenerator = idgenerator.NewGenerator(1, math.MaxUint32) upf.N3Interfaces = make([]UPFInterfaceInfo, 0) upf.N9Interfaces = make([]UPFInterfaceInfo, 0) @@ -280,33 +263,6 @@ func (upf *UPF) GetInterface(interfaceType models.UpInterfaceType, dnn string) * return nil } -func (upf *UPF) GenerateTEID() (uint32, error) { - if upf.UPFStatus != AssociatedSetUpSuccess { - err := fmt.Errorf("this upf not associate with smf") - return 0, err - } - - var id uint32 - if tmpID, err := upf.teidGenerator.Allocate(); err != nil { - return 0, err - } else { - id = uint32(tmpID) - } - - // Assuming one SMF host 5000 UEs, this code gets offset = smfCount * 5000 and generate unique TEID - - smfCountStr := os.Getenv("SMF_COUNT") - smfCount, err := strconv.Atoi(smfCountStr) - if err != nil { - logger.CtxLog.Errorf("failed to convert SMF_COUNT to int: %v", err) - } - - offset := (smfCount - 1) * 5000 - uniqueId := id + uint32(offset) - // return id, nil - return uniqueId, nil -} - func (upf *UPF) PFCPAddr() *net.UDPAddr { return &net.UDPAddr{ IP: upf.NodeID.ResolveNodeIdToIp(), @@ -366,19 +322,6 @@ func RemoveUPFNodeByNodeID(nodeID NodeID) bool { return false } -func SelectUPFByDnn(Dnn string) *UPF { - var upf *UPF - upfPool.Range(func(key, value interface{}) bool { - upf = value.(*UPF) - if upf.UPIPInfo.Assoni && string(upf.UPIPInfo.NetworkInstance) == Dnn { - return false - } - upf = nil - return true - }) - return upf -} - func (upf *UPF) GetUPFIP() string { upfIP := upf.NodeID.ResolveNodeIdToIp().String() return upfIP diff --git a/pfcp/adapter/adapter.go b/pfcp/adapter/adapter.go index 3222dce9..050c31df 100644 --- a/pfcp/adapter/adapter.go +++ b/pfcp/adapter/adapter.go @@ -4,12 +4,10 @@ package adapter import ( - "net" "sync" "github.com/omec-project/smf/context" "github.com/omec-project/smf/logger" - "github.com/omec-project/smf/pfcp/ies" "github.com/omec-project/smf/pfcp/udp" "github.com/wmnsk/go-pfcp/ie" "github.com/wmnsk/go-pfcp/message" @@ -100,15 +98,6 @@ func HandlePfcpAssociationSetupResponse(msg *udp.Message) { return } - var userPlaneIPResourceInformation *context.UserPlaneIPResourceInformation - if len(rsp.UserPlaneIPResourceInformation) != 0 { - userPlaneIPResourceInformation, err = ies.UnmarshalUEIPInformationBinary(rsp.UserPlaneIPResourceInformation[0].Payload) - if err != nil { - logger.PfcpLog.Errorf("failed to get UserPlaneIPResourceInformation: %+v", err) - return - } - } - if causeValue == ie.CauseRequestAccepted { logger.PfcpLog.Infof("handle PFCP Association Setup Response with NodeID[%s]", nodeID.ResolveNodeIdToIp().String()) @@ -118,15 +107,6 @@ func HandlePfcpAssociationSetupResponse(msg *udp.Message) { return } - // validate if DNNs served by UPF matches with the one provided by UPF - if userPlaneIPResourceInformation != nil { - upfProvidedDnn := string(userPlaneIPResourceInformation.NetworkInstance) - if !upf.IsDnnConfigured(upfProvidedDnn) { - logger.PfcpLog.Errorf("handle PFCP Association Setup Response, DNN mismatch, [%v] is not configured", upfProvidedDnn) - return - } - } - upf.UPFStatus = context.AssociatedSetUpSuccess if rsp.RecoveryTimeStamp == nil { logger.PfcpLog.Errorln("pfcp association setup response has no RecoveryTimeStamp") @@ -141,31 +121,6 @@ func HandlePfcpAssociationSetupResponse(msg *udp.Message) { RecoveryTimeStamp: recoveryTimestamp, } upf.NHeartBeat = 0 // reset Heartbeat attempt to 0 - - if rsp.UserPlaneIPResourceInformation != nil { - upf.UPIPInfo = *userPlaneIPResourceInformation - - if upf.UPIPInfo.Assosi && upf.UPIPInfo.Assoni && upf.UPIPInfo.SourceInterface == ie.SrcInterfaceAccess && - upf.UPIPInfo.V4 && !upf.UPIPInfo.Ipv4Address.Equal(net.IPv4zero) { - logger.PfcpLog.Infof("UPF[%s] received N3 interface IP[%v], network instance[%v] and TEID[%v]", - upf.NodeID.ResolveNodeIdToIp().String(), upf.UPIPInfo.Ipv4Address, - string(upf.UPIPInfo.NetworkInstance), upf.UPIPInfo.TeidRange) - - // reset the N3 interface of UPF - upf.N3Interfaces = make([]context.UPFInterfaceInfo, 0) - - // Insert N3 interface info from UPF - n3Interface := context.UPFInterfaceInfo{} - n3Interface.NetworkInstance = string(upf.UPIPInfo.NetworkInstance) - n3Interface.IPv4EndPointAddresses = append(n3Interface.IPv4EndPointAddresses, upf.UPIPInfo.Ipv4Address) - upf.N3Interfaces = append(upf.N3Interfaces, n3Interface) - } - - logger.PfcpLog.Infof("UPF(%s)[%s] setup association", - upf.NodeID.ResolveNodeIdToIp().String(), upf.UPIPInfo.NetworkInstance) - } else { - logger.PfcpLog.Errorln("pfcp association setup response has no UserPlane IP Resource Information") - } } } diff --git a/pfcp/handler/handler.go b/pfcp/handler/handler.go index 486def8b..22e03be8 100644 --- a/pfcp/handler/handler.go +++ b/pfcp/handler/handler.go @@ -35,6 +35,16 @@ func FindUEIPAddress(createdPDRIEs []*ie.IE) net.IP { return nil } +func FindFTEID(createdPDRIEs []*ie.IE) (uint32, error) { + for _, createdPDRIE := range createdPDRIEs { + teid, err := createdPDRIE.FTEID() + if err == nil { + return teid.TEID, nil + } + } + return 0, fmt.Errorf("TEID not found") +} + func HandlePfcpHeartbeatRequest(msg *udp.Message) { _, ok := msg.PfcpMessage.(*message.HeartbeatRequest) if !ok { @@ -157,6 +167,12 @@ func HandlePfcpAssociationSetupRequest(msg *udp.Message) { nodeID := smf_context.NewNodeID(nodeIDStr) + recoveryTimestamp, err := req.RecoveryTimeStamp.RecoveryTimeStamp() + if err != nil { + logger.PfcpLog.Errorf("failed to parse RecoveryTimeStamp: %+v", err) + return + } + upf := smf_context.RetrieveUPFNodeByNodeID(*nodeID) if upf == nil { logger.PfcpLog.Errorf("can not find UPF[%s]", nodeIDStr) @@ -165,21 +181,6 @@ func HandlePfcpAssociationSetupRequest(msg *udp.Message) { upf.UpfLock.Lock() defer upf.UpfLock.Unlock() - var userPlaneIPResourceInformation *smf_context.UserPlaneIPResourceInformation - if len(req.UserPlaneIPResourceInformation) != 0 { - userPlaneIPResourceInformation, err = ies.UnmarshalUEIPInformationBinary(req.UserPlaneIPResourceInformation[0].Payload) - if err != nil { - logger.PfcpLog.Errorf("failed to get UserPlaneIPResourceInformation: %+v", err) - return - } - upf.UPIPInfo = *userPlaneIPResourceInformation - } - - recoveryTimestamp, err := req.RecoveryTimeStamp.RecoveryTimeStamp() - if err != nil { - logger.PfcpLog.Errorf("failed to parse RecoveryTimeStamp: %+v", err) - return - } upf.RecoveryTimeStamp = smf_context.RecoveryTimeStamp{ RecoveryTimeStamp: recoveryTimestamp, @@ -238,24 +239,6 @@ func HandlePfcpAssociationSetupResponse(msg *udp.Message) { return } - var userPlaneIPResourceInformation *smf_context.UserPlaneIPResourceInformation - if len(rsp.UserPlaneIPResourceInformation) != 0 { - userPlaneIPResourceInformation, err = ies.UnmarshalUEIPInformationBinary(rsp.UserPlaneIPResourceInformation[0].Payload) - if err != nil { - logger.PfcpLog.Errorf("failed to get UserPlaneIPResourceInformation: %+v", err) - return - } - } - - // validate if DNNs served by UPF matches with the one provided by UPF - if userPlaneIPResourceInformation != nil { - upfProvidedDnn := string(userPlaneIPResourceInformation.NetworkInstance) - if !upf.IsDnnConfigured(upfProvidedDnn) { - logger.PfcpLog.Errorf("handle PFCP Association Setup success Response, DNN mismatch, [%v] is not configured ", upfProvidedDnn) - return - } - } - upf.UpfLock.Lock() defer upf.UpfLock.Unlock() upf.UPFStatus = smf_context.AssociatedSetUpSuccess @@ -294,31 +277,6 @@ func HandlePfcpAssociationSetupResponse(msg *udp.Message) { logger.PfcpLog.Debugf("handle PFCP Association Setup success Response, received UPFunctionFeatures= %v ", UPFunctionFeatures) upf.UPFunctionFeatures = UPFunctionFeatures } - - if userPlaneIPResourceInformation != nil { - upf.UPIPInfo = *userPlaneIPResourceInformation - - if upf.UPIPInfo.Assosi && upf.UPIPInfo.Assoni && upf.UPIPInfo.SourceInterface == ie.SrcInterfaceAccess && - upf.UPIPInfo.V4 && !upf.UPIPInfo.Ipv4Address.Equal(net.IPv4zero) { - logger.PfcpLog.Infof("UPF[%s] received N3 interface IP[%v], network instance[%v] and TEID[%v]", - upf.NodeID.ResolveNodeIdToIp().String(), upf.UPIPInfo.Ipv4Address, - string(upf.UPIPInfo.NetworkInstance), upf.UPIPInfo.TeidRange) - - // reset the N3 interface of UPF - upf.N3Interfaces = make([]smf_context.UPFInterfaceInfo, 0) - - // Insert N3 interface info from UPF - n3Interface := smf_context.UPFInterfaceInfo{} - n3Interface.NetworkInstance = string(upf.UPIPInfo.NetworkInstance) - n3Interface.IPv4EndPointAddresses = append(n3Interface.IPv4EndPointAddresses, upf.UPIPInfo.Ipv4Address) - upf.N3Interfaces = append(upf.N3Interfaces, n3Interface) - } - - logger.PfcpLog.Infof("UPF(%s)[%s] setup association success", - upf.NodeID.ResolveNodeIdToIp().String(), upf.UPIPInfo.NetworkInstance) - } else { - logger.PfcpLog.Errorln("pfcp association setup response has no UserPlane IP Resource Information") - } } } @@ -457,8 +415,8 @@ func HandlePfcpSessionEstablishmentResponse(msg *udp.Message) { smContext.SubPfcpLog.Infof("in HandlePfcpSessionEstablishmentResponse rsp.UPFSEID.Seid [%v] ", rspUPFseid.SEID) } - // UE IP-Addr(only v4 supported) if rsp.CreatedPDR != nil { + // UE IP-Addr(only v4 supported) ueIPAddress := FindUEIPAddress(rsp.CreatedPDR) smContext.SubPfcpLog.Infof("upf provided ue ip address [%v]", ueIPAddress) @@ -471,12 +429,17 @@ func HandlePfcpSessionEstablishmentResponse(msg *udp.Message) { // Update with one received from UPF smContext.PDUAddress.Ip = ueIPAddress smContext.PDUAddress.UpfProvided = true + + // Store F-TEID created by UPF + teid, err := FindFTEID(rsp.CreatedPDR) + if err != nil { + logger.PfcpLog.Errorf("failed to parse TEID IE: %+v", err) + return + } + smContext.Tunnel.FTEID = teid } smContext.SMLock.Unlock() - // Get N3 interface UPF - ANUPF := smContext.Tunnel.DataPathPool.GetDefaultPath().FirstDPNode - if rsp.NodeID == nil { logger.PfcpLog.Errorln("PFCP Session Establishment Response missing NodeID") return @@ -488,6 +451,9 @@ func HandlePfcpSessionEstablishmentResponse(msg *udp.Message) { } rspNodeID := smf_context.NewNodeID(rspNodeIDStr) + // Get N3 interface UPF + ANUPF := smContext.Tunnel.DataPathPool.GetDefaultPath().FirstDPNode + if ANUPF.UPF.NodeID.ResolveNodeIdToIp().Equal(nodeID.ResolveNodeIdToIp()) { // UPF Accept if rsp.Cause == nil { diff --git a/pfcp/handler/handler_test.go b/pfcp/handler/handler_test.go index f12c37de..38b6a876 100644 --- a/pfcp/handler/handler_test.go +++ b/pfcp/handler/handler_test.go @@ -107,14 +107,6 @@ func TestHandlePfcpAssociationSetupResponse(t *testing.T) { 1, ie.NewCause(ie.CauseRequestAccepted), ie.NewNodeID("1.1.1.1", "", ""), - ie.NewUserPlaneIPResourceInformation( - uint8(0x61), - 0, - "1.2.3.4", - "", - ".internet", // Note the additional character here. This is because the SD-Core UPF sends the network instance with 1 leading character - ie.SrcInterfaceAccess, - ), ie.NewRecoveryTimeStamp(recoveryTimestamp), ) @@ -135,13 +127,4 @@ func TestHandlePfcpAssociationSetupResponse(t *testing.T) { if upf.RecoveryTimeStamp.RecoveryTimeStamp.Truncate(1*time.Second) != recoveryTimestamp.Truncate(1*time.Second) { t.Errorf("Expected RecoveryTimeStamp %v, got %v", recoveryTimestamp.Truncate(1*time.Second), upf.RecoveryTimeStamp.RecoveryTimeStamp.Truncate(1*time.Second)) } - if upf.UPIPInfo.Ipv4Address.String() != "1.2.3.4" { - t.Errorf("Expected IP address %v, got %v", "1.2.3.4", upf.UPIPInfo.Ipv4Address.String()) - } - if upf.UPIPInfo.SourceInterface != ie.SrcInterfaceAccess { - t.Errorf("Expected Source Interface Access, got %v", upf.UPIPInfo.SourceInterface) - } - if string(upf.UPIPInfo.NetworkInstance) != "internet" { - t.Errorf("Expected Network Instance %v, got %v", "internet", upf.UPIPInfo.NetworkInstance) - } } diff --git a/pfcp/ies/user_plane_ip_resource_information.go b/pfcp/ies/user_plane_ip_resource_information.go deleted file mode 100644 index bef8f1ff..00000000 --- a/pfcp/ies/user_plane_ip_resource_information.go +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2024 Canonical Ltd. -// -// SPDX-License-Identifier: Apache-2.0 -// -// The User Plane IP Resource Information has been deprecated in 3GPP Release 16. -// This file should be removed from the project as we get rid of the User Plane IP Resource Information -// IE in the PFCP Association Setup Response Message - -package ies - -import ( - "fmt" - "net" - - "github.com/omec-project/smf/context" -) - -const ( - Mask8 = 1<<8 - 1 - Mask7 = 1<<7 - 1 - Mask6 = 1<<6 - 1 - Mask5 = 1<<5 - 1 - Mask4 = 1<<4 - 1 - Mask3 = 1<<3 - 1 - Mask2 = 1<<2 - 1 - Mask1 = 1<<1 - 1 -) - -const ( - BitMask8 = 1 << 7 - BitMask7 = 1 << 6 - BitMask6 = 1 << 5 - BitMask5 = 1 << 4 - BitMask4 = 1 << 3 - BitMask3 = 1 << 2 - BitMask2 = 1 << 1 - BitMask1 = 1 -) - -func utob(u uint8) bool { - return u != 0 -} - -func UnmarshalUEIPInformationBinary(data []byte) (*context.UserPlaneIPResourceInformation, error) { - u := &context.UserPlaneIPResourceInformation{} - length := uint16(len(data)) - - var idx uint16 = 0 - // Octet 5 - if length < idx+1 { - return nil, fmt.Errorf("inadequate TLV length: %d", length) - } - u.Assosi = utob(data[idx] & BitMask7) - u.Assoni = utob(data[idx] & BitMask6) - u.Teidri = data[idx] >> 2 & Mask3 - u.V6 = utob(data[idx] & BitMask2) - u.V4 = utob(data[idx] & BitMask1) - idx = idx + 1 - - // Octet 6 - if u.Teidri != 0 { - if length < idx+1 { - return nil, fmt.Errorf("inadequate TLV length: %d", length) - } - u.TeidRange = data[idx] - idx = idx + 1 - } - - // Octet m to (m+3) - if u.V4 { - if length < idx+net.IPv4len { - return nil, fmt.Errorf("inadequate TLV length: %d", length) - } - u.Ipv4Address = data[idx : idx+net.IPv4len] - idx = idx + net.IPv4len - } - - // Octet p to (p+15) - if u.V6 { - if length < idx+net.IPv6len { - return nil, fmt.Errorf("inadequate TLV length: %d", length) - } - u.Ipv6Address = data[idx : idx+net.IPv6len] - idx = idx + net.IPv6len - } - - if !u.V4 && !u.V6 { - return nil, fmt.Errorf("none of V4 and V6 flags is set") - } - - // Octet r - if u.Assosi { - if length < idx+1 { - return nil, fmt.Errorf("inadequate TLV length: %d", length) - } - u.SourceInterface = data[length-1] & Mask4 - data = data[:length-1] - } - - // Octet k to l - if u.Assoni { - if length < idx+1 { - return nil, fmt.Errorf("inadequate TLV length: %d", length) - } - err := u.NetworkInstance.UnmarshalBinary(data[idx:]) - if err != nil { - return nil, err - } - } - - return u, nil -} diff --git a/pfcp/ies/user_plane_ip_resource_information_test.go b/pfcp/ies/user_plane_ip_resource_information_test.go deleted file mode 100644 index 6e4f43ca..00000000 --- a/pfcp/ies/user_plane_ip_resource_information_test.go +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright 2024 Canonical Ltd. -// -// SPDX-License-Identifier: Apache-2.0 - -package ies_test - -import ( - "testing" - - "github.com/omec-project/smf/pfcp/ies" - "github.com/wmnsk/go-pfcp/ie" -) - -type Flag uint8 - -// setBit sets the bit at the given position to the specified value (true or false) -// Positions go from 1 to 8 -func (f *Flag) setBit(position uint8) { - if position < 1 || position > 8 { - return - } - *f |= 1 << (position - 1) -} - -func CreateFlags(v4 bool, v6 bool, ni bool, si bool) uint8 { - flags := new(Flag) - if v4 { - flags.setBit(1) - } - if v6 { - flags.setBit(2) - } - - if ni { - flags.setBit(6) - } - - if si { - flags.setBit(7) - } - - return uint8(*flags) -} - -func TestUnmarshalUEIPInformationBinaryOnlySourceInterface(t *testing.T) { - const ipv4Address = "1.2.3.4" - flags := CreateFlags(true, false, false, true) - - userplaneIE := ie.NewUserPlaneIPResourceInformation(flags, 0, ipv4Address, "", "", ie.SrcInterfaceAccess) - - ueIpInfo, err := ies.UnmarshalUEIPInformationBinary(userplaneIE.Payload) - if err != nil { - t.Errorf("error unmarshalling UE IP Information: %v", err) - } - - if ueIpInfo.TeidRange != 0 { - t.Errorf("expected TEIDRange 0, got %d", ueIpInfo.TeidRange) - } - - if ueIpInfo.Ipv4Address.String() != ipv4Address { - t.Errorf("expected IP address %v got %s", ipv4Address, ueIpInfo.Ipv4Address.String()) - } - - if ueIpInfo.Ipv6Address != nil { - t.Errorf("expected nil IPv6 address, got %s", ueIpInfo.Ipv6Address.String()) - } - - if ueIpInfo.SourceInterface != ie.SrcInterfaceAccess { - t.Errorf("expected Source Interface Access, got %d", ueIpInfo.SourceInterface) - } - - if string(ueIpInfo.NetworkInstance) != "" { - t.Errorf("expected empty network instance, got %s", ueIpInfo.NetworkInstance) - } -} - -func TestUnmarshalUEIPInformationBinaryOnlyNetworkInstance(t *testing.T) { - flags := CreateFlags(true, false, true, false) - userplaneIE := ie.NewUserPlaneIPResourceInformation(flags, 0, "1.2.3.4", "", string(ie.NewNetworkInstanceFQDN("internet").Payload), 0) - - ueIpInfo, err := ies.UnmarshalUEIPInformationBinary(userplaneIE.Payload) - if err != nil { - t.Errorf("error unmarshalling UE IP Information: %v", err) - } - - if ueIpInfo.TeidRange != 0 { - t.Errorf("expected TEIDRange 0, got %d", ueIpInfo.TeidRange) - } - - if ueIpInfo.Ipv4Address.String() != "1.2.3.4" { - t.Errorf("expected IP address 1.2.3.4 got %s", ueIpInfo.Ipv4Address.String()) - } - - if ueIpInfo.Ipv6Address != nil { - t.Errorf("expected nil IPv6 address, got %s", ueIpInfo.Ipv6Address.String()) - } - - if ueIpInfo.SourceInterface != 0 { - t.Errorf("expected Source Interface 0, got %d", ueIpInfo.SourceInterface) - } - - if string(ueIpInfo.NetworkInstance) != "internet" { - t.Errorf("expected network instance internet, got %s", ueIpInfo.NetworkInstance) - } -} - -func TestUnmarshalUEIPInformationBinary(t *testing.T) { - flags := CreateFlags(true, false, true, true) - userplaneIE := ie.NewUserPlaneIPResourceInformation(flags, 0, "1.2.3.4", "", string(ie.NewNetworkInstanceFQDN("internet").Payload), ie.SrcInterfaceAccess) - - ueIpInfo, err := ies.UnmarshalUEIPInformationBinary(userplaneIE.Payload) - if err != nil { - t.Errorf("error unmarshalling UE IP Information: %v", err) - } - - if ueIpInfo.TeidRange != 0 { - t.Errorf("expected TEIDRange 0, got %d", ueIpInfo.TeidRange) - } - - if ueIpInfo.Ipv4Address.String() != "1.2.3.4" { - t.Errorf("expected IP address 1.2.3.4 got %s", ueIpInfo.Ipv4Address.String()) - } - - if ueIpInfo.Ipv6Address != nil { - t.Errorf("expected nil IPv6 address, got %s", ueIpInfo.Ipv6Address.String()) - } - - if ueIpInfo.SourceInterface != ie.SrcInterfaceAccess { - t.Errorf("expected Source Interface Access, got %d", ueIpInfo.SourceInterface) - } - - if string(ueIpInfo.NetworkInstance) != "internet" { - t.Errorf("expected network instance internet, got %s", ueIpInfo.NetworkInstance) - } -} From c93386024588a7b2807378be88e5a1c1d17beb22 Mon Sep 17 00:00:00 2001 From: Guillaume Belanger Date: Thu, 17 Oct 2024 18:28:52 -0400 Subject: [PATCH 02/13] fix: also implement for downlink pdr Signed-off-by: Guillaume Belanger --- context/datapath.go | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/context/datapath.go b/context/datapath.go index 31f9ccf6..2c248f2c 100644 --- a/context/datapath.go +++ b/context/datapath.go @@ -552,7 +552,6 @@ func (dpNode *DataPathNode) ActivateDlLinkPdr(smContext *SMContext, defQER *QER, for name, DLPDR := range curDLTunnel.PDR { logger.CtxLog.Infof("activate Downlink PDR[%v]:[%v]", name, DLPDR) - DLDestUPF := curDLTunnel.DestEndPoint.UPF DLPDR.QER = append(DLPDR.QER, defQER) if DLPDR.Precedence == 0 { @@ -566,20 +565,13 @@ func (dpNode *DataPathNode) ActivateDlLinkPdr(smContext *SMContext, defQER *QER, OuterHeaderRemovalDescription: OuterHeaderRemovalGtpUUdpIpv4, } - iface = DLDestUPF.GetInterface(models.UpInterfaceType_N9, smContext.Dnn) - if upIP, err := iface.IP(smContext.SelectedPDUSessionType); err != nil { - logger.CtxLog.Errorf("activate Downlink PDR[%v] failed %v", name, err) - return err - } else { - DLPDR.PDI.SourceInterface = SourceInterface{InterfaceValue: SourceInterfaceCore} - DLPDR.PDI.LocalFTeid = &FTEID{ - V4: true, - Ipv4Address: upIP, - Teid: curDLTunnel.TEID, - } - - DLPDR.PDI.UEIPAddress = &ueIpAddr + DLPDR.PDI.SourceInterface = SourceInterface{InterfaceValue: SourceInterfaceCore} + DLPDR.PDI.LocalFTeid = &FTEID{ + Ch: true, + Teid: curDLTunnel.TEID, } + + DLPDR.PDI.UEIPAddress = &ueIpAddr } DLFAR := DLPDR.FAR From 3379f4ce3bbed837e45bda7a598ddf80f13ea0a7 Mon Sep 17 00:00:00 2001 From: Guillaume Belanger Date: Fri, 18 Oct 2024 12:50:38 -0400 Subject: [PATCH 03/13] fix: set fteid with downlink tunnel Signed-off-by: Guillaume Belanger --- context/datapath.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context/datapath.go b/context/datapath.go index 2c248f2c..abc2d915 100644 --- a/context/datapath.go +++ b/context/datapath.go @@ -197,7 +197,7 @@ func (node *DataPathNode) ActivateDownLinkTunnel(smContext *SMContext) error { return err } - node.UpLinkTunnel.TEID = smContext.Tunnel.FTEID + node.DownLinkTunnel.TEID = smContext.Tunnel.FTEID return nil } From 11d07f730cd0b406a91055703b36cc1098477117 Mon Sep 17 00:00:00 2001 From: Guillaume Belanger Date: Fri, 18 Oct 2024 17:51:24 -0400 Subject: [PATCH 04/13] fix: write f-teid to proper variable Signed-off-by: Guillaume Belanger --- context/datapath.go | 22 ++++++-------------- context/upf.go | 1 - pfcp/handler/handler.go | 46 +++++++++++++++++++++++++---------------- 3 files changed, 34 insertions(+), 35 deletions(-) diff --git a/context/datapath.go b/context/datapath.go index abc2d915..5718c18c 100644 --- a/context/datapath.go +++ b/context/datapath.go @@ -153,8 +153,6 @@ func (node *DataPathNode) ActivateUpLinkTunnel(smContext *SMContext) error { return err } - node.UpLinkTunnel.TEID = smContext.Tunnel.FTEID - return nil } @@ -197,7 +195,6 @@ func (node *DataPathNode) ActivateDownLinkTunnel(smContext *SMContext) error { return err } - node.DownLinkTunnel.TEID = smContext.Tunnel.FTEID return nil } @@ -488,8 +485,7 @@ func (dpNode *DataPathNode) ActivateUpLinkPdr(smContext *SMContext, defQER *QER, ULPDR.PDI.SourceInterface = SourceInterface{InterfaceValue: SourceInterfaceAccess} ULPDR.PDI.LocalFTeid = &FTEID{ - Ch: true, - Teid: curULTunnel.TEID, + Ch: true, } ULPDR.PDI.UEIPAddress = &ueIpAddr ULPDR.PDI.NetworkInstance = util_3gpp.Dnn(smContext.Dnn) @@ -558,22 +554,16 @@ func (dpNode *DataPathNode) ActivateDlLinkPdr(smContext *SMContext, defQER *QER, DLPDR.Precedence = defPrecedence } - if dpNode.IsAnchorUPF() { - DLPDR.PDI.UEIPAddress = &ueIpAddr - } else { + if !dpNode.IsAnchorUPF() { DLPDR.OuterHeaderRemoval = &OuterHeaderRemoval{ OuterHeaderRemovalDescription: OuterHeaderRemovalGtpUUdpIpv4, } - - DLPDR.PDI.SourceInterface = SourceInterface{InterfaceValue: SourceInterfaceCore} - DLPDR.PDI.LocalFTeid = &FTEID{ - Ch: true, - Teid: curDLTunnel.TEID, - } - - DLPDR.PDI.UEIPAddress = &ueIpAddr } + DLPDR.PDI.SourceInterface = SourceInterface{InterfaceValue: SourceInterfaceCore} + DLPDR.PDI.UEIPAddress = &ueIpAddr + DLPDR.PDI.UEIPAddress = &ueIpAddr + DLFAR := DLPDR.FAR logger.PduSessLog.Debugln("current DP Node IP:", dpNode.UPF.NodeID.ResolveNodeIdToIp().String()) diff --git a/context/upf.go b/context/upf.go index 3a49259c..05ac3975 100644 --- a/context/upf.go +++ b/context/upf.go @@ -29,7 +29,6 @@ var upfPool sync.Map type UPTunnel struct { PathIDGenerator *idgenerator.IDGenerator DataPathPool DataPathPool - FTEID uint32 ANInformation struct { IPAddress net.IP TEID uint32 diff --git a/pfcp/handler/handler.go b/pfcp/handler/handler.go index 22e03be8..7fa73fe6 100644 --- a/pfcp/handler/handler.go +++ b/pfcp/handler/handler.go @@ -35,14 +35,14 @@ func FindUEIPAddress(createdPDRIEs []*ie.IE) net.IP { return nil } -func FindFTEID(createdPDRIEs []*ie.IE) (uint32, error) { +func FindFTEID(createdPDRIEs []*ie.IE) (*ie.FTEIDFields, error) { for _, createdPDRIE := range createdPDRIEs { teid, err := createdPDRIE.FTEID() if err == nil { - return teid.TEID, nil + return teid, nil } } - return 0, fmt.Errorf("TEID not found") + return nil, fmt.Errorf("FTEID not found in CreatedPDR") } func HandlePfcpHeartbeatRequest(msg *udp.Message) { @@ -415,28 +415,41 @@ func HandlePfcpSessionEstablishmentResponse(msg *udp.Message) { smContext.SubPfcpLog.Infof("in HandlePfcpSessionEstablishmentResponse rsp.UPFSEID.Seid [%v] ", rspUPFseid.SEID) } + // Get N3 interface UPF + ANUPF := smContext.Tunnel.DataPathPool.GetDefaultPath().FirstDPNode + if rsp.CreatedPDR != nil { - // UE IP-Addr(only v4 supported) ueIPAddress := FindUEIPAddress(rsp.CreatedPDR) - smContext.SubPfcpLog.Infof("upf provided ue ip address [%v]", ueIPAddress) + if ueIPAddress != nil { + smContext.SubPfcpLog.Infof("upf provided ue ip address [%v]", ueIPAddress) + // Release previous locally allocated UE IP-Addr + err := smContext.ReleaseUeIpAddr() + if err != nil { + logger.PfcpLog.Errorf("failed to release UE IP-Addr: %+v", err) + } - // Release previous locally allocated UE IP-Addr - err := smContext.ReleaseUeIpAddr() - if err != nil { - logger.PfcpLog.Errorf("failed to release UE IP-Addr: %+v", err) + // Update with one received from UPF + smContext.PDUAddress.Ip = ueIPAddress + smContext.PDUAddress.UpfProvided = true } - // Update with one received from UPF - smContext.PDUAddress.Ip = ueIPAddress - smContext.PDUAddress.UpfProvided = true - // Store F-TEID created by UPF - teid, err := FindFTEID(rsp.CreatedPDR) + fteid, err := FindFTEID(rsp.CreatedPDR) if err != nil { logger.PfcpLog.Errorf("failed to parse TEID IE: %+v", err) return } - smContext.Tunnel.FTEID = teid + logger.PfcpLog.Infof("created PDR FTEID: %+v", fteid) + ANUPF.UpLinkTunnel.TEID = fteid.TEID + upf := smf_context.RetrieveUPFNodeByNodeID(*nodeID) + if upf == nil { + logger.PfcpLog.Errorf("can't find UPF[%s]", nodeID.ResolveNodeIdToIp().String()) + return + } + upf.N3Interfaces = make([]smf_context.UPFInterfaceInfo, 0) + n3Interface := smf_context.UPFInterfaceInfo{} + n3Interface.IPv4EndPointAddresses = append(n3Interface.IPv4EndPointAddresses, fteid.IPv4Address) + upf.N3Interfaces = append(upf.N3Interfaces, n3Interface) } smContext.SMLock.Unlock() @@ -451,9 +464,6 @@ func HandlePfcpSessionEstablishmentResponse(msg *udp.Message) { } rspNodeID := smf_context.NewNodeID(rspNodeIDStr) - // Get N3 interface UPF - ANUPF := smContext.Tunnel.DataPathPool.GetDefaultPath().FirstDPNode - if ANUPF.UPF.NodeID.ResolveNodeIdToIp().Equal(nodeID.ResolveNodeIdToIp()) { // UPF Accept if rsp.Cause == nil { From f1d35096a1e9d78e5e381c85e6bcefc35938d355 Mon Sep 17 00:00:00 2001 From: Guillaume Belanger Date: Fri, 8 Nov 2024 09:54:38 -0500 Subject: [PATCH 05/13] chore: add unit tests Signed-off-by: Guillaume Belanger --- context/datapath_test.go | 122 +++++++++++++++++++++++++++++++++++ pfcp/handler/handler.go | 15 ++++- pfcp/handler/handler_test.go | 74 +++++++++++++++++++++ 3 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 context/datapath_test.go diff --git a/context/datapath_test.go b/context/datapath_test.go new file mode 100644 index 00000000..8188d0f6 --- /dev/null +++ b/context/datapath_test.go @@ -0,0 +1,122 @@ +package context_test + +import ( + "net" + "testing" + + "github.com/omec-project/smf/context" +) + +func TestActivateUpLinkPdr(t *testing.T) { + smContext := &context.SMContext{ + PDUAddress: &context.UeIpAddr{ + Ip: net.IPv4(192, 168, 1, 1), + }, + Dnn: "internet", + } + + defQER := &context.QER{} + + dpNode := &context.DataPathNode{ + UPF: &context.UPF{}, + UpLinkTunnel: &context.GTPTunnel{ + PDR: map[string]*context.PDR{ + "default": { + Precedence: 0, + FAR: &context.FAR{}, + }, + }, + }, + } + + err := dpNode.ActivateUpLinkPdr(smContext, defQER, 10) + if err != nil { + t.Errorf("expected no error, got %v", err) + } + + pdr := dpNode.UpLinkTunnel.PDR["default"] + if pdr == nil { + t.Fatalf("expected pdr to be not nil") + } + + if pdr.PDI.SourceInterface.InterfaceValue != context.SourceInterfaceAccess { + t.Errorf("expected SourceInterface to be %v, got %v", context.SourceInterfaceAccess, pdr.PDI.SourceInterface.InterfaceValue) + } + if pdr.PDI.LocalFTeid == nil { + t.Errorf("expected pdr.PDI.LocalFTeid to be not nil") + } + if !pdr.PDI.LocalFTeid.Ch { + t.Errorf("expected pdr.PDI.LocalFTeid.Ch to be true") + } + if pdr.PDI.UEIPAddress == nil { + t.Errorf("expected pdr.PDI.UEIPAddress to be not nil") + } + if !pdr.PDI.UEIPAddress.V4 { + t.Errorf("expected pdr.PDI.UEIPAddress.V4 to be true") + } + if !pdr.PDI.UEIPAddress.Ipv4Address.Equal(net.IP{192, 168, 1, 1}) { + t.Errorf("expected pdr.PDI.UEIPAddress.Ipv4Address to be %v, got %v", net.IP{192, 168, 1, 1}, pdr.PDI.UEIPAddress.Ipv4Address) + } + if string(pdr.PDI.NetworkInstance) != "internet" { + t.Errorf("expected pdr.PDI.NetworkInstance to be 'internet', got %v", string(pdr.PDI.NetworkInstance)) + } +} + +func TestActivateDlLinkPdr(t *testing.T) { + smContext := &context.SMContext{ + PDUAddress: &context.UeIpAddr{ + Ip: net.IP{192, 168, 1, 1}, + }, + Dnn: "internet", + Tunnel: &context.UPTunnel{ + ANInformation: struct { + IPAddress net.IP + TEID uint32 + }{ + IPAddress: net.IP{10, 0, 0, 1}, + TEID: 12345, + }, + }, + } + + defQER := &context.QER{} + + dpNode := &context.DataPathNode{ + UPF: &context.UPF{}, + DownLinkTunnel: &context.GTPTunnel{ + PDR: map[string]*context.PDR{ + "default": { + Precedence: 0, + FAR: &context.FAR{}, + }, + }, + }, + } + + dataPath := &context.DataPath{ + FirstDPNode: dpNode, + } + + err := dpNode.ActivateDlLinkPdr(smContext, defQER, 10, dataPath) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + pdr := dpNode.DownLinkTunnel.PDR["default"] + if pdr == nil { + t.Fatalf("expected pdr to be not nil") + } + + if pdr.PDI.SourceInterface.InterfaceValue != context.SourceInterfaceCore { + t.Errorf("expected SourceInterface to be %v, got %v", context.SourceInterfaceCore, pdr.PDI.SourceInterface.InterfaceValue) + } + if pdr.PDI.UEIPAddress == nil { + t.Errorf("expected pdr.PDI.UEIPAddress to be not nil") + } + if !pdr.PDI.UEIPAddress.V4 { + t.Errorf("expected pdr.PDI.UEIPAddress.V4 to be true") + } + if !pdr.PDI.UEIPAddress.Ipv4Address.Equal(net.IP{192, 168, 1, 1}) { + t.Errorf("expected pdr.PDI.UEIPAddress.Ipv4Address to be %v, got %v", net.IP{192, 168, 1, 1}, pdr.PDI.UEIPAddress.Ipv4Address) + } +} diff --git a/pfcp/handler/handler.go b/pfcp/handler/handler.go index 7fa73fe6..3248a978 100644 --- a/pfcp/handler/handler.go +++ b/pfcp/handler/handler.go @@ -381,7 +381,6 @@ func HandlePfcpSessionEstablishmentResponse(msg *udp.Message) { return } logger.PfcpLog.Infof("handle PFCP Session Establishment Response") - SEID := rsp.SEID() if SEID == 0 { if eventData, ok := msg.EventData.(udp.PfcpEventData); !ok { @@ -401,6 +400,10 @@ func HandlePfcpSessionEstablishmentResponse(msg *udp.Message) { // Get NodeId from Seq:NodeId Map seq := rsp.Sequence() nodeID := pfcp_message.FetchPfcpTxn(seq) + if nodeID == nil { + logger.PfcpLog.Errorf("no pending pfcp response for sequence no: %v", seq) + return + } if rsp.UPFSEID != nil { // NodeIDtoIP := rsp.NodeID.ResolveNodeIdToIp().String() @@ -416,6 +419,11 @@ func HandlePfcpSessionEstablishmentResponse(msg *udp.Message) { } // Get N3 interface UPF + defaultPath := smContext.Tunnel.DataPathPool.GetDefaultPath() + if defaultPath == nil { + logger.PfcpLog.Errorln("failed to get default path") + return + } ANUPF := smContext.Tunnel.DataPathPool.GetDefaultPath().FirstDPNode if rsp.CreatedPDR != nil { @@ -464,6 +472,11 @@ func HandlePfcpSessionEstablishmentResponse(msg *udp.Message) { } rspNodeID := smf_context.NewNodeID(rspNodeIDStr) + if ANUPF.UPF == nil { + logger.PfcpLog.Errorln("failed to get UPF from default path") + return + } + if ANUPF.UPF.NodeID.ResolveNodeIdToIp().Equal(nodeID.ResolveNodeIdToIp()) { // UPF Accept if rsp.Cause == nil { diff --git a/pfcp/handler/handler_test.go b/pfcp/handler/handler_test.go index 38b6a876..823b6e83 100644 --- a/pfcp/handler/handler_test.go +++ b/pfcp/handler/handler_test.go @@ -128,3 +128,77 @@ func TestHandlePfcpAssociationSetupResponse(t *testing.T) { t.Errorf("Expected RecoveryTimeStamp %v, got %v", recoveryTimestamp.Truncate(1*time.Second), upf.RecoveryTimeStamp.RecoveryTimeStamp.Truncate(1*time.Second)) } } + +func TestHandlePfcpSessionEstablishmentResponse(t *testing.T) { + recoveryTimestamp := time.Now() + nodeID := context.NewNodeID("1.1.1.1") + smContext := context.NewSMContext("imsi-123456789012345", 10) + + smContext.Tunnel = &context.UPTunnel{ + DataPathPool: context.DataPathPool{ + 10: &context.DataPath{ + IsDefaultPath: true, + FirstDPNode: &context.DataPathNode{ + UPF: &context.UPF{}, + UpLinkTunnel: &context.GTPTunnel{ + TEID: 0, + }, + }, + }, + }, + ANInformation: struct { + IPAddress net.IP + TEID uint32 + }{ + IPAddress: net.ParseIP("192.168.1.1"), + TEID: 0, + }, + } + + smContext.PFCPContext = map[string]*context.PFCPSessionContext{ + nodeID.ResolveNodeIdToIp().String(): { + RemoteSEID: 12345, + }, + } + + datapath := &context.DataPath{ + FirstDPNode: &context.DataPathNode{ + UPF: &context.UPF{}, + }, + } + smContext.AllocateLocalSEIDForDataPath(datapath) + pfcp_message.InsertPfcpTxn(1, nodeID) + + rsp := message.NewSessionEstablishmentResponse( + 0, + 0, + 1, + 1, + 0, + ie.NewCause(ie.CauseRequestAccepted), + ie.NewNodeID("1.1.1.1", "", ""), + ie.NewRecoveryTimeStamp(recoveryTimestamp), + ie.NewCreatedPDR( + ie.NewFTEID(0, 4321, net.ParseIP("192.168.1.1"), nil, 0), + ), + ) + + udpMessage := udp.Message{ + RemoteAddr: &net.UDPAddr{ + IP: net.ParseIP("1.1.1.1"), + Port: factory.DEFAULT_PFCP_PORT, + }, + PfcpMessage: rsp, + } + + handler.HandlePfcpSessionEstablishmentResponse(&udpMessage) + + if smContext.Tunnel.DataPathPool.GetDefaultPath().FirstDPNode.UpLinkTunnel.TEID != 4321 { + t.Errorf("Expected TEID 4321, got %d", smContext.Tunnel.ANInformation.TEID) + } + + expectedIP := net.ParseIP("192.168.1.1") + if !smContext.Tunnel.ANInformation.IPAddress.Equal(expectedIP) { + t.Errorf("Expected ANInformation IP %v, got %v", expectedIP, smContext.Tunnel.ANInformation.IPAddress) + } +} From ae36e02bfca9f51121ae762a9fb3a47ae6a14425 Mon Sep 17 00:00:00 2001 From: Guillaume Belanger Date: Fri, 8 Nov 2024 09:58:38 -0500 Subject: [PATCH 06/13] chore: add copyright info Signed-off-by: Guillaume Belanger --- context/datapath_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/context/datapath_test.go b/context/datapath_test.go index 8188d0f6..2fb55d28 100644 --- a/context/datapath_test.go +++ b/context/datapath_test.go @@ -1,3 +1,7 @@ +// Copyright 2024 Canonical Ltd. +// +// SPDX-License-Identifier: Apache-2.0 + package context_test import ( From 19f9281f950a512aa94ec21107a95e172911542b Mon Sep 17 00:00:00 2001 From: Guillaume Belanger Date: Fri, 8 Nov 2024 10:05:14 -0500 Subject: [PATCH 07/13] chore: bump version Signed-off-by: Guillaume Belanger --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 896d75a9..359a5b95 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.7.1-dev +2.0.0 \ No newline at end of file From 562d5b009a80d171e6c1b9944b75cc8f780917aa Mon Sep 17 00:00:00 2001 From: Guillaume Belanger Date: Tue, 12 Nov 2024 06:38:19 -0500 Subject: [PATCH 08/13] chore: use different ports to avoid conflicts Signed-off-by: Guillaume Belanger --- pfcp/handler/handler_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pfcp/handler/handler_test.go b/pfcp/handler/handler_test.go index 823b6e83..63051c8c 100644 --- a/pfcp/handler/handler_test.go +++ b/pfcp/handler/handler_test.go @@ -112,7 +112,7 @@ func TestHandlePfcpAssociationSetupResponse(t *testing.T) { remoteAddress := &net.UDPAddr{ IP: net.ParseIP("1.1.1.1"), - Port: factory.DEFAULT_PFCP_PORT, + Port: 8810, } udpMessage := udp.Message{ RemoteAddr: remoteAddress, @@ -186,7 +186,7 @@ func TestHandlePfcpSessionEstablishmentResponse(t *testing.T) { udpMessage := udp.Message{ RemoteAddr: &net.UDPAddr{ IP: net.ParseIP("1.1.1.1"), - Port: factory.DEFAULT_PFCP_PORT, + Port: 8809, }, PfcpMessage: rsp, } From 58c4a220908da6f0810bd693d16fe731626f0e4a Mon Sep 17 00:00:00 2001 From: Guillaume Belanger Date: Tue, 12 Nov 2024 10:53:44 -0500 Subject: [PATCH 09/13] chore: re-add newline to version file Signed-off-by: Guillaume Belanger --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 359a5b95..227cea21 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.0.0 \ No newline at end of file +2.0.0 From 1f33efe464607cfc27ea74675210803f10d94f33 Mon Sep 17 00:00:00 2001 From: Guillaume Belanger Date: Fri, 15 Nov 2024 15:19:13 -0500 Subject: [PATCH 10/13] feat: store upf generated f-teid when using pfcp adapter Signed-off-by: Guillaume Belanger --- pfcp/adapter/adapter.go | 88 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 82 insertions(+), 6 deletions(-) diff --git a/pfcp/adapter/adapter.go b/pfcp/adapter/adapter.go index 050c31df..c7118327 100644 --- a/pfcp/adapter/adapter.go +++ b/pfcp/adapter/adapter.go @@ -4,6 +4,8 @@ package adapter import ( + "fmt" + "net" "sync" "github.com/omec-project/smf/context" @@ -66,6 +68,26 @@ func HandleAdapterPfcpRsp(pfcpMsg message.Message, evtData *udp.PfcpEventData) e return nil } +func FindUEIPAddress(createdPDRIEs []*ie.IE) net.IP { + for _, createdPDRIE := range createdPDRIEs { + ueIPAddress, err := createdPDRIE.UEIPAddress() + if err == nil { + return ueIPAddress.IPv4Address + } + } + return nil +} + +func FindFTEID(createdPDRIEs []*ie.IE) (*ie.FTEIDFields, error) { + for _, createdPDRIE := range createdPDRIEs { + teid, err := createdPDRIE.FTEID() + if err == nil { + return teid, nil + } + } + return nil, fmt.Errorf("FTEID not found in CreatedPDR") +} + func HandlePfcpAssociationSetupResponse(msg *udp.Message) { rsp, ok := msg.PfcpMessage.(*message.AssociationSetupResponse) if !ok { @@ -198,6 +220,10 @@ func HandlePfcpSessionEstablishmentResponse(msg *udp.Message) { // Get NodeId from Seq:NodeId Map seq := rsp.Sequence() nodeID := FetchPfcpTxn(seq) + if nodeID == nil { + logger.PfcpLog.Errorf("no pending pfcp session establishment response for sequence no: %v", seq) + return + } if rsp.UPFSEID != nil { NodeIDtoIP := nodeID.ResolveNodeIdToIp().String() @@ -212,8 +238,64 @@ func HandlePfcpSessionEstablishmentResponse(msg *udp.Message) { } // Get N3 interface UPF + defaultPath := smContext.Tunnel.DataPathPool.GetDefaultPath() + if defaultPath == nil { + logger.PfcpLog.Errorln("failed to get default path") + return + } ANUPF := smContext.Tunnel.DataPathPool.GetDefaultPath().FirstDPNode + if rsp.CreatedPDR != nil { + ueIPAddress := FindUEIPAddress(rsp.CreatedPDR) + if ueIPAddress != nil { + smContext.SubPfcpLog.Infof("upf provided ue ip address [%v]", ueIPAddress) + // Release previous locally allocated UE IP-Addr + err := smContext.ReleaseUeIpAddr() + if err != nil { + logger.PfcpLog.Errorf("failed to release UE IP-Addr: %+v", err) + } + + // Update with one received from UPF + smContext.PDUAddress.Ip = ueIPAddress + smContext.PDUAddress.UpfProvided = true + } + + // Store F-TEID created by UPF + fteid, err := FindFTEID(rsp.CreatedPDR) + if err != nil { + logger.PfcpLog.Errorf("failed to parse TEID IE: %+v", err) + return + } + logger.PfcpLog.Infof("created PDR FTEID: %+v", fteid) + ANUPF.UpLinkTunnel.TEID = fteid.TEID + upf := context.RetrieveUPFNodeByNodeID(*nodeID) + if upf == nil { + logger.PfcpLog.Errorf("can't find UPF[%s]", nodeID.ResolveNodeIdToIp().String()) + return + } + upf.N3Interfaces = make([]context.UPFInterfaceInfo, 0) + n3Interface := context.UPFInterfaceInfo{} + n3Interface.IPv4EndPointAddresses = append(n3Interface.IPv4EndPointAddresses, fteid.IPv4Address) + upf.N3Interfaces = append(upf.N3Interfaces, n3Interface) + } + smContext.SMLock.Unlock() + + if rsp.NodeID == nil { + logger.PfcpLog.Errorln("PFCP Session Establishment Response missing NodeID") + return + } + rspNodeIDStr, err := rsp.NodeID.NodeID() + if err != nil { + logger.PfcpLog.Errorf("failed to parse NodeID IE: %+v", err) + return + } + rspNodeID := context.NewNodeID(rspNodeIDStr) + + if ANUPF.UPF == nil { + logger.PfcpLog.Errorln("failed to get UPF from default path") + return + } + if ANUPF.UPF.NodeID.ResolveNodeIdToIp().Equal(nodeID.ResolveNodeIdToIp()) { if rsp.Cause == nil { logger.PfcpLog.Errorln("pfcp session establishment response has no cause") @@ -232,12 +314,6 @@ func HandlePfcpSessionEstablishmentResponse(msg *udp.Message) { smContext.SBIPFCPCommunicationChan <- context.SessionEstablishFailed smContext.SubPfcpLog.Errorf("PFCP Session Establishment rejected with cause [%v]", causeValue) if causeValue == ie.CauseNoEstablishedPFCPAssociation { - rspNodeIDStr, err := rsp.NodeID.NodeID() - if err != nil { - logger.PfcpLog.Errorf("pfcp session establishment response NodeID error: %v", err) - return - } - rspNodeID := context.NewNodeID(rspNodeIDStr) SetUpfInactive(*rspNodeID) } } From f37c0302ef37de20d49db7beb478ee69c2402e1c Mon Sep 17 00:00:00 2001 From: Guillaume Belanger Date: Fri, 15 Nov 2024 15:19:55 -0500 Subject: [PATCH 11/13] chore: bump go version to v1.23 Signed-off-by: Guillaume Belanger --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index dd61f2df..283e61dc 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/omec-project/smf -go 1.21 +go 1.23 require ( github.com/antihax/optional v1.0.0 From ff1ded0873baa0c950c3834e0f26a7143dc93f2b Mon Sep 17 00:00:00 2001 From: Guillaume Belanger Date: Mon, 18 Nov 2024 08:50:39 -0500 Subject: [PATCH 12/13] chore: add missing lock statement Signed-off-by: Guillaume Belanger --- context/datapath.go | 1 - pfcp/adapter/adapter.go | 5 +++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/context/datapath.go b/context/datapath.go index 5718c18c..f91eac26 100644 --- a/context/datapath.go +++ b/context/datapath.go @@ -562,7 +562,6 @@ func (dpNode *DataPathNode) ActivateDlLinkPdr(smContext *SMContext, defQER *QER, DLPDR.PDI.SourceInterface = SourceInterface{InterfaceValue: SourceInterfaceCore} DLPDR.PDI.UEIPAddress = &ueIpAddr - DLPDR.PDI.UEIPAddress = &ueIpAddr DLFAR := DLPDR.FAR diff --git a/pfcp/adapter/adapter.go b/pfcp/adapter/adapter.go index c7118327..e2f4197d 100644 --- a/pfcp/adapter/adapter.go +++ b/pfcp/adapter/adapter.go @@ -214,6 +214,11 @@ func HandlePfcpSessionEstablishmentResponse(msg *udp.Message) { } } smContext := context.GetSMContextBySEID(SEID) + if smContext == nil { + logger.PfcpLog.Warnln("PFCP Session Establish Response found SM context nil, response discarded") + return + } + smContext.SMLock.Lock() logger.PfcpLog.Infof("in HandlePfcpSessionEstablishmentResponse SEID %v", SEID) logger.PfcpLog.Infof("in HandlePfcpSessionEstablishmentResponse smContext %+v", smContext) From 912b4e5035f086336b3cddd2d0f3790497e1bf6e Mon Sep 17 00:00:00 2001 From: Guillaume Belanger Date: Tue, 26 Nov 2024 12:31:23 -0500 Subject: [PATCH 13/13] chore: defer mutex unlock right away Signed-off-by: Guillaume Belanger --- pfcp/adapter/adapter.go | 2 +- pfcp/handler/handler.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pfcp/adapter/adapter.go b/pfcp/adapter/adapter.go index e2f4197d..3d701251 100644 --- a/pfcp/adapter/adapter.go +++ b/pfcp/adapter/adapter.go @@ -219,6 +219,7 @@ func HandlePfcpSessionEstablishmentResponse(msg *udp.Message) { return } smContext.SMLock.Lock() + defer smContext.SMLock.Unlock() logger.PfcpLog.Infof("in HandlePfcpSessionEstablishmentResponse SEID %v", SEID) logger.PfcpLog.Infof("in HandlePfcpSessionEstablishmentResponse smContext %+v", smContext) @@ -283,7 +284,6 @@ func HandlePfcpSessionEstablishmentResponse(msg *udp.Message) { n3Interface.IPv4EndPointAddresses = append(n3Interface.IPv4EndPointAddresses, fteid.IPv4Address) upf.N3Interfaces = append(upf.N3Interfaces, n3Interface) } - smContext.SMLock.Unlock() if rsp.NodeID == nil { logger.PfcpLog.Errorln("PFCP Session Establishment Response missing NodeID") diff --git a/pfcp/handler/handler.go b/pfcp/handler/handler.go index 3248a978..defc8cb1 100644 --- a/pfcp/handler/handler.go +++ b/pfcp/handler/handler.go @@ -396,6 +396,7 @@ func HandlePfcpSessionEstablishmentResponse(msg *udp.Message) { return } smContext.SMLock.Lock() + defer smContext.SMLock.Unlock() // Get NodeId from Seq:NodeId Map seq := rsp.Sequence() @@ -459,7 +460,6 @@ func HandlePfcpSessionEstablishmentResponse(msg *udp.Message) { n3Interface.IPv4EndPointAddresses = append(n3Interface.IPv4EndPointAddresses, fteid.IPv4Address) upf.N3Interfaces = append(upf.N3Interfaces, n3Interface) } - smContext.SMLock.Unlock() if rsp.NodeID == nil { logger.PfcpLog.Errorln("PFCP Session Establishment Response missing NodeID")