Skip to content
This repository was archived by the owner on Feb 19, 2025. It is now read-only.

Commit 1b90677

Browse files
author
b.koshenov
committed
decoders/netflow: add variable-length fields parsing support
Signed-off-by: b.koshenov <[email protected]>
1 parent 56d241c commit 1b90677

File tree

3 files changed

+77
-18
lines changed

3 files changed

+77
-18
lines changed

decoders/netflow/netflow.go

+47-14
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package netflow
22

33
import (
44
"bytes"
5+
"encoding/binary"
56
"fmt"
67
"io"
78
"sync"
@@ -164,18 +165,28 @@ func GetTemplateSize(template []Field) int {
164165

165166
// DecodeDataRecordFields decodes the fields(type and value) of DataRecord.
166167
func DecodeDataRecordFields(payload *bytes.Buffer, listFields []Field, record *[]DataField) error {
167-
size := GetTemplateSize(listFields)
168-
if payload.Len() < size {
169-
return fmt.Errorf("decode dataset: there are fewer than %d bytes in the buffer", size)
170-
}
171-
172168
*record = (*record)[:cap(*record)]
173169
if len(*record) < len(listFields) {
174170
*record = append(*record, make([]DataField, len(listFields)-len(*record))...)
175171
}
176172

173+
var ok bool
177174
for i, templateField := range listFields {
178-
value := payload.Next(int(templateField.Length))
175+
176+
l := int(templateField.Length)
177+
178+
if templateField.IsVariableLength() {
179+
if l, ok = getVariableLength(payload); !ok {
180+
return fmt.Errorf("decode DataRecordFields: invalid variable-length: %d", l)
181+
}
182+
}
183+
184+
// XXX: Retaining a slice returned by Next() may be unsafe according to
185+
// method's documentation as it may be invalidated by future Read call.
186+
value := payload.Next(l)
187+
if len(value) < l {
188+
return fmt.Errorf("decode dataset: there are fewer than %d bytes in the buffer", l)
189+
}
179190

180191
(*record)[i].Type = templateField.Type
181192
(*record)[i].Value = value
@@ -184,6 +195,31 @@ func DecodeDataRecordFields(payload *bytes.Buffer, listFields []Field, record *[
184195
return nil
185196
}
186197

198+
func getVariableLength(payload *bytes.Buffer) (int, bool) {
199+
b, err := payload.ReadByte()
200+
if err != nil {
201+
return 0, false
202+
}
203+
204+
// RFC 7011 Sec. 7.Variable-Length Information Element.
205+
if b != 0xff {
206+
return int(b), true
207+
}
208+
209+
// RFC 7011 Sec. 7.Variable-Length Information Element:
210+
// The length may also be encoded into 3 octets before the Information
211+
// Element, allowing the length of the Information Element to be greater
212+
// than or equal to 255 octets. In this case, the first octet of the Length
213+
// field MUST be 255, and the length is carried in the second and third
214+
// octets.
215+
length := payload.Next(2)
216+
if len(length) < 2 {
217+
return 0, false
218+
}
219+
220+
return int(binary.BigEndian.Uint16(length)), true
221+
}
222+
187223
type ErrorTemplateNotFound struct {
188224
version uint16
189225
obsDomainId uint32
@@ -281,24 +317,19 @@ func DecodeOptionsDataSet(payload *bytes.Buffer, fs *OptionsDataFlowSet, listFie
281317
func DecodeDataSet(payload *bytes.Buffer, listFields []Field, flowSet *DataFlowSet) error {
282318
flowSet.Records = flowSet.Records[:cap(flowSet.Records)]
283319

284-
listFieldsSize := GetTemplateSize(listFields)
285-
286320
i := 0
287-
for payload.Len() >= listFieldsSize {
288-
payloadLim := bytes.NewBuffer(payload.Next(listFieldsSize))
289-
321+
for payload.Len() > 0 {
290322
if i >= len(flowSet.Records) {
291323
flowSet.Records = append(flowSet.Records, DataRecord{})
292324
}
293325

294326
datafields := &flowSet.Records[i].Values
295-
if err := DecodeDataRecordFields(payloadLim, listFields, datafields); err != nil {
327+
if err := DecodeDataRecordFields(payload, listFields, datafields); err != nil {
296328
return err
297329
}
298330

299331
i++
300332
}
301-
302333
flowSet.Records = flowSet.Records[:i]
303334
return nil
304335
}
@@ -533,7 +564,9 @@ func (f *FlowMessage) DecodeIPFIXPacket(payload *bytes.Buffer, templates NetFlow
533564
)
534565

535566
for i := 0; i < int(f.PacketIPFIX.Length) && payload.Len() > 0; i++ {
536-
f.fsheader.ReadFrom(payload)
567+
if ok := f.fsheader.ReadFrom(payload); !ok {
568+
return NewErrorDecodingNetFlow("error decoding packet: invalid FlowSet header.")
569+
}
537570

538571
nextrelpos := int(f.fsheader.Length) - flowSetHeaderSize
539572
if nextrelpos < 0 {

decoders/netflow/netflow_test.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -222,10 +222,10 @@ func TestDecodeDataSet(t *testing.T) {
222222
fs := DataFlowSet{}
223223

224224
// type and length of a single value in a netflow Data Record
225-
nfListFields := []Field{{8, 4}, {225, 4}, {12, 4}, {226, 4}, {7, 2}, {227, 2}, {11, 2}, {228, 2}, {234, 4}, {4, 1}, {230, 1}, {323, 8}}
225+
nfListFields := []Field{{8, 4, 0}, {225, 4, 0}, {12, 4, 0}, {226, 4, 0}, {7, 2, 0}, {227, 2, 0}, {11, 2, 0}, {228, 2, 0}, {234, 4, 0}, {4, 1, 0}, {230, 1, 0}, {323, 8, 0}}
226226

227227
// type and length of a single value in ipfix Flow Data Record
228-
ipxListFields := []Field{{8, 4}, {12, 4}, {5, 1}, {4, 1}, {7, 2}, {11, 2}, {32, 2}, {10, 4}, {16, 4}, {17, 4}, {18, 4}, {14, 4}, {1, 4}, {2, 4}, {22, 4}, {21, 4}, {15, 4}, {9, 1}, {13, 1}, {6, 1}, {60, 1}, {152, 8}, {153, 8}}
228+
ipxListFields := []Field{{8, 4, 0}, {12, 4, 0}, {5, 1, 0}, {4, 1, 0}, {7, 2, 0}, {11, 2, 0}, {32, 2, 0}, {10, 4, 0}, {16, 4, 0}, {17, 4, 0}, {18, 4, 0}, {14, 4, 0}, {1, 4, 0}, {2, 4, 0}, {22, 4, 0}, {21, 4, 0}, {15, 4, 0}, {9, 1, 0}, {13, 1, 0}, {6, 1, 0}, {60, 1, 0}, {152, 8, 0}, {153, 8, 0}}
229229

230230
// netflow data flowset
231231
nfData := bytes.NewBuffer(netflowTestPackets[1].Data[210:])
@@ -245,10 +245,10 @@ func TestDecodeDataSet(t *testing.T) {
245245

246246
func TestDecodeDataSetUsingFields(t *testing.T) {
247247
// type and length of a single value in a netflow Data Record
248-
nfListFields := []Field{{8, 4}, {225, 4}, {12, 4}, {226, 4}, {7, 2}, {227, 2}, {11, 2}, {228, 2}, {234, 4}, {4, 1}, {230, 1}, {323, 8}}
248+
nfListFields := []Field{{8, 4, 0}, {225, 4, 0}, {12, 4, 0}, {226, 4, 0}, {7, 2, 0}, {227, 2, 0}, {11, 2, 0}, {228, 2, 0}, {234, 4, 0}, {4, 1, 0}, {230, 1, 0}, {323, 8, 0}}
249249

250250
// type and length of a single value in ipfix Flow Data Record
251-
ipxListFields := []Field{{8, 4}, {12, 4}, {5, 1}, {4, 1}, {7, 2}, {11, 2}, {32, 2}, {10, 4}, {16, 4}, {17, 4}, {18, 4}, {14, 4}, {1, 4}, {2, 4}, {22, 4}, {21, 4}, {15, 4}, {9, 1}, {13, 1}, {6, 1}, {60, 1}, {152, 8}, {153, 8}}
251+
ipxListFields := []Field{{8, 4, 0}, {12, 4, 0}, {5, 1, 0}, {4, 1, 0}, {7, 2, 0}, {11, 2, 0}, {32, 2, 0}, {10, 4, 0}, {16, 4, 0}, {17, 4, 0}, {18, 4, 0}, {14, 4, 0}, {1, 4, 0}, {2, 4, 0}, {22, 4, 0}, {21, 4, 0}, {15, 4, 0}, {9, 1, 0}, {13, 1, 0}, {6, 1, 0}, {60, 1, 0}, {152, 8, 0}, {153, 8, 0}}
252252

253253
dr := DataRecord{}
254254

decoders/netflow/packet.go

+26
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,24 @@ type Field struct {
114114

115115
// The length (in bytes) of the field.
116116
Length uint16
117+
118+
// IANA private enterprise number (PEN) of the authority defining the
119+
// Information Element identifier in this Template Record.
120+
//
121+
// Note: it's valid only for v10(IPFIX).
122+
EnterpriseNumber uint32
123+
}
124+
125+
// IsVariableLength reports whether the Field is variable-length Information
126+
// Element (RFC 7011 sec. 3.2. Field Specifier Format).
127+
func (f *Field) IsVariableLength() bool {
128+
return f.Length == 0xffff
129+
}
130+
131+
// ContainsEnterpriseNumber reports whether the Field is an enterprise-specific
132+
// Information Element (RFC 7011 sec. 3.2. Field Specifier Format).
133+
func (f *Field) ContainsEnterpriseNumber() bool {
134+
return f.Type&0x8000 != 0
117135
}
118136

119137
// DataField contains the type and record value itself.
@@ -161,9 +179,17 @@ func (f *Field) ReadFrom(b *bytes.Buffer) bool {
161179
if ok := utils.ReadUint16FromBuffer(b, &f.Type); !ok {
162180
return false
163181
}
182+
164183
if ok := utils.ReadUint16FromBuffer(b, &f.Length); !ok {
165184
return false
166185
}
186+
187+
if f.ContainsEnterpriseNumber() {
188+
if ok := utils.ReadUint32FromBuffer(b, &f.EnterpriseNumber); !ok {
189+
return false
190+
}
191+
}
192+
167193
return true
168194
}
169195

0 commit comments

Comments
 (0)