Skip to content

Commit

Permalink
decoders: replace binary.Read with a version not using reflection
Browse files Browse the repository at this point in the history
  • Loading branch information
vincentbernat committed Jan 16, 2023
1 parent 3326554 commit 7e5362b
Show file tree
Hide file tree
Showing 4 changed files with 176 additions and 6 deletions.
2 changes: 1 addition & 1 deletion decoders/netflow/netflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ func DecodeMessage(payload *bytes.Buffer, templates NetFlowTemplateSystem) (inte

for i := 0; ((i < int(size) && version == 9) || version == 10) && payload.Len() > 0; i++ {
fsheader := FlowSetHeader{}
if err := utils.BinaryDecoder(payload, &fsheader); err != nil {
if err := utils.BinaryDecoder(payload, &fsheader.Id, &fsheader.Length); err != nil {
return returnItem, fmt.Errorf("Error decoding FlowSet header: %v", err)
}

Expand Down
23 changes: 22 additions & 1 deletion decoders/netflowlegacy/netflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,28 @@ func DecodeMessage(payload *bytes.Buffer) (interface{}, error) {
packet.Records = make([]RecordsNetFlowV5, int(packet.Count))
for i := 0; i < int(packet.Count) && payload.Len() >= 48; i++ {
record := RecordsNetFlowV5{}
err := utils.BinaryDecoder(payload, &record)
err := utils.BinaryDecoder(payload,
&record.SrcAddr,
&record.DstAddr,
&record.NextHop,
&record.Input,
&record.Output,
&record.DPkts,
&record.DOctets,
&record.First,
&record.Last,
&record.SrcPort,
&record.DstPort,
&record.Pad1,
&record.TCPFlags,
&record.Proto,
&record.Tos,
&record.SrcAS,
&record.DstAS,
&record.SrcMask,
&record.DstMask,
&record.Pad2,
)
if err != nil {
return packet, err
}
Expand Down
40 changes: 37 additions & 3 deletions decoders/sflow/sflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,48 @@ func DecodeCounterRecord(header *RecordHeader, payload *bytes.Buffer) (CounterRe
switch (*header).DataFormat {
case 1:
ifCounters := IfCounters{}
err := utils.BinaryDecoder(payload, &ifCounters)
err := utils.BinaryDecoder(payload,
&ifCounters.IfIndex,
&ifCounters.IfType,
&ifCounters.IfSpeed,
&ifCounters.IfDirection,
&ifCounters.IfStatus,
&ifCounters.IfInOctets,
&ifCounters.IfInUcastPkts,
&ifCounters.IfInMulticastPkts,
&ifCounters.IfInBroadcastPkts,
&ifCounters.IfInDiscards,
&ifCounters.IfInErrors,
&ifCounters.IfInUnknownProtos,
&ifCounters.IfOutOctets,
&ifCounters.IfOutUcastPkts,
&ifCounters.IfOutMulticastPkts,
&ifCounters.IfOutBroadcastPkts,
&ifCounters.IfOutDiscards,
&ifCounters.IfOutErrors,
&ifCounters.IfPromiscuousMode,
)
if err != nil {
return counterRecord, err
}
counterRecord.Data = ifCounters
case 2:
ethernetCounters := EthernetCounters{}
err := utils.BinaryDecoder(payload, &ethernetCounters)
err := utils.BinaryDecoder(payload,
&ethernetCounters.Dot3StatsAlignmentErrors,
&ethernetCounters.Dot3StatsFCSErrors,
&ethernetCounters.Dot3StatsSingleCollisionFrames,
&ethernetCounters.Dot3StatsMultipleCollisionFrames,
&ethernetCounters.Dot3StatsSQETestErrors,
&ethernetCounters.Dot3StatsDeferredTransmissions,
&ethernetCounters.Dot3StatsLateCollisions,
&ethernetCounters.Dot3StatsExcessiveCollisions,
&ethernetCounters.Dot3StatsInternalMacTransmitErrors,
&ethernetCounters.Dot3StatsCarrierSenseErrors,
&ethernetCounters.Dot3StatsFrameTooLongs,
&ethernetCounters.Dot3StatsInternalMacReceiveErrors,
&ethernetCounters.Dot3StatsSymbolErrors,
)
if err != nil {
return counterRecord, err
}
Expand Down Expand Up @@ -132,7 +166,7 @@ func DecodeFlowRecord(header *RecordHeader, payload *bytes.Buffer) (FlowRecord,
switch (*header).DataFormat {
case FORMAT_EXT_SWITCH:
extendedSwitch := ExtendedSwitch{}
err := utils.BinaryDecoder(payload, &extendedSwitch)
err := utils.BinaryDecoder(payload, &(extendedSwitch.SrcVlan), &(extendedSwitch.SrcPriority), &(extendedSwitch.DstVlan), &(extendedSwitch.DstPriority))
if err != nil {
return flowRecord, err
}
Expand Down
117 changes: 116 additions & 1 deletion decoders/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,130 @@ package utils

import (
"encoding/binary"
"errors"
"io"
"reflect"
)

func BinaryDecoder(payload io.Reader, dests ...interface{}) error {
for _, dest := range dests {
err := binary.Read(payload, binary.BigEndian, dest)
err := binaryRead(payload, binary.BigEndian, dest)
if err != nil {
return err
}
}
return nil
}

func binaryRead(r io.Reader, order binary.ByteOrder, data any) error {
// Fast path for basic types and slices.
if n := intDataSize(data); n != 0 {
bs := make([]byte, n)
if _, err := io.ReadFull(r, bs); err != nil {
return err
}
switch data := data.(type) {
case *bool:
*data = bs[0] != 0
case *int8:
*data = int8(bs[0])
case *uint8:
*data = bs[0]
case *int16:
*data = int16(order.Uint16(bs))
case *uint16:
*data = order.Uint16(bs)
case *int32:
*data = int32(order.Uint32(bs))
case *uint32:
*data = order.Uint32(bs)
case *int64:
*data = int64(order.Uint64(bs))
case *uint64:
*data = order.Uint64(bs)
case []bool:
for i, x := range bs { // Easier to loop over the input for 8-bit values.
data[i] = x != 0
}
case []int8:
for i, x := range bs {
data[i] = int8(x)
}
case []uint8:
copy(data, bs)
case []int16:
for i := range data {
data[i] = int16(order.Uint16(bs[2*i:]))
}
case []uint16:
for i := range data {
data[i] = order.Uint16(bs[2*i:])
}
case []int32:
for i := range data {
data[i] = int32(order.Uint32(bs[4*i:]))
}
case []uint32:
for i := range data {
data[i] = order.Uint32(bs[4*i:])
}
case []int64:
for i := range data {
data[i] = int64(order.Uint64(bs[8*i:]))
}
case []uint64:
for i := range data {
data[i] = order.Uint64(bs[8*i:])
}
default:
n = 0 // fast path doesn't apply
}
if n != 0 {
return nil
}
}

return errors.New("binary.Read: invalid type " + reflect.TypeOf(data).String())
}

// intDataSize returns the size of the data required to represent the data when encoded.
// It returns zero if the type cannot be implemented by the fast path in Read or Write.
func intDataSize(data any) int {
switch data := data.(type) {
case bool, int8, uint8, *bool, *int8, *uint8:
return 1
case []bool:
return len(data)
case []int8:
return len(data)
case []uint8:
return len(data)
case int16, uint16, *int16, *uint16:
return 2
case []int16:
return 2 * len(data)
case []uint16:
return 2 * len(data)
case int32, uint32, *int32, *uint32:
return 4
case []int32:
return 4 * len(data)
case []uint32:
return 4 * len(data)
case int64, uint64, *int64, *uint64:
return 8
case []int64:
return 8 * len(data)
case []uint64:
return 8 * len(data)
case float32, *float32:
return 4
case float64, *float64:
return 8
case []float32:
return 4 * len(data)
case []float64:
return 8 * len(data)
}
return 0
}

0 comments on commit 7e5362b

Please sign in to comment.