diff --git a/README.md b/README.md index 692663c..8673316 100644 --- a/README.md +++ b/README.md @@ -78,4 +78,4 @@ func HandleHeartbeatRequest(sequenceNumber uint32, msg messages.HeartbeatRequest - [x] PFCP Session Establishment - [ ] PFCP Session Modification - [x] PFCP Session Deletion -- [ ] PFCP Session Report +- [x] PFCP Session Report diff --git a/client/client.go b/client/client.go index 176a6ed..073d294 100644 --- a/client/client.go +++ b/client/client.go @@ -193,3 +193,23 @@ func (pfcp *Pfcp) SendPFCPSessionDeletionResponse(msg messages.PFCPSessionDeleti } return nil } + +func (pfcp *Pfcp) SendPFCPSessionReportRequest(msg messages.PFCPSessionReportRequest, seid uint64, sequenceNumber uint32) error { + header := messages.NewSessionPFCPHeader(messages.PFCPSessionReportRequestMessageType, seid, sequenceNumber) + ies := []ie.InformationElement{msg.ReportType} + err := pfcp.sendPfcpMessage(header, ies) + if err != nil { + return fmt.Errorf("error sending PFCP Session Report Request: %w", err) + } + return nil +} + +func (pfcp *Pfcp) SendPFCPSessionReportResponse(msg messages.PFCPSessionReportResponse, seid uint64, sequenceNumber uint32) error { + header := messages.NewSessionPFCPHeader(messages.PFCPSessionReportResponseMessageType, seid, sequenceNumber) + ies := []ie.InformationElement{msg.Cause} + err := pfcp.sendPfcpMessage(header, ies) + if err != nil { + return fmt.Errorf("error sending PFCP Session Report Response: %w", err) + } + return nil +} diff --git a/ie/cause.go b/ie/cause.go index 338994e..4007832 100644 --- a/ie/cause.go +++ b/ie/cause.go @@ -9,14 +9,41 @@ import ( type Cause struct { IEType uint16 Length uint16 - Value uint8 + Value CauseValue } -func NewCause(value int) (Cause, error) { +type CauseValue uint8 + +const ( + RequestAccepted CauseValue = 1 + MoreUsageReportToSend CauseValue = 2 + RequestRejected CauseValue = 64 + SessionContextNotFound CauseValue = 65 + MandatoryIEMissing CauseValue = 66 + ConditionalIEMissing CauseValue = 67 + InvalidLength CauseValue = 68 + MandatoryIEIncorrect CauseValue = 69 + InvalidForwardingPolicy CauseValue = 70 + InvalidFTeidAllocation CauseValue = 71 + NoEstablishedPFCPAssociation CauseValue = 72 + RuleCreationFailure CauseValue = 73 + PFCPEntityInCongestion CauseValue = 74 + NoResourcesAvailable CauseValue = 75 + ServiceNotSupported CauseValue = 76 + SystemFailure CauseValue = 77 + RedirectionRequested CauseValue = 78 +) + +func NewCause(value CauseValue) (Cause, error) { + // Validate that value is in the range of supported values + if value < 1 || value > 78 { + return Cause{}, fmt.Errorf("invalid value for Cause: %d", value) + } + return Cause{ IEType: uint16(CauseIEType), Length: 1, - Value: uint8(value), + Value: value, }, nil } @@ -30,7 +57,7 @@ func (cause Cause) Serialize() []byte { binary.Write(buf, binary.BigEndian, uint16(cause.Length)) // Octet 5: Value (1 byte) - buf.WriteByte(cause.Value) + buf.WriteByte(uint8(cause.Value)) return buf.Bytes() } @@ -57,6 +84,6 @@ func DeserializeCause(ieType uint16, ieLength uint16, ieValue []byte) (Cause, er return Cause{ IEType: ieType, Length: ieLength, - Value: ieValue[0], + Value: CauseValue(ieValue[0]), }, nil } diff --git a/ie/ie.go b/ie/ie.go index 4d6228f..f399301 100644 --- a/ie/ie.go +++ b/ie/ie.go @@ -16,6 +16,7 @@ const ( CauseIEType IEType = 19 SourceInterfaceIEType IEType = 20 PrecedenceIEType IEType = 29 + ReportTypeIEType IEType = 39 UPFunctionFeaturesIEType IEType = 43 ApplyActionIEType IEType = 44 PDRIDIEType IEType = 56 @@ -83,6 +84,8 @@ func ParseInformationElements(b []byte) ([]InformationElement, error) { ie, err = DeserializeApplyAction(uint16(ieType), ieLength, ieValue) case CreateFARIEType: ie, err = DeserializeCreateFAR(uint16(ieType), ieLength, ieValue) + case ReportTypeIEType: + ie, err = DeserializeReportType(uint16(ieType), ieLength, ieValue) default: err = fmt.Errorf("unknown IE type %d", ieType) } diff --git a/ie/reportType.go b/ie/reportType.go new file mode 100644 index 0000000..3c1c3c9 --- /dev/null +++ b/ie/reportType.go @@ -0,0 +1,78 @@ +package ie + +import ( + "bytes" + "encoding/binary" + "errors" +) + +type Report int + +const ( + UISR Report = iota + SESR + TMIR + UPIR + ERIR + USAR + DLDR +) + +type ReportType struct { + IEType uint16 + Length uint16 + Reports []Report +} + +func NewReportType(reports []Report) (ReportType, error) { + return ReportType{ + IEType: uint16(ReportTypeIEType), + Length: 1, + Reports: reports, + }, nil +} + +func (reportType ReportType) Serialize() []byte { + buf := new(bytes.Buffer) + + // Octets 1 to 2: Type + binary.Write(buf, binary.BigEndian, uint16(ReportTypeIEType)) + + // Octets 3 to 4: Length + binary.Write(buf, binary.BigEndian, uint16(reportType.Length)) + + // Octet 5: Reports + // Bit 1: DLDR, Bit 2: USAR, Bit 3: ERIR, Bit 4: UPIR, Bit 5: TMIR, Bit 6: SESR, Bit 7: UISR, Bit 8: Spare + var reportsByte byte = 0 + for _, report := range reportType.Reports { + reportsByte |= 1 << report + } + buf.WriteByte(reportsByte) + + return buf.Bytes() +} + +func (reportType ReportType) IsZeroValue() bool { + return reportType.Length == 0 +} + +func DeserializeReportType(ieType uint16, ieLength uint16, ieValue []byte) (ReportType, error) { + if len(ieValue) != int(ieLength) { + return ReportType{}, errors.New("invalid length for ReportType") + } + + var reports []Report + reportsByte := ieValue[0] + + for i := 0; i < 8; i++ { + if reportsByte&(1<