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

Commit

Permalink
feat: Implements client 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 290b9e1 commit 6e8ff02
Show file tree
Hide file tree
Showing 5 changed files with 183 additions and 13 deletions.
33 changes: 26 additions & 7 deletions client/client.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package client

import (
"fmt"
"log"

"github.com/dot-5g/pfcp/ie"
Expand All @@ -22,17 +23,17 @@ func New(ServerAddress string) *Pfcp {
return &Pfcp{ServerAddress: ServerAddress, Udp: udpClient}
}

func (pfcp *Pfcp) sendPfcpMessage(header messages.PFCPHeader, elements []ie.InformationElement, messageType string) error {
func (pfcp *Pfcp) sendPfcpMessage(header messages.PFCPHeader, elements []ie.InformationElement) error {
var payload []byte
for _, element := range elements {
payload = append(payload, element.Serialize()...)
}
message := serializeMessage(header, payload)
if err := pfcp.Udp.Send(message); err != nil {
log.Printf("Failed to send PFCP %s: %v\n", messageType, err)
log.Printf("Failed to send PFCP: %v\n", err)
return err
}
log.Printf("PFCP %s sent successfully to %s.\n", messageType, pfcp.ServerAddress)
log.Printf("PFCP sent successfully to %s.\n", pfcp.ServerAddress)
return nil
}

Expand All @@ -44,12 +45,30 @@ func serializeMessage(header messages.PFCPHeader, payload []byte) []byte {

func (pfcp *Pfcp) SendHeartbeatRequest(recoveryTimeStamp ie.RecoveryTimeStamp, sequenceNumber uint32) (ie.RecoveryTimeStamp, error) {
header := messages.NewPFCPHeader(messages.PFCPHeartbeatRequest, sequenceNumber)
err := pfcp.sendPfcpMessage(header, []ie.InformationElement{recoveryTimeStamp}, "Heartbeat Request")
return recoveryTimeStamp, err
payload := []ie.InformationElement{recoveryTimeStamp}
err := pfcp.sendPfcpMessage(header, payload)
if err != nil {
return recoveryTimeStamp, fmt.Errorf("error sending PFCP Heartbeat Request: %w", err)
}
return recoveryTimeStamp, nil
}

func (pfcp *Pfcp) SendHeartbeatResponse(recoveryTimeStamp ie.RecoveryTimeStamp, sequenceNumber uint32) (ie.RecoveryTimeStamp, error) {
header := messages.NewPFCPHeader(messages.PFCPHeartbeatResponse, sequenceNumber)
err := pfcp.sendPfcpMessage(header, []ie.InformationElement{recoveryTimeStamp}, "Heartbeat Response")
return recoveryTimeStamp, err
payload := []ie.InformationElement{recoveryTimeStamp}
err := pfcp.sendPfcpMessage(header, payload)
if err != nil {
return recoveryTimeStamp, fmt.Errorf("error sending PFCP Heartbeat Response: %w", err)
}
return recoveryTimeStamp, nil
}

func (pfcp *Pfcp) SendPFCPAssociationSetupRequest(nodeID ie.NodeID, recoveryTimeStamp ie.RecoveryTimeStamp, sequenceNumber uint32) error {
header := messages.NewPFCPHeader(messages.PFCPAssociationSetupRequest, sequenceNumber)
payload := []ie.InformationElement{nodeID, recoveryTimeStamp}
err := pfcp.sendPfcpMessage(header, payload)
if err != nil {
return fmt.Errorf("error sending PFCP Association Setup Request: %w", err)
}
return nil
}
6 changes: 4 additions & 2 deletions ie/ie.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"fmt"
)

const IEHeaderLength = 4

type InformationElement interface {
Serialize() []byte
}
Expand All @@ -15,13 +17,13 @@ func ParseInformationElements(b []byte) ([]InformationElement, error) {
index := 0

for index < len(b) {
if len(b[index:]) < 4 {
if len(b[index:]) < IEHeaderLength {
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 += 4 // Move past the header
index += IEHeaderLength // Move past the header

if len(b[index:]) < ieLength {
return nil, fmt.Errorf("not enough bytes for IE data, expected %d, got %d", ieLength, len(b[index:]))
Expand Down
55 changes: 51 additions & 4 deletions ie/nodeId.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,68 @@ package ie
import (
"bytes"
"encoding/binary"
"fmt"
"net"
)

const (
IPv4 NodeIDType = iota
IPv6
FQDN
)

type NodeIDType int

type NodeID struct {
Type uint16
Length uint16
NodeIDType int
NodeIDType NodeIDType
NodeIDValue []byte
}

func NewNodeID(nodeIDType int, nodeIDValue []byte) NodeID {
func NewNodeID(nodeIDType NodeIDType, nodeIDValue string) NodeID {
var nodeIDValueBytes []byte
var length uint16

switch nodeIDType {
case IPv4:
ip := net.ParseIP(nodeIDValue)
if ip == nil {
panic("invalid IPv4 address")
}
ipv4 := ip.To4()
if ipv4 == nil {
panic("invalid IPv4 address")
}
nodeIDValueBytes = ipv4
length = uint16(len(nodeIDValueBytes))
case IPv6:
ip := net.ParseIP(nodeIDValue)
if ip == nil {
panic("invalid IPv6 address")
}
ipv6 := ip.To16()
if ipv6 == nil {
panic("invalid IPv6 address")
}
nodeIDValueBytes = ipv6
length = uint16(len(nodeIDValueBytes))
case FQDN:
fqdn := []byte(nodeIDValue)
if len(fqdn) > 255 {
panic("FQDN too long")
}
nodeIDValueBytes = fqdn
length = uint16(len(nodeIDValueBytes))

default:
panic(fmt.Sprintf("invalid NodeIDType %d", nodeIDType))
}
return NodeID{
Type: 60,
Length: uint16(len(nodeIDValue) + 1),
Length: length,
NodeIDType: nodeIDType,
NodeIDValue: nodeIDValue,
NodeIDValue: nodeIDValueBytes,
}
}

Expand Down
91 changes: 91 additions & 0 deletions ie/nodeId_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package ie_test

import (
"testing"

"github.com/dot-5g/pfcp/ie"
)

func TestNewNodeIDIPv4(t *testing.T) {
nodeID := ie.NewNodeID(ie.IPv4, "1.2.3.4")

if nodeID.Type != 60 {
t.Errorf("Expected NodeID, got %d", nodeID.Type)
}

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

if nodeID.NodeIDType != 0 {
t.Errorf("Expected NodeID type IPv4, got %d", nodeID.NodeIDType)
}

if len(nodeID.NodeIDValue) != 4 {
t.Errorf("Expected NodeID value length 4, got %d", len(nodeID.NodeIDValue))
}

expectedNodeIDValue := []byte{1, 2, 3, 4}
for i := range nodeID.NodeIDValue {
if nodeID.NodeIDValue[i] != expectedNodeIDValue[i] {
t.Errorf("Expected NodeID value %v, got %v", expectedNodeIDValue, nodeID.NodeIDValue)
}
}

}

func TestNewNodeIDIPv6(t *testing.T) {
nodeID := ie.NewNodeID(ie.IPv6, "2001:db8::68")

if nodeID.Type != 60 {
t.Errorf("Expected NodeID, got %d", nodeID.Type)
}

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

if nodeID.NodeIDType != 1 {
t.Errorf("Expected NodeID type IPv6, got %d", nodeID.NodeIDType)
}

if len(nodeID.NodeIDValue) != 16 {
t.Errorf("Expected NodeID value length 16, got %d", len(nodeID.NodeIDValue))
}

expectedNodeIDValue := []byte{32, 1, 13, 184, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 104}
for i := range nodeID.NodeIDValue {
if nodeID.NodeIDValue[i] != expectedNodeIDValue[i] {
t.Errorf("Expected NodeID value %v, got %v", expectedNodeIDValue, nodeID.NodeIDValue)
}
}

}

func TestNewNodeIDFQDN(t *testing.T) {
nodeID := ie.NewNodeID(ie.FQDN, "www.example.com")

if nodeID.Type != 60 {
t.Errorf("Expected NodeID, got %d", nodeID.Type)
}

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

if nodeID.NodeIDType != 2 {
t.Errorf("Expected NodeID type FQDN, got %d", nodeID.NodeIDType)
}

if len(nodeID.NodeIDValue) != 15 {
t.Errorf("Expected NodeID value length 15, got %d", len(nodeID.NodeIDValue))
}

expectedNodeIDValue := []byte{119, 119, 119, 46, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109}
for i := range nodeID.NodeIDValue {
if nodeID.NodeIDValue[i] != expectedNodeIDValue[i] {
t.Errorf("Expected NodeID value %v, got %v", expectedNodeIDValue, nodeID.NodeIDValue)
}
}

}
11 changes: 11 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ func HandleHeartbeatResponse(sequenceNumber uint32, recoveryTimeStamp ie.Recover
func TestServer(t *testing.T) {
t.Run("TestHeartbeatRequest", HeartbeatRequest)
t.Run("TestHeartbeatResponse", HeartbeatResponse)
t.Run("TestPFCPAssociationSetupRequest", PFCPAssociationSetupRequest)
}

func HeartbeatRequest(t *testing.T) {
Expand Down Expand Up @@ -109,3 +110,13 @@ func HeartbeatResponse(t *testing.T) {
pfcpServer.Close()

}

func PFCPAssociationSetupRequest(t *testing.T) {

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

}

0 comments on commit 6e8ff02

Please sign in to comment.