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

feat: Adds PFCP Node Report #21

Merged
merged 1 commit into from
Dec 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func HandleHeartbeatRequest(sequenceNumber uint32, msg messages.HeartbeatRequest
- [x] PFCP Association Setup
- [x] PFCP Association Update
- [x] PFCP Association Release
- [ ] PFCP Node Report
- [x] PFCP Node Report

### Session

Expand Down
20 changes: 20 additions & 0 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,23 @@ func (pfcp *Pfcp) SendPFCPAssociationReleaseResponse(msg messages.PFCPAssociatio
}
return nil
}

func (pfcp *Pfcp) SendPFCPNodeReportRequest(msg messages.PFCPNodeReportRequest, sequenceNumber uint32) error {
header := headers.NewPFCPHeader(messages.PFCPNodeReportRequestMessageType, sequenceNumber)
payload := []ie.InformationElement{msg.NodeID, msg.NodeReportType}
err := pfcp.sendPfcpMessage(header, payload)
if err != nil {
return fmt.Errorf("error sending PFCP Node Report Request: %w", err)
}
return nil
}

func (pfcp *Pfcp) SendPFCPNodeReportResponse(msg messages.PFCPNodeReportResponse, sequenceNumber uint32) error {
header := headers.NewPFCPHeader(messages.PFCPNodeReportResponseMessageType, sequenceNumber)
payload := []ie.InformationElement{msg.NodeID, msg.Cause}
err := pfcp.sendPfcpMessage(header, payload)
if err != nil {
return fmt.Errorf("error sending PFCP Node Report Response: %w", err)
}
return nil
}
4 changes: 0 additions & 4 deletions ie/cause.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,6 @@ func (cause Cause) Serialize() []byte {
return buf.Bytes()
}

func (cause Cause) Type() uint16 {
return cause.IEtype
}

