From cbea0d65c4c08ed490a8b0a48f5a7e120450f073 Mon Sep 17 00:00:00 2001 From: Sven Rebhan Date: Thu, 23 May 2024 18:59:31 +0200 Subject: [PATCH 1/3] chore(decoders.sflow): Cleanup constant definitions and use switch statements --- decoders/sflow/sflow.go | 119 ++++++++++++++++++++++++----------- decoders/sflow/sflow_test.go | 16 ++--- 2 files changed, 87 insertions(+), 48 deletions(-) diff --git a/decoders/sflow/sflow.go b/decoders/sflow/sflow.go index 20656d72..453e7b22 100644 --- a/decoders/sflow/sflow.go +++ b/decoders/sflow/sflow.go @@ -7,6 +7,48 @@ import ( "github.com/netsampler/goflow2/v2/decoders/utils" ) +// Opaque sample_data types according to https://sflow.org/SFLOW-DATAGRAM5.txt +const ( + SAMPLE_FORMAT_FLOW = 1 + SAMPLE_FORMAT_COUNTER = 2 + SAMPLE_FORMAT_EXPANDED_FLOW = 3 + SAMPLE_FORMAT_EXPANDED_COUNTER = 4 +) + +// Opaque flow_data types according to https://sflow.org/SFLOW-STRUCTS5.txt +const ( + FLOW_TYPE_RAW = 1 + FLOW_TYPE_ETH = 2 + FLOW_TYPE_IPV4 = 3 + FLOW_TYPE_IPV6 = 4 + FLOW_TYPE_EXT_SWITCH = 1001 + FLOW_TYPE_EXT_ROUTER = 1002 + FLOW_TYPE_EXT_GATEWAY = 1003 + FLOW_TYPE_EXT_USER = 1004 + FLOW_TYPE_EXT_URL = 1005 + FLOW_TYPE_EXT_MPLS = 1006 + FLOW_TYPE_EXT_NAT = 1007 + FLOW_TYPE_EXT_MPLS_TUNNEL = 1008 + FLOW_TYPE_EXT_MPLS_VC = 1009 + FLOW_TYPE_EXT_MPLS_FEC = 1010 + FLOW_TYPE_EXT_MPLS_LVP_FEC = 1011 + FLOW_TYPE_EXT_VLAN_TUNNEL = 1012 +) + +// Opaque counter_data types according to https://sflow.org/SFLOW-STRUCTS5.txt +const ( + COUNTER_TYPE_IF = 1 + COUNTER_TYPE_ETH = 2 + COUNTER_TYPE_TOKENRING = 3 + COUNTER_TYPE_VG = 4 + COUNTER_TYPE_VLAN = 5 + COUNTER_TYPE_CPU = 1001 +) + +// Deprecated: These definitions are mixing sample-format, flow-data type and +// counter-data type definitions and are left here for compatibility only. +// +// Use the sample-format or flow/counter type definitions instead! const ( FORMAT_EXT_SWITCH = 1001 FORMAT_EXT_ROUTER = 1002 @@ -84,7 +126,7 @@ func DecodeCounterRecord(header *RecordHeader, payload *bytes.Buffer) (CounterRe Header: *header, } switch header.DataFormat { - case 1: + case COUNTER_TYPE_IF: var ifCounters IfCounters if err := utils.BinaryDecoder(payload, &ifCounters.IfIndex, @@ -110,7 +152,7 @@ func DecodeCounterRecord(header *RecordHeader, payload *bytes.Buffer) (CounterRe return counterRecord, &RecordError{header.DataFormat, err} } counterRecord.Data = ifCounters - case 2: + case COUNTER_TYPE_ETH: var ethernetCounters EthernetCounters if err := utils.BinaryDecoder(payload, ðernetCounters.Dot3StatsAlignmentErrors, @@ -145,14 +187,7 @@ func DecodeFlowRecord(header *RecordHeader, payload *bytes.Buffer) (FlowRecord, } var err error switch header.DataFormat { - case FORMAT_EXT_SWITCH: - extendedSwitch := ExtendedSwitch{} - err := utils.BinaryDecoder(payload, &extendedSwitch.SrcVlan, &extendedSwitch.SrcPriority, &extendedSwitch.DstVlan, &extendedSwitch.DstPriority) - if err != nil { - return flowRecord, &RecordError{header.DataFormat, err} - } - flowRecord.Data = extendedSwitch - case FORMAT_RAW_PKT: + case FLOW_TYPE_RAW: sampledHeader := SampledHeader{} if err := utils.BinaryDecoder(payload, &sampledHeader.Protocol, @@ -164,7 +199,7 @@ func DecodeFlowRecord(header *RecordHeader, payload *bytes.Buffer) (FlowRecord, } sampledHeader.HeaderData = payload.Bytes() flowRecord.Data = sampledHeader - case FORMAT_IPV4: + case FLOW_TYPE_IPV4: sampledIP := SampledIPv4{ SampledIPBase: SampledIPBase{ SrcIP: make([]byte, 4), @@ -184,7 +219,7 @@ func DecodeFlowRecord(header *RecordHeader, payload *bytes.Buffer) (FlowRecord, return flowRecord, &RecordError{header.DataFormat, err} } flowRecord.Data = sampledIP - case FORMAT_IPV6: + case FLOW_TYPE_IPV6: sampledIP := SampledIPv6{ SampledIPBase: SampledIPBase{ SrcIP: make([]byte, 16), @@ -204,7 +239,14 @@ func DecodeFlowRecord(header *RecordHeader, payload *bytes.Buffer) (FlowRecord, return flowRecord, &RecordError{header.DataFormat, err} } flowRecord.Data = sampledIP - case FORMAT_EXT_ROUTER: + case FLOW_TYPE_EXT_SWITCH: + extendedSwitch := ExtendedSwitch{} + err := utils.BinaryDecoder(payload, &extendedSwitch.SrcVlan, &extendedSwitch.SrcPriority, &extendedSwitch.DstVlan, &extendedSwitch.DstPriority) + if err != nil { + return flowRecord, &RecordError{header.DataFormat, err} + } + flowRecord.Data = extendedSwitch + case FLOW_TYPE_EXT_ROUTER: extendedRouter := ExtendedRouter{} if extendedRouter.NextHopIPVersion, extendedRouter.NextHop, err = DecodeIP(payload); err != nil { return flowRecord, &RecordError{header.DataFormat, err} @@ -216,7 +258,7 @@ func DecodeFlowRecord(header *RecordHeader, payload *bytes.Buffer) (FlowRecord, return flowRecord, &RecordError{header.DataFormat, err} } flowRecord.Data = extendedRouter - case FORMAT_EXT_GATEWAY: + case FLOW_TYPE_EXT_GATEWAY: extendedGateway := ExtendedGateway{} if extendedGateway.NextHopIPVersion, extendedGateway.NextHop, err = DecodeIP(payload); err != nil { return flowRecord, &RecordError{header.DataFormat, err} @@ -295,7 +337,9 @@ func DecodeSample(header *SampleHeader, payload *bytes.Buffer) (interface{}, err return sample, fmt.Errorf("header seq [%w]", err) } seq := header.SampleSequenceNumber - if format == FORMAT_RAW_PKT || format == FORMAT_ETH { + switch format { + case SAMPLE_FORMAT_FLOW, SAMPLE_FORMAT_COUNTER: + // Interlaced data-source format var sourceId uint32 if err := utils.BinaryDecoder(payload, &sourceId); err != nil { return sample, &FlowError{format, seq, fmt.Errorf("header source [%w]", err)} @@ -303,14 +347,15 @@ func DecodeSample(header *SampleHeader, payload *bytes.Buffer) (interface{}, err header.SourceIdType = sourceId >> 24 header.SourceIdValue = sourceId & 0x00ffffff - } else if format == FORMAT_IPV4 || format == FORMAT_IPV6 { + case SAMPLE_FORMAT_EXPANDED_FLOW, SAMPLE_FORMAT_EXPANDED_COUNTER: + // Explicit data-source format if err := utils.BinaryDecoder(payload, &header.SourceIdType, &header.SourceIdValue, ); err != nil { return sample, &FlowError{format, seq, fmt.Errorf("header source [%w]", err)} } - } else { + default: return sample, &FlowError{format, seq, fmt.Errorf("unknown format %d", format)} } @@ -318,10 +363,9 @@ func DecodeSample(header *SampleHeader, payload *bytes.Buffer) (interface{}, err var flowSample FlowSample var counterSample CounterSample var expandedFlowSample ExpandedFlowSample - if format == FORMAT_RAW_PKT { - flowSample = FlowSample{ - Header: *header, - } + switch format { + case SAMPLE_FORMAT_FLOW: + flowSample.Header = *header if err := utils.BinaryDecoder(payload, &flowSample.SamplingRate, &flowSample.SamplePool, @@ -338,23 +382,19 @@ func DecodeSample(header *SampleHeader, payload *bytes.Buffer) (interface{}, err } flowSample.Records = make([]FlowRecord, recordsCount) // max size of 1000 for protection sample = flowSample - } else if format == FORMAT_ETH || format == FORMAT_IPV6 { - if err := utils.BinaryDecoder(payload, &recordsCount); err != nil { + case SAMPLE_FORMAT_COUNTER, SAMPLE_FORMAT_EXPANDED_COUNTER: + counterSample.Header = *header + if err := utils.BinaryDecoder(payload, &counterSample.CounterRecordsCount); err != nil { return sample, &FlowError{format, seq, fmt.Errorf("eth [%w]", err)} } + recordsCount = counterSample.CounterRecordsCount if recordsCount > 1000 { // protection against ddos return sample, &FlowError{format, seq, fmt.Errorf("too many flow records: %d", recordsCount)} } - counterSample = CounterSample{ - Header: *header, - CounterRecordsCount: recordsCount, - } counterSample.Records = make([]CounterRecord, recordsCount) // max size of 1000 for protection sample = counterSample - } else if format == FORMAT_IPV4 { - expandedFlowSample = ExpandedFlowSample{ - Header: *header, - } + case SAMPLE_FORMAT_EXPANDED_FLOW: + expandedFlowSample.Header = *header if err := utils.BinaryDecoder(payload, &expandedFlowSample.SamplingRate, &expandedFlowSample.SamplePool, @@ -383,22 +423,25 @@ func DecodeSample(header *SampleHeader, payload *bytes.Buffer) (interface{}, err break } recordReader := bytes.NewBuffer(payload.Next(int(recordHeader.Length))) - if format == FORMAT_RAW_PKT || format == FORMAT_IPV4 { + switch format { + case SAMPLE_FORMAT_FLOW: record, err := DecodeFlowRecord(&recordHeader, recordReader) if err != nil { return sample, &FlowError{format, seq, fmt.Errorf("record [%w]", err)} } - if format == FORMAT_RAW_PKT { - flowSample.Records[i] = record - } else if format == FORMAT_IPV4 { - expandedFlowSample.Records[i] = record - } - } else if format == FORMAT_ETH || format == FORMAT_IPV6 { + flowSample.Records[i] = record + case SAMPLE_FORMAT_COUNTER, SAMPLE_FORMAT_EXPANDED_COUNTER: record, err := DecodeCounterRecord(&recordHeader, recordReader) if err != nil { return sample, &FlowError{format, seq, fmt.Errorf("counter [%w]", err)} } counterSample.Records[i] = record + case SAMPLE_FORMAT_EXPANDED_FLOW: + record, err := DecodeFlowRecord(&recordHeader, recordReader) + if err != nil { + return sample, &FlowError{format, seq, fmt.Errorf("record [%w]", err)} + } + expandedFlowSample.Records[i] = record } } return sample, nil diff --git a/decoders/sflow/sflow_test.go b/decoders/sflow/sflow_test.go index 7c4b773e..8c5335c2 100644 --- a/decoders/sflow/sflow_test.go +++ b/decoders/sflow/sflow_test.go @@ -23,19 +23,11 @@ func TestSFlowDecode(t *testing.T) { } buf := bytes.NewBuffer(data) var packet Packet - assert.Nil(t, DecodeMessageVersion(buf, &packet)) + assert.NoError(t, DecodeMessageVersion(buf, &packet)) } func TestExpandedSFlowDecode(t *testing.T) { - data := getExpandedSFlowDecode() - - buf := bytes.NewBuffer(data) - var packet Packet - assert.Nil(t, DecodeMessageVersion(buf, &packet)) -} - -func getExpandedSFlowDecode() []byte { - return []byte{ + data := []byte{ 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x03, 0x04, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xa7, 0x72, 0xc2, 0x0f, 0x76, 0x73, 0x48, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xdc, 0x20, 0x90, 0x93, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x42, 0xa4, @@ -131,4 +123,8 @@ func getExpandedSFlowDecode() []byte { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, } + + buf := bytes.NewBuffer(data) + var packet Packet + assert.NoError(t, DecodeMessageVersion(buf, &packet)) } From bd70d005f2149b7e56e03e08324a2fe238c5b82c Mon Sep 17 00:00:00 2001 From: Sven Rebhan Date: Wed, 29 May 2024 23:55:50 +0200 Subject: [PATCH 2/3] Remove deprecated constants --- decoders/sflow/sflow.go | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/decoders/sflow/sflow.go b/decoders/sflow/sflow.go index 453e7b22..e5995b09 100644 --- a/decoders/sflow/sflow.go +++ b/decoders/sflow/sflow.go @@ -45,20 +45,6 @@ const ( COUNTER_TYPE_CPU = 1001 ) -// Deprecated: These definitions are mixing sample-format, flow-data type and -// counter-data type definitions and are left here for compatibility only. -// -// Use the sample-format or flow/counter type definitions instead! -const ( - FORMAT_EXT_SWITCH = 1001 - FORMAT_EXT_ROUTER = 1002 - FORMAT_EXT_GATEWAY = 1003 - FORMAT_RAW_PKT = 1 - FORMAT_ETH = 2 - FORMAT_IPV4 = 3 - FORMAT_IPV6 = 4 -) - type DecoderError struct { Err error } From cab3240e2f4267cff9ce347fc553f5148ebadfea Mon Sep 17 00:00:00 2001 From: Sven Rebhan Date: Wed, 17 Jul 2024 12:28:10 +0200 Subject: [PATCH 3/3] feat(decoders.sflow): Add decoding of drop packets --- decoders/sflow/datastructure.go | 14 ++++++++ decoders/sflow/packet.go | 12 +++++++ decoders/sflow/sflow.go | 51 +++++++++++++++++++++++++-- decoders/sflow/sflow_test.go | 62 +++++++++++++++++++++++++++++++++ decoders/utils/utils.go | 9 +++++ 5 files changed, 146 insertions(+), 2 deletions(-) diff --git a/decoders/sflow/datastructure.go b/decoders/sflow/datastructure.go index e57573d9..817500e2 100644 --- a/decoders/sflow/datastructure.go +++ b/decoders/sflow/datastructure.go @@ -66,6 +66,20 @@ type ExtendedGateway struct { LocalPref uint32 `json:"local-pref"` } +type EgressQueue struct { + Queue uint32 `json:"queue"` +} + +type ExtendedACL struct { + Number uint32 `json:"number"` + Name string `json:"name"` + Direction uint32 `json:"direction"` // 0:unknown, 1:ingress, 2:egress +} + +type ExtendedFunction struct { + Symbol string `json:"symbol"` +} + type IfCounters struct { IfIndex uint32 `json:"if-index"` IfType uint32 `json:"if-type"` diff --git a/decoders/sflow/packet.go b/decoders/sflow/packet.go index 357a9041..bdd5cbb7 100644 --- a/decoders/sflow/packet.go +++ b/decoders/sflow/packet.go @@ -55,6 +55,18 @@ type ExpandedFlowSample struct { Records []FlowRecord `json:"records"` } +// DropSample data structure according to https://sflow.org/sflow_drops.txt +type DropSample struct { + Header SampleHeader `json:"header"` + + Drops uint32 `json:"drops"` + Input uint32 `json:"input"` + Output uint32 `json:"output"` + Reason uint32 `json:"reason"` + FlowRecordsCount uint32 `json:"flow-records-count"` + Records []FlowRecord `json:"records"` +} + type RecordHeader struct { DataFormat uint32 `json:"data-format"` Length uint32 `json:"length"` diff --git a/decoders/sflow/sflow.go b/decoders/sflow/sflow.go index e5995b09..bfbd3589 100644 --- a/decoders/sflow/sflow.go +++ b/decoders/sflow/sflow.go @@ -13,6 +13,7 @@ const ( SAMPLE_FORMAT_COUNTER = 2 SAMPLE_FORMAT_EXPANDED_FLOW = 3 SAMPLE_FORMAT_EXPANDED_COUNTER = 4 + SAMPLE_FORMAT_DROP = 5 ) // Opaque flow_data types according to https://sflow.org/SFLOW-STRUCTS5.txt @@ -33,6 +34,11 @@ const ( FLOW_TYPE_EXT_MPLS_FEC = 1010 FLOW_TYPE_EXT_MPLS_LVP_FEC = 1011 FLOW_TYPE_EXT_VLAN_TUNNEL = 1012 + + // According to https://sflow.org/sflow_drops.txt + FLOW_TYPE_EGRESS_QUEUE = 1036 + FLOW_TYPE_EXT_ACL = 1037 + FLOW_TYPE_EXT_FUNCTION = 1038 ) // Opaque counter_data types according to https://sflow.org/SFLOW-STRUCTS5.txt @@ -305,6 +311,24 @@ func DecodeFlowRecord(header *RecordHeader, payload *bytes.Buffer) (FlowRecord, extendedGateway.Communities = communities flowRecord.Data = extendedGateway + case FLOW_TYPE_EGRESS_QUEUE: + var queue EgressQueue + if err := utils.BinaryDecoder(payload, &queue.Queue); err != nil { + return flowRecord, &RecordError{header.DataFormat, err} + } + flowRecord.Data = queue + case FLOW_TYPE_EXT_ACL: + var acl ExtendedACL + if err := utils.BinaryDecoder(payload, &acl.Number, &acl.Name, &acl.Direction); err != nil { + return flowRecord, &RecordError{header.DataFormat, err} + } + flowRecord.Data = acl + case FLOW_TYPE_EXT_FUNCTION: + var function ExtendedFunction + if err := utils.BinaryDecoder(payload, &function.Symbol); err != nil { + return flowRecord, &RecordError{header.DataFormat, err} + } + flowRecord.Data = function default: var rawRecord RawRecord rawRecord.Data = payload.Bytes() @@ -330,10 +354,9 @@ func DecodeSample(header *SampleHeader, payload *bytes.Buffer) (interface{}, err if err := utils.BinaryDecoder(payload, &sourceId); err != nil { return sample, &FlowError{format, seq, fmt.Errorf("header source [%w]", err)} } - header.SourceIdType = sourceId >> 24 header.SourceIdValue = sourceId & 0x00ffffff - case SAMPLE_FORMAT_EXPANDED_FLOW, SAMPLE_FORMAT_EXPANDED_COUNTER: + case SAMPLE_FORMAT_EXPANDED_FLOW, SAMPLE_FORMAT_EXPANDED_COUNTER, SAMPLE_FORMAT_DROP: // Explicit data-source format if err := utils.BinaryDecoder(payload, &header.SourceIdType, @@ -349,6 +372,7 @@ func DecodeSample(header *SampleHeader, payload *bytes.Buffer) (interface{}, err var flowSample FlowSample var counterSample CounterSample var expandedFlowSample ExpandedFlowSample + var dropSample DropSample switch format { case SAMPLE_FORMAT_FLOW: flowSample.Header = *header @@ -396,6 +420,23 @@ func DecodeSample(header *SampleHeader, payload *bytes.Buffer) (interface{}, err recordsCount = expandedFlowSample.FlowRecordsCount expandedFlowSample.Records = make([]FlowRecord, recordsCount) sample = expandedFlowSample + case SAMPLE_FORMAT_DROP: + dropSample.Header = *header + if err := utils.BinaryDecoder(payload, + &dropSample.Drops, + &dropSample.Input, + &dropSample.Output, + &dropSample.Reason, + &dropSample.FlowRecordsCount, + ); err != nil { + return sample, &FlowError{format, seq, fmt.Errorf("raw [%w]", err)} + } + recordsCount = dropSample.FlowRecordsCount + if recordsCount > 1000 { // protection against ddos + return sample, &FlowError{format, seq, fmt.Errorf("too many flow records: %d", recordsCount)} + } + dropSample.Records = make([]FlowRecord, recordsCount) // max size of 1000 for protection + sample = dropSample } for i := 0; i < int(recordsCount) && payload.Len() >= 8; i++ { recordHeader := RecordHeader{} @@ -428,6 +469,12 @@ func DecodeSample(header *SampleHeader, payload *bytes.Buffer) (interface{}, err return sample, &FlowError{format, seq, fmt.Errorf("record [%w]", err)} } expandedFlowSample.Records[i] = record + case SAMPLE_FORMAT_DROP: + record, err := DecodeFlowRecord(&recordHeader, recordReader) + if err != nil { + return sample, &FlowError{format, seq, fmt.Errorf("record [%w]", err)} + } + dropSample.Records[i] = record } } return sample, nil diff --git a/decoders/sflow/sflow_test.go b/decoders/sflow/sflow_test.go index 8c5335c2..404f3a18 100644 --- a/decoders/sflow/sflow_test.go +++ b/decoders/sflow/sflow_test.go @@ -128,3 +128,65 @@ func TestExpandedSFlowDecode(t *testing.T) { var packet Packet assert.NoError(t, DecodeMessageVersion(buf, &packet)) } + +func TestSFlowDecodeDropEgressQueue(t *testing.T) { + data := []byte{ + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0xc0, 0xa8, 0x77, 0xb8, 0x00, 0x01, 0x86, 0xa0, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x30, 0x7e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x2a, + } + + buf := bytes.NewBuffer(data) + var packet Packet + assert.NoError(t, DecodeMessageVersion(buf, &packet)) + assert.Len(t, packet.Samples, 1) + assert.NotNil(t, packet.Samples[0]) + sample, ok := packet.Samples[0].(DropSample) + assert.True(t, ok) + assert.Len(t, sample.Records, 1) + assert.Equal(t, EgressQueue{Queue: 42}, sample.Records[0].Data) +} + +func TestSFlowDecodeDropExtendedACL(t *testing.T) { + data := []byte{ + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0xc0, 0xa8, 0x77, 0xb8, 0x00, 0x01, 0x86, 0xa0, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x30, 0x7e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x2a, + 0x00, 0x00, 0x00, 0x04, 0x66, 0x6f, 0x6f, 0x21, 0x00, 0x00, 0x00, 0x02, + } + + buf := bytes.NewBuffer(data) + var packet Packet + assert.NoError(t, DecodeMessageVersion(buf, &packet)) + assert.Len(t, packet.Samples, 1) + assert.NotNil(t, packet.Samples[0]) + sample, ok := packet.Samples[0].(DropSample) + assert.True(t, ok) + assert.Len(t, sample.Records, 1) + assert.Equal(t, ExtendedACL{Number: 42, Name: "foo!", Direction: 2}, sample.Records[0].Data) +} + +func TestSFlowDecodeDropExtendedFunction(t *testing.T) { + data := []byte{ + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0xc0, 0xa8, 0x77, 0xb8, 0x00, 0x01, 0x86, 0xa0, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x30, 0x7e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x06, + 0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72, + } + + buf := bytes.NewBuffer(data) + var packet Packet + assert.NoError(t, DecodeMessageVersion(buf, &packet)) + assert.Len(t, packet.Samples, 1) + assert.NotNil(t, packet.Samples[0]) + sample, ok := packet.Samples[0].(DropSample) + assert.True(t, ok) + assert.Len(t, sample.Records, 1) + assert.Equal(t, ExtendedFunction{Symbol: "foobar"}, sample.Records[0].Data) +} diff --git a/decoders/utils/utils.go b/decoders/utils/utils.go index ae2c7e75..79624806 100644 --- a/decoders/utils/utils.go +++ b/decoders/utils/utils.go @@ -48,6 +48,13 @@ func BinaryRead(payload BytesBuffer, order binary.ByteOrder, data any) error { *data = int64(order.Uint64(bs)) case *uint64: *data = order.Uint64(bs) + case *string: + strlen := int(order.Uint32(bs)) + buf := payload.Next(strlen) + if len(buf) < strlen { + return io.ErrUnexpectedEOF + } + *data = string(buf) case []bool: for i, x := range bs { // Easier to loop over the input for 8-bit values. data[i] = x != 0 @@ -121,6 +128,8 @@ func intDataSize(data any) int { return 2 * len(data) case int32, uint32, *int32, *uint32: return 4 + case *string: // return the length field + return 4 case []int32: return 4 * len(data) case []uint32: