This repository has been archived by the owner on Nov 27, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 26
/
Copy pathinterpreter.go
266 lines (238 loc) · 6.94 KB
/
interpreter.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
package ipfix
import (
"crypto/md5"
"encoding/binary"
"fmt"
"math"
"net"
"os"
"time"
)
// Interpreter provides translation between the raw bytes of a DataRecord
// and the actual values as specified by the corresponding template.
type Interpreter struct {
dictionary fieldDictionary
session *Session
}
// FieldType is the IPFIX type of an Information Element ("Field").
type FieldType int
// The available field types as defined by RFC 5102.
const (
Unknown FieldType = iota
Uint8
Uint16
Uint32
Uint64
Int8
Int16
Int32
Int64
Float32
Float64
Boolean
MacAddress
OctetArray
String
DateTimeSeconds
DateTimeMilliseconds
DateTimeMicroseconds
DateTimeNanoseconds
Ipv4Address
Ipv6Address
)
// FieldTypes maps string representations of field types into their
// corresponding FieldType value.
var FieldTypes = map[string]FieldType{
"unsigned8": Uint8,
"unsigned16": Uint16,
"unsigned32": Uint32,
"unsigned64": Uint64,
"signed8": Int8,
"signed16": Int16,
"signed32": Int32,
"signed64": Int64,
"float32": Float32,
"float64": Float64,
"boolean": Boolean,
"macAddress": MacAddress,
"octetArray": OctetArray,
"string": String,
"dateTimeSeconds": DateTimeSeconds,
"dateTimeMilliseconds": DateTimeMilliseconds,
"dateTimeMicroseconds": DateTimeMicroseconds,
"dateTimeNanoseconds": DateTimeNanoseconds,
"ipv4Address": Ipv4Address,
"ipv6Address": Ipv6Address,
}
// minLength is the minimum length of a field of the given type, in bytes.
func (t FieldType) minLength() int {
switch t {
case Uint8, Int8, Boolean, Uint16, Int16, Uint32, Int32, Uint64, Int64:
return 1 // all integers can be reduced-size encoded
case Float32, DateTimeSeconds:
return 4
case Float64, DateTimeMilliseconds, DateTimeMicroseconds, DateTimeNanoseconds:
return 8
case MacAddress:
return 6
case Ipv4Address:
return 4
case Ipv6Address:
return 16
default:
return 0
}
}
// DictionaryEntry provides a mapping between an (Enterprise, Field) pair and
// a Name and Type.
type DictionaryEntry struct {
Name string
FieldID uint16
EnterpriseID uint32
Type FieldType
}
func (f *FieldType) UnmarshalText(bs []byte) error {
*f = FieldTypes[string(bs)]
return nil
}
type dictionaryKey struct {
EnterpriseID uint32
FieldID uint16
}
type fieldDictionary map[dictionaryKey]DictionaryEntry
// An InterpretedField is a field with the field name filled in and the value
// converted to the appropriate type. If this is not possible (because the
// name and type of the field is unknown at the time of interpretation), Name
// will be the empty string, Value will be a nil interface and RawValue will
// contain the original bytes.
type InterpretedField struct {
Name string
EnterpriseID uint32
FieldID uint16
Value interface{}
RawValue []byte
}
// An InterpretedTemplateFieldSpecifier is a template specifier with the field
// name filled in, if found in the dictionary.
type InterpretedTemplateFieldSpecifier struct {
Name string
TemplateFieldSpecifier
}
// NewInterpreter craets a new Interpreter based on the specified Session.
func NewInterpreter(s *Session) *Interpreter {
return &Interpreter{builtinDictionary, s}
}
// Interpret a raw DataRecord into a list of InterpretedFields.
func (i *Interpreter) Interpret(rec DataRecord) []InterpretedField {
return i.InterpretInto(rec, nil)
}
// InterpretInto interprets a raw DataRecord into an existing slice of
// InterpretedFields. If the slice is not long enough it will be reallocated.
func (i *Interpreter) InterpretInto(rec DataRecord, fieldList []InterpretedField) []InterpretedField {
tpl := i.session.lookupTemplateFieldSpecifiers(rec.TemplateID)
if tpl == nil {
return nil
}
if len(fieldList) < len(tpl) {
fieldList = make([]InterpretedField, len(tpl))
} else {
fieldList = fieldList[:len(tpl)]
}
for j, field := range tpl {
fieldList[j].FieldID = field.FieldID
fieldList[j].EnterpriseID = field.EnterpriseID
if entry, ok := i.dictionary[dictionaryKey{field.EnterpriseID, field.FieldID}]; ok {
fieldList[j].Name = entry.Name
fieldList[j].Value = interpretBytes(&rec.Fields[j], entry.Type)
} else {
fieldList[j].RawValue = rec.Fields[j]
}
}
return fieldList
}
// InterpretTemplate interprets a template record and adds a name to the interpreted fields, if
// a given {EnterpriseID,FieldID} can be find in the dictionary.
func (i *Interpreter) InterpretTemplate(rec TemplateRecord) []InterpretedTemplateFieldSpecifier {
fieldList := make([]InterpretedTemplateFieldSpecifier, len(rec.FieldSpecifiers))
for j, field := range rec.FieldSpecifiers {
fieldList[j].TemplateFieldSpecifier = field
if entry, ok := i.dictionary[dictionaryKey{field.EnterpriseID, field.FieldID}]; ok {
fieldList[j].Name = entry.Name
}
}
return fieldList
}
// AddDictionaryEntry adds a DictionaryEntry (containing a vendor field) to
// the dictionary used by Interpret.
func (i *Interpreter) AddDictionaryEntry(e DictionaryEntry) {
i.dictionary[dictionaryKey{e.EnterpriseID, e.FieldID}] = e
}
var md5HashSalt = []byte(os.Getenv("IPFIX_IP_HASH"))
func interpretBytes(bs *[]byte, t FieldType) interface{} {
if len(*bs) < t.minLength() {
// Field is too short (corrupt) - return it uninterpreted.
return *bs
}
switch t {
case Ipv4Address, Ipv6Address:
if len(md5HashSalt) > 0 {
h := md5.New()
h.Write(md5HashSalt)
h.Write(*bs)
return fmt.Sprintf("%x", h.Sum(nil))
}
return (*net.IP)(bs)
case Uint8:
return uint8(number(*bs))
case Uint16:
return uint16(number(*bs))
case Uint32:
return uint32(number(*bs))
case Uint64:
return uint64(number(*bs))
case Int8:
return int8(number(*bs))
case Int16:
return int16(number(*bs))
case Int32:
return int32(number(*bs))
case Int64:
return int64(number(*bs))
case Float32:
return math.Float32frombits(binary.BigEndian.Uint32(*bs))
case Float64:
return math.Float64frombits(binary.BigEndian.Uint64(*bs))
case Boolean:
return (*bs)[0] == 1
case Unknown, MacAddress, OctetArray:
return *bs
case String:
return string(*bs)
case DateTimeSeconds:
return time.Unix(int64(binary.BigEndian.Uint32(*bs)), 0)
case DateTimeMilliseconds:
unixTimeMs := int64(binary.BigEndian.Uint64(*bs))
return time.Unix(0, 0).Add(time.Duration(unixTimeMs) * time.Millisecond)
case DateTimeMicroseconds:
unixTimeUs := int64(binary.BigEndian.Uint64(*bs))
return time.Unix(0, 0).Add(time.Duration(unixTimeUs) * time.Microsecond)
case DateTimeNanoseconds:
unixTimeNs := int64(binary.BigEndian.Uint64(*bs))
return time.Unix(0, 0).Add(time.Duration(unixTimeNs))
}
return *bs
}
func number(bs []byte) uint64 {
switch len(bs) {
case 1:
return uint64(bs[0])
case 2:
return uint64(binary.BigEndian.Uint16(bs))
case 4:
return uint64(binary.BigEndian.Uint32(bs))
case 8:
return uint64(binary.BigEndian.Uint64(bs))
default:
return 0
}
}