func DeserializeCause(ieType uint16, ieLength uint16, ieValue []byte) Cause {
return Cause{
IEtype: ieType,
Expand Down
3 changes: 2 additions & 1 deletion ie/ie.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ const IEHeaderLength = 4

type InformationElement interface {
Serialize() []byte
Type() uint16
}

func ParseInformationElements(b []byte) ([]InformationElement, error) {
Expand Down Expand Up @@ -39,6 +38,8 @@ func ParseInformationElements(b []byte) ([]InformationElement, error) {
ie = DeserializeNodeID(ieType, ieLength, ieValue)
case 96:
ie = DeserializeRecoveryTimeStamp(ieType, ieLength, ieValue)
case 101:
ie = DeserializeNodeReportType(ieType, ieLength, ieValue)
default:
err = fmt.Errorf("unknown IE type %d", ieType)
}
Expand Down
4 changes: 0 additions & 4 deletions ie/nodeId.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,6 @@ func (n NodeID) Serialize() []byte {
return buf.Bytes()
}

func (n NodeID) Type() uint16 {
return n.IEtype
}

func DeserializeNodeID(ieType uint16, ieLength uint16, ieValue []byte) NodeID {
nodeIDType := NodeIDType(ieValue[0] & 0x0F) // Ensure NodeIDType is only 4 bits
nodeIDValue := ieValue[1:]
Expand Down
75 changes: 75 additions & 0 deletions ie/nodeReportType.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package ie

import (
"bytes"
"encoding/binary"
)

type NodeReportType struct {
IEtype uint16
Length uint16
GPQR bool
CKDR bool
UPRR bool
UPFR bool
}

func NewNodeReportType(gpqr bool, ckdr bool, uprr bool, upfr bool) NodeReportType {
return NodeReportType{
IEtype: 101,
Length: 1,
GPQR: gpqr,
CKDR: ckdr,
UPRR: uprr,
UPFR: upfr,
}
}

func (nrt NodeReportType) Serialize() []byte {
buf := new(bytes.Buffer)

// Octets 1 to 2: Type (60)
binary.Write(buf, binary.BigEndian, uint16(nrt.IEtype))

// Octets 3 to 4: Length
binary.Write(buf, binary.BigEndian, uint16(nrt.Length))

// Octet 5: Spare, Spare, Spare, Spare, GPQR, CKDR, UPRR, UPFR
var octet5 byte
if nrt.GPQR {
octet5 |= 1 << 3
}
if nrt.CKDR {
octet5 |= 1 << 2
}
if nrt.UPRR {
octet5 |= 1 << 1
}
if nrt.UPFR {
octet5 |= 1
}
buf.WriteByte(octet5)

return buf.Bytes()
}

func DeserializeNodeReportType(ieType uint16, ieLength uint16, ieValue []byte) NodeReportType {
var nrt NodeReportType

buf := bytes.NewBuffer(ieValue)

nrt.IEtype = ieType
nrt.Length = ieLength

// Read the bit flags from octet 5
var octet5 byte
if len(buf.Bytes()) > 0 {
octet5, _ = buf.ReadByte()
nrt.GPQR = octet5&0x08 != 0
nrt.CKDR = octet5&0x04 != 0
nrt.UPRR = octet5&0x02 != 0
nrt.UPFR = octet5&0x01 != 0
}

return nrt
}
4 changes: 0 additions & 4 deletions ie/recoveryTimeStamp.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,6 @@ func (rt RecoveryTimeStamp) Serialize() []byte {
return bytes
}

func (rt RecoveryTimeStamp) Type() uint16 {
return rt.IEtype
}

func DeserializeRecoveryTimeStamp(ieType uint16, ieLength uint16, ieValue []byte) RecoveryTimeStamp {
return RecoveryTimeStamp{
IEtype: ieType,
Expand Down
2 changes: 0 additions & 2 deletions messages/heartbeat.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,10 @@ import (

type HeartbeatRequest struct {
MessageType MessageType
SequenceNumber uint32
RecoveryTimeStamp ie.RecoveryTimeStamp
}

type HeartbeatResponse struct {
SequenceNumber uint32
RecoveryTimeStamp ie.RecoveryTimeStamp
}

Expand Down
2 changes: 2 additions & 0 deletions messages/messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ const (
PFCPAssociationUpdateResponseMessageType MessageType = 8
PFCPAssociationReleaseRequestMessageType MessageType = 9
PFCPAssociationReleaseResponseMessageType MessageType = 10
PFCPNodeReportRequestMessageType MessageType = 12
PFCPNodeReportResponseMessageType MessageType = 13
)
8 changes: 3 additions & 5 deletions messages/pfcp_association_release.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,12 @@ package messages
import "github.com/dot-5g/pfcp/ie"

type PFCPAssociationReleaseRequest struct {
SequenceNumber uint32
NodeID ie.NodeID
NodeID ie.NodeID
}

type PFCPAssociationReleaseResponse struct {
SequenceNumber uint32
NodeID ie.NodeID
Cause ie.Cause
NodeID ie.NodeID
Cause ie.Cause
}

func NewPFCPAssociationReleaseRequest(nodeID ie.NodeID) PFCPAssociationReleaseRequest {
Expand Down
8 changes: 3 additions & 5 deletions messages/pfcp_association_update.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,12 @@ package messages
import "github.com/dot-5g/pfcp/ie"

type PFCPAssociationUpdateRequest struct {
SequenceNumber uint32
NodeID ie.NodeID
NodeID ie.NodeID
}

type PFCPAssociationUpdateResponse struct {
SequenceNumber uint32
NodeID ie.NodeID
Cause ie.Cause
NodeID ie.NodeID
Cause ie.Cause
}

func NewPFCPAssociationUpdateRequest(nodeID ie.NodeID) PFCPAssociationUpdateRequest {
Expand Down
71 changes: 71 additions & 0 deletions messages/pfcp_node_report.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package messages

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

type PFCPNodeReportRequest struct {
NodeID ie.NodeID
NodeReportType ie.NodeReportType
}

type PFCPNodeReportResponse struct {
NodeID ie.NodeID
Cause ie.Cause
}

func NewPFCPNodeReportRequest(nodeID ie.NodeID, nodeReportType ie.NodeReportType) PFCPNodeReportRequest {
return PFCPNodeReportRequest{
NodeID: nodeID,
NodeReportType: nodeReportType,
}
}

func NewPFCPNodeReportResponse(nodeID ie.NodeID, cause ie.Cause) PFCPNodeReportResponse {
return PFCPNodeReportResponse{
NodeID: nodeID,
Cause: cause,
}
}

func ParsePFCPNodeReportRequest(data []byte) (PFCPNodeReportRequest, error) {
ies, err := ie.ParseInformationElements(data)
var nodeID ie.NodeID
var nodeReportType ie.NodeReportType
for _, elem := range ies {
if nodeIDIE, ok := elem.(ie.NodeID); ok {
nodeID = nodeIDIE
continue
}
if nodeReportTypeIE, ok := elem.(ie.NodeReportType); ok {
nodeReportType = nodeReportTypeIE
continue
}
}

return PFCPNodeReportRequest{
NodeID: nodeID,
NodeReportType: nodeReportType,
}, err
}

func ParsePFCPNodeReportResponse(data []byte) (PFCPNodeReportResponse, error) {
ies, err := ie.ParseInformationElements(data)
var nodeID ie.NodeID
var cause ie.Cause
for _, elem := range ies {
if nodeIDIE, ok := elem.(ie.NodeID); ok {
nodeID = nodeIDIE
continue
}
if causeIE, ok := elem.(ie.Cause); ok {
cause = causeIE
continue
}
}

return PFCPNodeReportResponse{
NodeID: nodeID,
Cause: cause,
}, err
}
34 changes: 34 additions & 0 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ type HandlePFCPAssociationUpdateRequest func(sequenceNumber uint32, msg messages
type HandlePFCPAssociationUpdateResponse func(sequenceNumber uint32, msg messages.PFCPAssociationUpdateResponse)
type HandlePFCPAssociationReleaseRequest func(sequenceNumber uint32, msg messages.PFCPAssociationReleaseRequest)
type HandlePFCPAssociationReleaseResponse func(sequenceNumber uint32, msg messages.PFCPAssociationReleaseResponse)
type HandlePFCPNodeReportRequest func(sequenceNumber uint32, msg messages.PFCPNodeReportRequest)
type HandlePFCPNodeReportResponse func(sequenceNumber uint32, msg messages.PFCPNodeReportResponse)

type Server struct {
address string
Expand All @@ -29,6 +31,8 @@ type Server struct {
pfcpAssociationUpdateResponseHandler HandlePFCPAssociationUpdateResponse
pfcpAssociationReleaseRequestHandler HandlePFCPAssociationReleaseRequest
pfcpAssociationReleaseResponseHandler HandlePFCPAssociationReleaseResponse
pfcpNodeReportRequestHandler HandlePFCPNodeReportRequest
pfcpNodeReportResponseHandler HandlePFCPNodeReportResponse
}

func New(address string) *Server {
Expand Down Expand Up @@ -81,6 +85,14 @@ func (server *Server) PFCPAssociationReleaseResponse(handler HandlePFCPAssociati
server.pfcpAssociationReleaseResponseHandler = handler
}

func (server *Server) PFCPNodeReportRequest(handler HandlePFCPNodeReportRequest) {
server.pfcpNodeReportRequestHandler = handler
}

func (server *Server) PFCPNodeReportResponse(handler HandlePFCPNodeReportResponse) {
server.pfcpNodeReportResponseHandler = handler
}

func (server *Server) handleUDPMessage(data []byte) {
header, err := headers.ParsePFCPHeader(data[:headers.HeaderSize])
if err != nil {
Expand Down Expand Up @@ -177,6 +189,28 @@ func (server *Server) handleUDPMessage(data []byte) {
return
}
server.pfcpAssociationReleaseResponseHandler(header.SequenceNumber, msg)
case messages.PFCPNodeReportRequestMessageType:
if server.pfcpNodeReportRequestHandler == nil {
log.Printf("No handler for PFCP Node Report Request")
return
}
msg, err := messages.ParsePFCPNodeReportRequest(data[headers.HeaderSize:])
if err != nil {
log.Printf("Error parsing PFCP Node Report Request: %v", err)
return
}
server.pfcpNodeReportRequestHandler(header.SequenceNumber, msg)
case messages.PFCPNodeReportResponseMessageType:
if server.pfcpNodeReportResponseHandler == nil {
log.Printf("No handler for PFCP Node Report Response")
return
}
msg, err := messages.ParsePFCPNodeReportResponse(data[headers.HeaderSize:])
if err != nil {
log.Printf("Error parsing PFCP Node Report Response: %v", err)
return
}
server.pfcpNodeReportResponseHandler(header.SequenceNumber, msg)
default:
log.Printf("Unknown PFCP message type: %v", header.MessageType)
}
Expand Down
Loading
Loading