Skip to content
This repository has been archived by the owner on Nov 27, 2018. It is now read-only.

Commit

Permalink
Better align with IPFIX terminology
Browse files Browse the repository at this point in the history
  • Loading branch information
calmh committed Jul 23, 2013
1 parent 1e9a57b commit 34d8be8
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 75 deletions.
4 changes: 2 additions & 2 deletions builtin-dictionary.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package ipfix
// Autogenerated Mon Jul 22 14:09:08 CEST 2013
var builtinDictionary = dictionary{
// Autogenerated Tue Jul 23 10:41:23 CEST 2013
var builtinDictionary = fieldDictionary{
dictionaryKey{0, 141}: DictionaryEntry{Name: "lineCardId", Type: "unsigned32"},
dictionaryKey{0, 142}: DictionaryEntry{Name: "portId", Type: "unsigned32"},
dictionaryKey{0, 10}: DictionaryEntry{Name: "ingressInterface", Type: "unsigned32"},
Expand Down
2 changes: 1 addition & 1 deletion etc/generate-builtin-dict.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

echo "package ipfix"
echo "// Autogenerated $(date)"
echo "var builtinDictionary = dictionary{"
echo "var builtinDictionary = fieldDictionary{"
curl -s http://www.ietf.org/rfc/rfc5102.txt | awk '
BEGIN { fields = 0 }
/^5\.[0-9]+\.[0-9]+/ { name=$2; fields++ }
Expand Down
10 changes: 5 additions & 5 deletions interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,10 @@ type dictionaryKey struct {
FieldId uint16
}

type dictionary map[dictionaryKey]DictionaryEntry
type fieldDictionary map[dictionaryKey]DictionaryEntry

// Interpret a raw DataSet into a map of field name => cooked value.
func (s *Session) Interpret(ds *DataSet) map[string]interface{} {
// Interpret a raw DataRecord into a map of field name => cooked value.
func (s *Session) Interpret(ds *DataRecord) map[string]interface{} {
set := make(map[string]interface{})
tpl := s.templates[ds.TemplateId]
if tpl == nil {
Expand All @@ -63,10 +63,10 @@ func (s *Session) Interpret(ds *DataSet) map[string]interface{} {
entry, ok := s.dictionary[dictionaryKey{field.EnterpriseId, field.FieldId}]
if !ok {
name = fmt.Sprintf("F[%d.%d]", field.EnterpriseId, field.FieldId)
value = integers(ds.Records[i])
value = integers(ds.Fields[i])
} else {
name = entry.Name
value = interpretBytes(ds.Records[i], entry.Type)
value = interpretBytes(ds.Fields[i], entry.Type)
}

set[name] = value
Expand Down
109 changes: 55 additions & 54 deletions parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ var ErrVersion = errors.New("incorrect version field in message header - out of
// A Message is the top level construct representing an IPFIX message. A well
// formed message contains one or more sets of data or template information.
type Message struct {
Header MessageHeader
DataSets []DataSet
TemplateSets []TemplateSet
Header MessageHeader
DataRecords []DataRecord
TemplateRecords []TemplateRecord
}

// The MessageHeader provides metadata for the entire Message. The sequence
Expand All @@ -42,40 +42,41 @@ type templateHeader struct {
FieldCount uint16
}

// The DataSet represents a single exported flow. The Records each describe
// The DataRecord represents a single exported flow. The Fields each describe
// different aspects of the flow (source and destination address, counters,
// service, etc.).
type DataSet struct {
type DataRecord struct {
TemplateId uint16
Records [][]byte
Fields [][]byte
}

// The TemplateSet describes a data template, as used by DataSets.
type TemplateSet struct {
TemplateId uint16
Records []TemplateRecord
// The TemplateRecord describes a data template, as used by DataRecords.
type TemplateRecord struct {
TemplateId uint16
FieldSpecifiers []TemplateFieldSpecifier
}

// The TemplateRecord describes the ID and size of the corresponding Records in a DataSet.
type TemplateRecord struct {
// The TemplateFieldSpecifier describes the ID and size of the corresponding
// Fields in a DataRecord.
type TemplateFieldSpecifier struct {
EnterpriseId uint32
FieldId uint16
Length uint16
}

// The Session is the context for IPFIX messages.
type Session struct {
templates [][]TemplateRecord
templates [][]TemplateFieldSpecifier
reader io.Reader
minRecord []uint16
dictionary dictionary
dictionary fieldDictionary
bytesRead int
}

// NewSession initializes a new Session based on the provided io.Reader.
func NewSession(reader io.Reader) *Session {
s := Session{}
s.templates = make([][]TemplateRecord, 65536)
s.templates = make([][]TemplateFieldSpecifier, 65536)
s.reader = reader
s.minRecord = make([]uint16, 65536)
s.dictionary = builtinDictionary
Expand Down Expand Up @@ -104,8 +105,8 @@ func (s *Session) ReadMessage() (msg *Message, err error) {

s.bytesRead = 0
msg = &Message{}
msg.DataSets = make([]DataSet, 0)
msg.TemplateSets = make([]TemplateSet, 0)
msg.DataRecords = make([]DataRecord, 0)
msg.TemplateRecords = make([]TemplateRecord, 0)

msgHdr := MessageHeader{}
err = binary.Read(s.reader, binary.BigEndian, &msgHdr)
Expand All @@ -117,9 +118,9 @@ func (s *Session) ReadMessage() (msg *Message, err error) {
msg.Header = msgHdr

for s.bytesRead < int(msgHdr.Length) {
tsets, dsets := s.readSet()
msg.TemplateSets = append(msg.TemplateSets, tsets...)
msg.DataSets = append(msg.DataSets, dsets...)
trecs, drecs := s.readSet()
msg.TemplateRecords = append(msg.TemplateRecords, trecs...)
msg.DataRecords = append(msg.DataRecords, drecs...)
}

return
Expand All @@ -131,9 +132,9 @@ func (s *Session) errorIf(err error) {
}
}

func (s *Session) readSet() ([]TemplateSet, []DataSet) {
tsets := make([]TemplateSet, 0)
dsets := make([]DataSet, 0)
func (s *Session) readSet() ([]TemplateRecord, []DataRecord) {
trecs := make([]TemplateRecord, 0)
drecs := make([]DataRecord, 0)

setHdr := setHeader{}
err := binary.Read(s.reader, binary.BigEndian, &setHdr)
Expand All @@ -150,83 +151,75 @@ func (s *Session) readSet() ([]TemplateSet, []DataSet) {
s.bytesRead = end
} else if setHdr.SetId == 2 {
// Template Set
ts := s.readTemplateSet()
tsets = append(tsets, *ts)
ts := s.readTemplateRecord()
trecs = append(trecs, *ts)

// Update the template cache
tid := ts.TemplateId
if len(ts.Records) == 0 {
if len(ts.FieldSpecifiers) == 0 {
// Set was withdrawn
s.templates[tid] = nil
} else {
s.templates[tid] = ts.Records
s.templates[tid] = ts.FieldSpecifiers
}

// Update the minimum record length cache
var minLength uint16
for i := range ts.Records {
if ts.Records[i].Length == 65535 {
for i := range ts.FieldSpecifiers {
if ts.FieldSpecifiers[i].Length == 65535 {
minLength += 1
} else {
minLength += ts.Records[i].Length
minLength += ts.FieldSpecifiers[i].Length
}
}
s.minRecord[tid] = minLength
} else if setHdr.SetId == 3 {
// Options Template Set, not handled
s.readFull(end - s.bytesRead)
s.readFixedLength(end - s.bytesRead)
} else {
if tpl := s.templates[setHdr.SetId]; tpl != nil {
// Data set
ds := s.readDataSet(tpl)
ds := s.readDataRecord(tpl)
ds.TemplateId = setHdr.SetId
dsets = append(dsets, *ds)
drecs = append(drecs, *ds)
} else {
// Data set with unknown template
s.readFull(end - s.bytesRead)
s.readFixedLength(end - s.bytesRead)
}
}
}

return tsets, dsets
return trecs, drecs
}

func (s *Session) readFull(n int) []byte {
bs := make([]byte, n)
_, err := io.ReadFull(s.reader, bs)
s.errorIf(err)
s.bytesRead += len(bs)
return bs
}

func (s *Session) readDataSet(tpl []TemplateRecord) *DataSet {
ds := DataSet{}
ds.Records = make([][]byte, len(tpl))
func (s *Session) readDataRecord(tpl []TemplateFieldSpecifier) *DataRecord {
ds := DataRecord{}
ds.Fields = make([][]byte, len(tpl))

for i := range tpl {
var bs []byte
if tpl[i].Length == 65535 {
bs = s.readVariableLength()
} else {
bs = s.readFull(int(tpl[i].Length))
bs = s.readFixedLength(int(tpl[i].Length))
}
ds.Records[i] = bs
ds.Fields[i] = bs
}

return &ds
}

func (s *Session) readTemplateSet() *TemplateSet {
ts := TemplateSet{}
func (s *Session) readTemplateRecord() *TemplateRecord {
ts := TemplateRecord{}
th := templateHeader{}
err := binary.Read(s.reader, binary.BigEndian, &th)
s.errorIf(err)
s.bytesRead += templateHeaderLength

ts.TemplateId = th.TemplateId
ts.Records = make([]TemplateRecord, th.FieldCount)
ts.FieldSpecifiers = make([]TemplateFieldSpecifier, th.FieldCount)
for i := 0; i < int(th.FieldCount); i++ {
f := TemplateRecord{}
f := TemplateFieldSpecifier{}
err = binary.Read(s.reader, binary.BigEndian, &f.FieldId)
s.errorIf(err)
s.bytesRead += 2
Expand All @@ -239,12 +232,20 @@ func (s *Session) readTemplateSet() *TemplateSet {
s.errorIf(err)
s.bytesRead += 4
}
ts.Records[i] = f
ts.FieldSpecifiers[i] = f
}

return &ts
}

func (s *Session) readFixedLength(n int) []byte {
bs := make([]byte, n)
_, err := io.ReadFull(s.reader, bs)
s.errorIf(err)
s.bytesRead += len(bs)
return bs
}

func (s *Session) readVariableLength() []byte {
var l int

Expand All @@ -263,5 +264,5 @@ func (s *Session) readVariableLength() []byte {
l = int(l1)
}

return s.readFull(l)
return s.readFixedLength(l)
}
26 changes: 13 additions & 13 deletions parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import (
"encoding/hex"
"fmt"
"github.com/calmh/ipfix"
"testing"
"io"
"testing"
)

func compare(t *testing.T, msg string, a, b interface{}) {
Expand Down Expand Up @@ -35,12 +35,12 @@ func TestParseTemplateSet(t *testing.T) {
}

compare(t, "msg.Header.Version", msg.Header.Version, uint(0xa))
compare(t, "len(DataSets)", len(msg.DataSets), 0)
compare(t, "len(TemplateSets)", len(msg.TemplateSets), 2)
compare(t, "TemplateSets[0].TemplateId",
msg.TemplateSets[0].TemplateId, uint16(10299))
compare(t, "TemplateSets[1].TemplateId",
msg.TemplateSets[1].TemplateId, uint16(49836))
compare(t, "len(DataRecords)", len(msg.DataRecords), 0)
compare(t, "len(TemplateRecords)", len(msg.TemplateRecords), 2)
compare(t, "TemplateRecords[0].TemplateId",
msg.TemplateRecords[0].TemplateId, uint16(10299))
compare(t, "TemplateRecords[1].TemplateId",
msg.TemplateRecords[1].TemplateId, uint16(49836))
}

func TestParseDataSet(t *testing.T) {
Expand All @@ -59,24 +59,24 @@ func TestParseDataSet(t *testing.T) {
t.Error("ReadMessage failed", err)
}

compare(t, "len(DataSets)", len(msg.DataSets), 0)
compare(t, "len(TemplateSets)", len(msg.TemplateSets), 0)
compare(t, "len(DataRecords)", len(msg.DataRecords), 0)
compare(t, "len(TemplateRecords)", len(msg.TemplateRecords), 0)

msg, err = p.ReadMessage()
if msg == nil || err != nil {
t.Error("ReadMessage failed", err)
}

compare(t, "len(DataSets)", len(msg.DataSets), 0)
compare(t, "len(TemplateSets)", len(msg.TemplateSets), 2)
compare(t, "len(DataRecords)", len(msg.DataRecords), 0)
compare(t, "len(TemplateRecords)", len(msg.TemplateRecords), 2)

msg, err = p.ReadMessage()
if msg == nil || err != nil {
t.Error("ReadMessage failed", err)
}

compare(t, "len(DataSets)", len(msg.DataSets), 31)
compare(t, "len(TemplateSets)", len(msg.TemplateSets), 0)
compare(t, "len(DataRecords)", len(msg.DataRecords), 31)
compare(t, "len(TemplateRecords)", len(msg.TemplateRecords), 0)
}

func TestEOFError(t *testing.T) {
Expand Down

0 comments on commit 34d8be8

Please sign in to comment.