Skip to content

Commit e171a04

Browse files
authored
feat(decoders.sflow): Add decoding of drop packets (#337)
1 parent b19557c commit e171a04

File tree

5 files changed

+147
-2
lines changed

5 files changed

+147
-2
lines changed

decoders/sflow/datastructure.go

+14
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,20 @@ type ExtendedGateway struct {
6666
LocalPref uint32 `json:"local-pref"`
6767
}
6868

69+
type EgressQueue struct {
70+
Queue uint32 `json:"queue"`
71+
}
72+
73+
type ExtendedACL struct {
74+
Number uint32 `json:"number"`
75+
Name string `json:"name"`
76+
Direction uint32 `json:"direction"` // 0:unknown, 1:ingress, 2:egress
77+
}
78+
79+
type ExtendedFunction struct {
80+
Symbol string `json:"symbol"`
81+
}
82+
6983
type IfCounters struct {
7084
IfIndex uint32 `json:"if-index"`
7185
IfType uint32 `json:"if-type"`

decoders/sflow/packet.go

+12
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,18 @@ type ExpandedFlowSample struct {
5555
Records []FlowRecord `json:"records"`
5656
}
5757

58+
// DropSample data structure according to https://sflow.org/sflow_drops.txt
59+
type DropSample struct {
60+
Header SampleHeader `json:"header"`
61+
62+
Drops uint32 `json:"drops"`
63+
Input uint32 `json:"input"`
64+
Output uint32 `json:"output"`
65+
Reason uint32 `json:"reason"`
66+
FlowRecordsCount uint32 `json:"flow-records-count"`
67+
Records []FlowRecord `json:"records"`
68+
}
69+
5870
type RecordHeader struct {
5971
DataFormat uint32 `json:"data-format"`
6072
Length uint32 `json:"length"`

decoders/sflow/sflow.go

+50-2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const (
1313
SAMPLE_FORMAT_COUNTER = 2
1414
SAMPLE_FORMAT_EXPANDED_FLOW = 3
1515
SAMPLE_FORMAT_EXPANDED_COUNTER = 4
16+
SAMPLE_FORMAT_DROP = 5
1617
)
1718

1819
// Opaque flow_data types according to https://sflow.org/SFLOW-STRUCTS5.txt
@@ -33,6 +34,11 @@ const (
3334
FLOW_TYPE_EXT_MPLS_FEC = 1010
3435
FLOW_TYPE_EXT_MPLS_LVP_FEC = 1011
3536
FLOW_TYPE_EXT_VLAN_TUNNEL = 1012
37+
38+
// According to https://sflow.org/sflow_drops.txt
39+
FLOW_TYPE_EGRESS_QUEUE = 1036
40+
FLOW_TYPE_EXT_ACL = 1037
41+
FLOW_TYPE_EXT_FUNCTION = 1038
3642
)
3743

3844
// Opaque counter_data types according to https://sflow.org/SFLOW-STRUCTS5.txt
@@ -319,6 +325,24 @@ func DecodeFlowRecord(header *RecordHeader, payload *bytes.Buffer) (FlowRecord,
319325
extendedGateway.Communities = communities
320326

321327
flowRecord.Data = extendedGateway
328+
case FLOW_TYPE_EGRESS_QUEUE:
329+
var queue EgressQueue
330+
if err := utils.BinaryDecoder(payload, &queue.Queue); err != nil {
331+
return flowRecord, &RecordError{header.DataFormat, err}
332+
}
333+
flowRecord.Data = queue
334+
case FLOW_TYPE_EXT_ACL:
335+
var acl ExtendedACL
336+
if err := utils.BinaryDecoder(payload, &acl.Number, &acl.Name, &acl.Direction); err != nil {
337+
return flowRecord, &RecordError{header.DataFormat, err}
338+
}
339+
flowRecord.Data = acl
340+
case FLOW_TYPE_EXT_FUNCTION:
341+
var function ExtendedFunction
342+
if err := utils.BinaryDecoder(payload, &function.Symbol); err != nil {
343+
return flowRecord, &RecordError{header.DataFormat, err}
344+
}
345+
flowRecord.Data = function
322346
default:
323347
var rawRecord RawRecord
324348
rawRecord.Data = payload.Bytes()
@@ -344,10 +368,9 @@ func DecodeSample(header *SampleHeader, payload *bytes.Buffer) (interface{}, err
344368
if err := utils.BinaryDecoder(payload, &sourceId); err != nil {
345369
return sample, &FlowError{format, seq, fmt.Errorf("header source [%w]", err)}
346370
}
347-
348371
header.SourceIdType = sourceId >> 24
349372
header.SourceIdValue = sourceId & 0x00ffffff
350-
case SAMPLE_FORMAT_EXPANDED_FLOW, SAMPLE_FORMAT_EXPANDED_COUNTER:
373+
case SAMPLE_FORMAT_EXPANDED_FLOW, SAMPLE_FORMAT_EXPANDED_COUNTER, SAMPLE_FORMAT_DROP:
351374
// Explicit data-source format
352375
if err := utils.BinaryDecoder(payload,
353376
&header.SourceIdType,
@@ -363,6 +386,8 @@ func DecodeSample(header *SampleHeader, payload *bytes.Buffer) (interface{}, err
363386
var flowSample FlowSample
364387
var counterSample CounterSample
365388
var expandedFlowSample ExpandedFlowSample
389+
var dropSample DropSample
390+
366391
switch format {
367392
case SAMPLE_FORMAT_FLOW:
368393
flowSample.Header = *header
@@ -410,6 +435,23 @@ func DecodeSample(header *SampleHeader, payload *bytes.Buffer) (interface{}, err
410435
recordsCount = expandedFlowSample.FlowRecordsCount
411436
expandedFlowSample.Records = make([]FlowRecord, recordsCount)
412437
sample = expandedFlowSample
438+
case SAMPLE_FORMAT_DROP:
439+
dropSample.Header = *header
440+
if err := utils.BinaryDecoder(payload,
441+
&dropSample.Drops,
442+
&dropSample.Input,
443+
&dropSample.Output,
444+
&dropSample.Reason,
445+
&dropSample.FlowRecordsCount,
446+
); err != nil {
447+
return sample, &FlowError{format, seq, fmt.Errorf("raw [%w]", err)}
448+
}
449+
recordsCount = dropSample.FlowRecordsCount
450+
if recordsCount > 1000 { // protection against ddos
451+
return sample, &FlowError{format, seq, fmt.Errorf("too many flow records: %d", recordsCount)}
452+
}
453+
dropSample.Records = make([]FlowRecord, recordsCount) // max size of 1000 for protection
454+
sample = dropSample
413455
}
414456
for i := 0; i < int(recordsCount) && payload.Len() >= 8; i++ {
415457
recordHeader := RecordHeader{}
@@ -442,6 +484,12 @@ func DecodeSample(header *SampleHeader, payload *bytes.Buffer) (interface{}, err
442484
return sample, &FlowError{format, seq, fmt.Errorf("record [%w]", err)}
443485
}
444486
expandedFlowSample.Records[i] = record
487+
case SAMPLE_FORMAT_DROP:
488+
record, err := DecodeFlowRecord(&recordHeader, recordReader)
489+
if err != nil {
490+
return sample, &FlowError{format, seq, fmt.Errorf("record [%w]", err)}
491+
}
492+
dropSample.Records[i] = record
445493
}
446494
}
447495
return sample, nil

decoders/sflow/sflow_test.go

+62
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,65 @@ func TestExpandedSFlowDecode(t *testing.T) {
128128
var packet Packet
129129
assert.NoError(t, DecodeMessageVersion(buf, &packet))
130130
}
131+
132+
func TestSFlowDecodeDropEgressQueue(t *testing.T) {
133+
data := []byte{
134+
0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0xc0, 0xa8, 0x77, 0xb8, 0x00, 0x01, 0x86, 0xa0,
135+
0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x30, 0x7e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05,
136+
0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
137+
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01,
138+
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x2a,
139+
}
140+
141+
buf := bytes.NewBuffer(data)
142+
var packet Packet
143+
assert.NoError(t, DecodeMessageVersion(buf, &packet))
144+
assert.Len(t, packet.Samples, 1)
145+
assert.NotNil(t, packet.Samples[0])
146+
sample, ok := packet.Samples[0].(DropSample)
147+
assert.True(t, ok)
148+
assert.Len(t, sample.Records, 1)
149+
assert.Equal(t, EgressQueue{Queue: 42}, sample.Records[0].Data)
150+
}
151+
152+
func TestSFlowDecodeDropExtendedACL(t *testing.T) {
153+
data := []byte{
154+
0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0xc0, 0xa8, 0x77, 0xb8, 0x00, 0x01, 0x86, 0xa0,
155+
0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x30, 0x7e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05,
156+
0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
157+
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01,
158+
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x2a,
159+
0x00, 0x00, 0x00, 0x04, 0x66, 0x6f, 0x6f, 0x21, 0x00, 0x00, 0x00, 0x02,
160+
}
161+
162+
buf := bytes.NewBuffer(data)
163+
var packet Packet
164+
assert.NoError(t, DecodeMessageVersion(buf, &packet))
165+
assert.Len(t, packet.Samples, 1)
166+
assert.NotNil(t, packet.Samples[0])
167+
sample, ok := packet.Samples[0].(DropSample)
168+
assert.True(t, ok)
169+
assert.Len(t, sample.Records, 1)
170+
assert.Equal(t, ExtendedACL{Number: 42, Name: "foo!", Direction: 2}, sample.Records[0].Data)
171+
}
172+
173+
func TestSFlowDecodeDropExtendedFunction(t *testing.T) {
174+
data := []byte{
175+
0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0xc0, 0xa8, 0x77, 0xb8, 0x00, 0x01, 0x86, 0xa0,
176+
0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x30, 0x7e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05,
177+
0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
178+
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01,
179+
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x06,
180+
0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72,
181+
}
182+
183+
buf := bytes.NewBuffer(data)
184+
var packet Packet
185+
assert.NoError(t, DecodeMessageVersion(buf, &packet))
186+
assert.Len(t, packet.Samples, 1)
187+
assert.NotNil(t, packet.Samples[0])
188+
sample, ok := packet.Samples[0].(DropSample)
189+
assert.True(t, ok)
190+
assert.Len(t, sample.Records, 1)
191+
assert.Equal(t, ExtendedFunction{Symbol: "foobar"}, sample.Records[0].Data)
192+
}

decoders/utils/utils.go

+9
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,13 @@ func BinaryRead(payload BytesBuffer, order binary.ByteOrder, data any) error {
4848
*data = int64(order.Uint64(bs))
4949
case *uint64:
5050
*data = order.Uint64(bs)
51+
case *string:
52+
strlen := int(order.Uint32(bs))
53+
buf := payload.Next(strlen)
54+
if len(buf) < strlen {
55+
return io.ErrUnexpectedEOF
56+
}
57+
*data = string(buf)
5158
case []bool:
5259
for i, x := range bs { // Easier to loop over the input for 8-bit values.
5360
data[i] = x != 0
@@ -121,6 +128,8 @@ func intDataSize(data any) int {
121128
return 2 * len(data)
122129
case int32, uint32, *int32, *uint32:
123130
return 4
131+
case *string: // return the length field
132+
return 4
124133
case []int32:
125134
return 4 * len(data)
126135
case []uint32:

0 commit comments

Comments
 (0)