Skip to content

Commit c563693

Browse files
authored
Implements reduced-size encoding per rfc7011
Implements reduced-size encoding per https://tools.ietf.org/html/rfc7011#section-6.2. Reduced-size encoding allows an ipfix exporter to send a field using a smaller number of bytes if the exporter knows it won't need to exceed the range provided by the smaller byte size.
2 parents 3a562cb + ec70a9f commit c563693

File tree

2 files changed

+369
-19
lines changed

2 files changed

+369
-19
lines changed

translate/translate.go

Lines changed: 117 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package translate
44

55
import (
66
"encoding/binary"
7+
"errors"
78
"math"
89
"net"
910
"time"
@@ -89,12 +90,16 @@ func (t FieldType) minLength() int {
8990
case Uint8, Int8, Boolean:
9091
return 1
9192
case Uint16, Int16:
92-
return 2
93-
case Uint32, Int32, Float32, DateTimeSeconds:
93+
return 1
94+
case Uint32, Int32:
95+
return 1
96+
case Float32, DateTimeSeconds:
9497
return 4
9598
case Uint64, Int64:
96-
return 4 // NetFlow v9 encodes in both 4 and 8 bytes
97-
case Float64, DateTimeMilliseconds, DateTimeMicroseconds, DateTimeNanoseconds:
99+
return 1
100+
case Float64:
101+
return 4 // Float64 can be encoded in 4 bytes though loss of precision may occur
102+
case DateTimeMilliseconds, DateTimeMicroseconds, DateTimeNanoseconds:
98103
return 8
99104
case MacAddress:
100105
return 6
@@ -129,6 +134,86 @@ type Key struct {
129134

130135
type informationElements map[Key]InformationElementEntry
131136

137+
var reducedSizeErr error = errors.New("Unable to read reduced size encoding: size not implemented")
138+
var tooManyBitsErr error = errors.New("Unable to read reduced size encoding: too many bits")
139+
140+
// Helper method to read an unsigned reduced size field
141+
func reducedSizeReadUnsigned(bs []byte, maxBits int) (uint64, error) {
142+
// Exit if `bs` has more bits than we can store
143+
if len(bs)*8 > maxBits {
144+
return 0, tooManyBitsErr
145+
}
146+
147+
switch len(bs) {
148+
case 1:
149+
return uint64(bs[0]), nil
150+
case 2:
151+
return uint64(binary.BigEndian.Uint16(bs)), nil
152+
case 3:
153+
return uint64(uint32(bs[0])<<16 + uint32(bs[1])<<8 + uint32(bs[2])), nil
154+
case 4:
155+
return uint64(binary.BigEndian.Uint32(bs)), nil
156+
case 8:
157+
return binary.BigEndian.Uint64(bs), nil
158+
}
159+
return 0, reducedSizeErr
160+
}
161+
162+
// Helper method to read a signed reduced size field
163+
func reducedSizeReadSigned(bs []byte, maxBits int) (int64, error) {
164+
// Exit if `bs` has more bits than we can store
165+
if len(bs)*8 > maxBits {
166+
return 0, tooManyBitsErr
167+
}
168+
169+
switch len(bs) {
170+
case 1:
171+
value := int8(bs[0])
172+
return int64(value), nil
173+
case 2:
174+
value := int16(binary.BigEndian.Uint16(bs))
175+
return int64(value), nil
176+
case 4:
177+
value := int32(binary.BigEndian.Uint32(bs))
178+
return int64(value), nil
179+
case 8:
180+
return int64(binary.BigEndian.Uint64(bs)), nil
181+
}
182+
return 0, reducedSizeErr
183+
}
184+
185+
// Read a reduced size field into its full size
186+
func reducedSizeRead(bs []byte, i interface{}) error {
187+
var unsigned uint64
188+
var signed int64
189+
var err error
190+
191+
switch v := i.(type) {
192+
case *uint16:
193+
unsigned, err = reducedSizeReadUnsigned(bs, 16)
194+
*v = uint16(unsigned)
195+
case *uint32:
196+
unsigned, err = reducedSizeReadUnsigned(bs, 32)
197+
*v = uint32(unsigned)
198+
case *uint64:
199+
unsigned, err = reducedSizeReadUnsigned(bs, 64)
200+
*v = uint64(unsigned)
201+
case *int16:
202+
signed, err = reducedSizeReadSigned(bs, 16)
203+
*v = int16(signed)
204+
case *int32:
205+
signed, err = reducedSizeReadSigned(bs, 32)
206+
*v = int32(signed)
207+
case *int64:
208+
signed, err = reducedSizeReadSigned(bs, 64)
209+
*v = int64(signed)
210+
default:
211+
err = reducedSizeErr
212+
}
213+
214+
return err
215+
}
216+
132217
// Bytes translates a byte string to a go native type.
133218
func Bytes(bs []byte, t FieldType) interface{} {
134219
if len(bs) < t.minLength() {
@@ -140,33 +225,46 @@ func Bytes(bs []byte, t FieldType) interface{} {
140225
case Uint8:
141226
return bs[0]
142227
case Uint16:
143-
return binary.BigEndian.Uint16(bs)
228+
var i uint16
229+
if err := reducedSizeRead(bs, &i); err == nil {
230+
return i
231+
}
144232
case Uint32:
145-
return binary.BigEndian.Uint32(bs)
233+
var i uint32
234+
if err := reducedSizeRead(bs, &i); err == nil {
235+
return i
236+
}
146237
case Uint64:
147-
switch len(bs) {
148-
case 4:
149-
return uint64(binary.BigEndian.Uint32(bs))
150-
case 8:
151-
return binary.BigEndian.Uint64(bs)
238+
var i uint64
239+
if err := reducedSizeRead(bs, &i); err == nil {
240+
return i
152241
}
153242
case Int8:
154243
return int8(bs[0])
155244
case Int16:
156-
return int16(binary.BigEndian.Uint16(bs))
245+
var i int16
246+
if err := reducedSizeRead(bs, &i); err == nil {
247+
return i
248+
}
157249
case Int32:
158-
return int32(binary.BigEndian.Uint32(bs))
250+
var i int32
251+
if err := reducedSizeRead(bs, &i); err == nil {
252+
return i
253+
}
159254
case Int64:
160-
switch len(bs) {
161-
case 4:
162-
return int64(binary.BigEndian.Uint32(bs))
163-
case 8:
164-
return int64(binary.BigEndian.Uint64(bs))
255+
var i int64
256+
if err := reducedSizeRead(bs, &i); err == nil {
257+
return i
165258
}
166259
case Float32:
167260
return math.Float32frombits(binary.BigEndian.Uint32(bs))
168261
case Float64:
169-
return math.Float64frombits(binary.BigEndian.Uint64(bs))
262+
switch len(bs) {
263+
case 4:
264+
return float64(math.Float32frombits(binary.BigEndian.Uint32(bs)))
265+
case 8:
266+
return math.Float64frombits(binary.BigEndian.Uint64(bs))
267+
}
170268
case Boolean:
171269
return bs[0] == 1
172270
case Unknown, OctetArray:

0 commit comments

Comments
 (0)