Skip to content
This repository has been archived by the owner on Oct 20, 2024. It is now read-only.

Commit

Permalink
feat: adds server side of pfcp association request
Browse files Browse the repository at this point in the history
  • Loading branch information
gruyaume committed Dec 24, 2023
1 parent 6e8ff02 commit b38c2af
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 31 deletions.
20 changes: 11 additions & 9 deletions ie/ie.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type InformationElement interface {

func ParseInformationElements(b []byte) ([]InformationElement, error) {
var ies []InformationElement
var err error

index := 0

Expand All @@ -21,29 +22,30 @@ func ParseInformationElements(b []byte) ([]InformationElement, error) {
return nil, fmt.Errorf("not enough bytes for IE header")
}

ieType := int(binary.BigEndian.Uint16(b[index : index+2]))
ieLength := int(binary.BigEndian.Uint16(b[index+2 : index+4]))
index += IEHeaderLength // Move past the header

if len(b[index:]) < ieLength {
ieType := binary.BigEndian.Uint16(b[index : index+2])
ieLength := binary.BigEndian.Uint16(b[index+2 : index+4])
index += IEHeaderLength
if len(b[index:]) < int(ieLength) {
return nil, fmt.Errorf("not enough bytes for IE data, expected %d, got %d", ieLength, len(b[index:]))
}

ieValue := b[index : index+ieLength]
ieValue := b[index : index+int(ieLength)]
var ie InformationElement
switch ieType {
case 60:
ie = DeserializeNodeID(ieType, ieLength, ieValue)
case 96:
ie = DeserializeRecoveryTimeStamp(ieType, ieLength, ieValue)
default:
return nil, fmt.Errorf("unknown IE type %d", ieType)
err = fmt.Errorf("unknown IE type %d", ieType)
}

if ie != nil {
ies = append(ies, ie)
}

index += ieLength
index += int(ieLength)
}

return ies, nil
return ies, err
}
20 changes: 17 additions & 3 deletions ie/nodeId.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func NewNodeID(nodeIDType NodeIDType, nodeIDValue string) NodeID {
panic("invalid IPv4 address")
}
nodeIDValueBytes = ipv4
length = uint16(len(nodeIDValueBytes))
length = uint16(len(nodeIDValueBytes)) + 1
case IPv6:
ip := net.ParseIP(nodeIDValue)
if ip == nil {
Expand All @@ -48,14 +48,14 @@ func NewNodeID(nodeIDType NodeIDType, nodeIDValue string) NodeID {
panic("invalid IPv6 address")
}
nodeIDValueBytes = ipv6
length = uint16(len(nodeIDValueBytes))
length = uint16(len(nodeIDValueBytes)) + 1
case FQDN:
fqdn := []byte(nodeIDValue)
if len(fqdn) > 255 {
panic("FQDN too long")
}
nodeIDValueBytes = fqdn
length = uint16(len(nodeIDValueBytes))
length = uint16(len(nodeIDValueBytes)) + 1

default:
panic(fmt.Sprintf("invalid NodeIDType %d", nodeIDType))
Expand Down Expand Up @@ -84,5 +84,19 @@ func (n NodeID) Serialize() []byte {
// Octets 6 to n+5: Node ID Value
buf.Write(n.NodeIDValue)

fmt.Printf("Node ID - serialized: %v\n", buf.Bytes())

return buf.Bytes()
}

func DeserializeNodeID(ieType uint16, ieLength uint16, ieValue []byte) NodeID {
nodeIDType := NodeIDType(ieValue[0] & 0x0F) // Ensure NodeIDType is only 4 bits
nodeIDValue := ieValue[1:]

return NodeID{
Type: ieType,
Length: ieLength,
NodeIDType: nodeIDType,
NodeIDValue: nodeIDValue,
}
}
6 changes: 3 additions & 3 deletions ie/nodeId_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ func TestNewNodeIDIPv4(t *testing.T) {
t.Errorf("Expected NodeID, got %d", nodeID.Type)
}

if nodeID.Length != 4 {
if nodeID.Length != 4+1 {
t.Errorf("Expected NodeID length 4, got %d", nodeID.Length)
}

Expand Down Expand Up @@ -41,7 +41,7 @@ func TestNewNodeIDIPv6(t *testing.T) {
t.Errorf("Expected NodeID, got %d", nodeID.Type)
}

if nodeID.Length != 16 {
if nodeID.Length != 16+1 {
t.Errorf("Expected NodeID length 16, got %d", nodeID.Length)
}

Expand Down Expand Up @@ -69,7 +69,7 @@ func TestNewNodeIDFQDN(t *testing.T) {
t.Errorf("Expected NodeID, got %d", nodeID.Type)
}

if nodeID.Length != 15 {
if nodeID.Length != 15+1 {
t.Errorf("Expected NodeID length 15, got %d", nodeID.Length)
}

Expand Down
6 changes: 3 additions & 3 deletions ie/recoveryTimeStamp.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import (
const ntpEpochOffset = 2208988800 // Offset between Unix and NTP epoch (seconds)

type RecoveryTimeStamp struct {
Type int
Length int
Type uint16
Length uint16
Value int64 // Seconds since 1900
}

Expand All @@ -29,7 +29,7 @@ func (rt RecoveryTimeStamp) Serialize() []byte {
return bytes
}

func DeserializeRecoveryTimeStamp(ieType int, ieLength int, ieValue []byte) RecoveryTimeStamp {
func DeserializeRecoveryTimeStamp(ieType uint16, ieLength uint16, ieValue []byte) RecoveryTimeStamp {
return RecoveryTimeStamp{
Type: ieType,
Length: ieLength,
Expand Down
63 changes: 59 additions & 4 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ var (
heartbeatResponseReceivedSequenceNumber uint32
)

var (
pfcpAssociationSetupRequestMu sync.Mutex
pfcpAssociationSetupRequesthandlerCalled bool
pfcpAssociationSetupRequestReceivedSequenceNumber uint32
pfcpAssociationSetupRequestReceivedRecoveryTimeStamp ie.RecoveryTimeStamp
pfcpAssociationSetupRequestReceivedNodeID ie.NodeID
)

func HandleHeartbeatRequest(sequenceNumber uint32, recoveryTimeStamp ie.RecoveryTimeStamp) {
heartbeatRequestMu.Lock()
defer heartbeatRequestMu.Unlock()
Expand All @@ -38,10 +46,18 @@ func HandleHeartbeatResponse(sequenceNumber uint32, recoveryTimeStamp ie.Recover
heartbeatResponsereceivedRecoveryTimestamp = recoveryTimeStamp
heartbeatResponseReceivedSequenceNumber = sequenceNumber
}
func HandlePFCPAssociationSetupRequest(sequenceNumber uint32, nodeID ie.NodeID, recoveryTimeStamp ie.RecoveryTimeStamp) {
pfcpAssociationSetupRequestMu.Lock()
defer pfcpAssociationSetupRequestMu.Unlock()
pfcpAssociationSetupRequesthandlerCalled = true
pfcpAssociationSetupRequestReceivedSequenceNumber = sequenceNumber
pfcpAssociationSetupRequestReceivedRecoveryTimeStamp = recoveryTimeStamp
pfcpAssociationSetupRequestReceivedNodeID = nodeID
}

func TestServer(t *testing.T) {
t.Run("TestHeartbeatRequest", HeartbeatRequest)
t.Run("TestHeartbeatResponse", HeartbeatResponse)
// t.Run("TestHeartbeatRequest", HeartbeatRequest)
// t.Run("TestHeartbeatResponse", HeartbeatResponse)
t.Run("TestPFCPAssociationSetupRequest", PFCPAssociationSetupRequest)
}

Expand Down Expand Up @@ -112,11 +128,50 @@ func HeartbeatResponse(t *testing.T) {
}

func PFCPAssociationSetupRequest(t *testing.T) {
pfcpServer := server.New("127.0.0.1:8805")
pfcpServer.PFCPAssociationSetupRequest(HandlePFCPAssociationSetupRequest)

go pfcpServer.Run()

time.Sleep(time.Second)
pfcpClient := client.New("127.0.0.1:8805")
nodeID := ie.NewNodeID(ie.IPv4, "1.2.3.4")
nodeID := ie.NewNodeID(ie.IPv4, "12.23.34.45")
recoveryTimeStamp := ie.NewRecoveryTimeStamp(time.Now())
sequenceNumber := uint32(21)
sequenceNumber := uint32(32)
pfcpClient.SendPFCPAssociationSetupRequest(nodeID, recoveryTimeStamp, sequenceNumber)

time.Sleep(time.Second)

pfcpAssociationSetupRequestMu.Lock()
if !pfcpAssociationSetupRequesthandlerCalled {
t.Errorf("PFCP Association Setup Request handler was not called")
}

if pfcpAssociationSetupRequestReceivedSequenceNumber != sequenceNumber {
t.Errorf("PFCP Association Setup Request handler was called with wrong sequence number.\n- Sent sequence number: %v\n- Received sequence number %v\n", sequenceNumber, pfcpAssociationSetupRequestReceivedSequenceNumber)
}

if pfcpAssociationSetupRequestReceivedRecoveryTimeStamp != recoveryTimeStamp {
t.Errorf("PFCP Association Setup Request handler was called with wrong recovery timestamp.\n- Sent recovery timestamp: %v\n- Received recovery timestamp %v\n", recoveryTimeStamp, pfcpAssociationSetupRequestReceivedRecoveryTimeStamp)
}

if pfcpAssociationSetupRequestReceivedNodeID.Length != nodeID.Length {
t.Errorf("PFCP Association Setup Request handler was called with wrong node ID length.\n- Sent node ID length: %v\n- Received node ID length %v\n", nodeID.Length, pfcpAssociationSetupRequestReceivedNodeID.Length)
}

if pfcpAssociationSetupRequestReceivedNodeID.NodeIDType != nodeID.NodeIDType {
t.Errorf("PFCP Association Setup Request handler was called with wrong node ID type.\n- Sent node ID type: %v\n- Received node ID type %v\n", nodeID.NodeIDType, pfcpAssociationSetupRequestReceivedNodeID.NodeIDType)
}

if len(pfcpAssociationSetupRequestReceivedNodeID.NodeIDValue) != len(nodeID.NodeIDValue) {
t.Errorf("PFCP Association Setup Request handler was called with wrong node ID value length.\n- Sent node ID value length: %v\n- Received node ID value length %v\n", len(nodeID.NodeIDValue), len(pfcpAssociationSetupRequestReceivedNodeID.NodeIDValue))
}

for i := range nodeID.NodeIDValue {
if pfcpAssociationSetupRequestReceivedNodeID.NodeIDValue[i] != nodeID.NodeIDValue[i] {
t.Errorf("PFCP Association Setup Request handler was called with wrong node ID value.\n- Sent node ID value: %v\n- Received node ID value %v\n", nodeID.NodeIDValue, pfcpAssociationSetupRequestReceivedNodeID.NodeIDValue)
}
}

pfcpAssociationSetupRequestMu.Unlock()
}
8 changes: 4 additions & 4 deletions network/udpserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ type UdpServer struct {
conn *net.UDPConn
closeCh chan struct{}
wg sync.WaitGroup
Handler func([]byte, net.Addr)
Handler func([]byte)
}

func (udpServer *UdpServer) SetHandler(handler func([]byte, net.Addr)) {
func (udpServer *UdpServer) SetHandler(handler func([]byte)) {
udpServer.Handler = handler
}

Expand All @@ -40,14 +40,14 @@ func (udpServer *UdpServer) Run(address string) error {
return
default:
buffer := make([]byte, 1024)
length, remoteAddr, err := udpServer.conn.ReadFrom(buffer)
length, _, err := udpServer.conn.ReadFrom(buffer)
if err != nil {
log.Printf("Error reading from UDP: %v", err)
continue
}

if udpServer.Handler != nil {
go udpServer.Handler(buffer[:length], remoteAddr)
go udpServer.Handler(buffer[:length])
}
}
}
Expand Down
29 changes: 24 additions & 5 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package server

import (
"log"
"net"

"github.com/dot-5g/pfcp/ie"
"github.com/dot-5g/pfcp/messages"
Expand All @@ -12,13 +11,16 @@ import (
const HeaderSize = 8

const (
HeartbeatRequestType byte = 1
HeartbeatResponseType byte = 2
HeartbeatRequestType byte = 1
HeartbeatResponseType byte = 2
PFCPAssociationSetupRequestType byte = 5
PFCPAssociationSetupResponseType byte = 6
)

// Define a new handler type that specifically accepts RecoveryTimeStampIE
type HandleHeartbeatRequest func(sequenceNumber uint32, recoveryTimeStampIE ie.RecoveryTimeStamp)
type HandleHeartbeatResponse func(sequenceNumber uint32, recoveryTimeStampIE ie.RecoveryTimeStamp)
type HandlePFCPAssociationSetupRequest func(sequenceNumber uint32, nodeID ie.NodeID, recoveryTimeStampIE ie.RecoveryTimeStamp)

type MessageHandler func(header messages.PFCPHeader, ies []ie.InformationElement)

type Server struct {
Expand Down Expand Up @@ -73,7 +75,24 @@ func (server *Server) HeartbeatResponse(handler HandleHeartbeatResponse) {
})
}

func (server *Server) handleUDPMessage(data []byte, addr net.Addr) {
func (server *Server) PFCPAssociationSetupRequest(handler HandlePFCPAssociationSetupRequest) {
server.registerHandler(PFCPAssociationSetupRequestType, func(header messages.PFCPHeader, ies []ie.InformationElement) {
var recoveryTimeStamp ie.RecoveryTimeStamp
var nodeID ie.NodeID
for _, elem := range ies {
if tsIE, ok := elem.(ie.RecoveryTimeStamp); ok {
recoveryTimeStamp = tsIE
}
if nodeIDIE, ok := elem.(ie.NodeID); ok {
nodeID = nodeIDIE
}
}

handler(header.SequenceNumber, nodeID, recoveryTimeStamp)
})
}

func (server *Server) handleUDPMessage(data []byte) {
header, err := messages.ParsePFCPHeader(data[:HeaderSize])
if err != nil {
log.Printf("Error parsing PFCP header: %v", err)
Expand Down

0 comments on commit b38c2af

Please sign in to comment.