| // Copyright 2014 Google, Inc. All rights reserved. |
| // |
| // Use of this source code is governed by a BSD-style license |
| // that can be found in the LICENSE file in the root of the source |
| // tree. |
| |
| /* |
| This layer decodes SFlow version 5 datagrams. |
| |
| The specification can be found here: http://sflow.org/sflow_version_5.txt |
| |
| Additional developer information about sflow can be found at: |
| http://sflow.org/developers/specifications.php |
| |
| And SFlow in general: |
| http://sflow.org/index.php |
| |
| Two forms of sample data are defined: compact and expanded. The |
| Specification has this to say: |
| |
| Compact and expand forms of counter and flow samples are defined. |
| An agent must not mix compact/expanded encodings. If an agent |
| will never use ifIndex numbers >= 2^24 then it must use compact |
| encodings for all interfaces. Otherwise the expanded formats must |
| be used for all interfaces. |
| |
| This decoder only supports the compact form, because that is the only |
| one for which data was avaialble. |
| |
| The datagram is composed of one or more samples of type flow or counter, |
| and each sample is composed of one or more records describing the sample. |
| A sample is a single instance of sampled inforamtion, and each record in |
| the sample gives additional / supplimentary information about the sample. |
| |
| The following sample record types are supported: |
| |
| Raw Packet Header |
| opaque = flow_data; enterprise = 0; format = 1 |
| |
| Extended Switch Data |
| opaque = flow_data; enterprise = 0; format = 1001 |
| |
| Extended Router Data |
| opaque = flow_data; enterprise = 0; format = 1002 |
| |
| Extended Gateway Data |
| opaque = flow_data; enterprise = 0; format = 1003 |
| |
| Extended User Data |
| opaque = flow_data; enterprise = 0; format = 1004 |
| |
| Extended URL Data |
| opaque = flow_data; enterprise = 0; format = 1005 |
| |
| The following types of counter records are supported: |
| |
| Generic Interface Counters - see RFC 2233 |
| opaque = counter_data; enterprise = 0; format = 1 |
| |
| Ethernet Interface Counters - see RFC 2358 |
| opaque = counter_data; enterprise = 0; format = 2 |
| |
| SFlow is encoded using XDR (RFC4506). There are a few places |
| where the standard 4-byte fields are partitioned into two |
| bitfields of different lengths. I'm not sure why the designers |
| chose to pack together two values like this in some places, and |
| in others they use the entire 4-byte value to store a number that |
| will never be more than a few bits. In any case, there are a couple |
| of types defined to handle the decoding of these bitfields, and |
| that's why they're there. */ |
| |
| package layers |
| |
| import ( |
| "encoding/binary" |
| "errors" |
| "fmt" |
| "net" |
| |
| "github.com/google/gopacket" |
| ) |
| |
| // SFlowRecord holds both flow sample records and counter sample records. |
| // A Record is the structure that actually holds the sampled data |
| // and / or counters. |
| type SFlowRecord interface { |
| } |
| |
| // SFlowDataSource encodes a 2-bit SFlowSourceFormat in its most significant |
| // 2 bits, and an SFlowSourceValue in its least significant 30 bits. |
| // These types and values define the meaning of the inteface information |
| // presented in the sample metadata. |
| type SFlowDataSource int32 |
| |
| func (sdc SFlowDataSource) decode() (SFlowSourceFormat, SFlowSourceValue) { |
| leftField := sdc >> 30 |
| rightField := uint32(0x3FFFFFFF) & uint32(sdc) |
| return SFlowSourceFormat(leftField), SFlowSourceValue(rightField) |
| } |
| |
| type SFlowDataSourceExpanded struct { |
| SourceIDClass SFlowSourceFormat |
| SourceIDIndex SFlowSourceValue |
| } |
| |
| func (sdce SFlowDataSourceExpanded) decode() (SFlowSourceFormat, SFlowSourceValue) { |
| leftField := sdce.SourceIDClass >> 30 |
| rightField := uint32(0x3FFFFFFF) & uint32(sdce.SourceIDIndex) |
| return SFlowSourceFormat(leftField), SFlowSourceValue(rightField) |
| } |
| |
| type SFlowSourceFormat uint32 |
| |
| type SFlowSourceValue uint32 |
| |
| const ( |
| SFlowTypeSingleInterface SFlowSourceFormat = 0 |
| SFlowTypePacketDiscarded SFlowSourceFormat = 1 |
| SFlowTypeMultipleDestinations SFlowSourceFormat = 2 |
| ) |
| |
| func (sdf SFlowSourceFormat) String() string { |
| switch sdf { |
| case SFlowTypeSingleInterface: |
| return "Single Interface" |
| case SFlowTypePacketDiscarded: |
| return "Packet Discarded" |
| case SFlowTypeMultipleDestinations: |
| return "Multiple Destinations" |
| default: |
| return "UNKNOWN" |
| } |
| } |
| |
| func decodeSFlow(data []byte, p gopacket.PacketBuilder) error { |
| s := &SFlowDatagram{} |
| err := s.DecodeFromBytes(data, p) |
| if err != nil { |
| return err |
| } |
| p.AddLayer(s) |
| p.SetApplicationLayer(s) |
| return nil |
| } |
| |
| // SFlowDatagram is the outermost container which holds some basic information |
| // about the reporting agent, and holds at least one sample record |
| type SFlowDatagram struct { |
| BaseLayer |
| |
| DatagramVersion uint32 |
| AgentAddress net.IP |
| SubAgentID uint32 |
| SequenceNumber uint32 |
| AgentUptime uint32 |
| SampleCount uint32 |
| FlowSamples []SFlowFlowSample |
| CounterSamples []SFlowCounterSample |
| } |
| |
| // An SFlow datagram's outer container has the following |
| // structure: |
| |
| // 0 15 31 |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | int sFlow version (2|4|5) | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | int IP version of the Agent (1=v4|2=v6) | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // / Agent IP address (v4=4byte|v6=16byte) / |
| // / / |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | int sub agent id | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | int datagram sequence number | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | int switch uptime in ms | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | int n samples in datagram | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // / n samples / |
| // / / |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| |
| // SFlowDataFormat encodes the EnterpriseID in the most |
| // significant 12 bits, and the SampleType in the least significant |
| // 20 bits. |
| type SFlowDataFormat uint32 |
| |
| func (sdf SFlowDataFormat) decode() (SFlowEnterpriseID, SFlowSampleType) { |
| leftField := sdf >> 12 |
| rightField := uint32(0xFFF) & uint32(sdf) |
| return SFlowEnterpriseID(leftField), SFlowSampleType(rightField) |
| } |
| |
| // SFlowEnterpriseID is used to differentiate between the |
| // official SFlow standard, and other, vendor-specific |
| // types of flow data. (Similiar to SNMP's enterprise MIB |
| // OIDs) Only the office SFlow Enterprise ID is decoded |
| // here. |
| type SFlowEnterpriseID uint32 |
| |
| const ( |
| SFlowStandard SFlowEnterpriseID = 0 |
| ) |
| |
| func (eid SFlowEnterpriseID) String() string { |
| switch eid { |
| case SFlowStandard: |
| return "Standard SFlow" |
| default: |
| return "" |
| } |
| } |
| |
| func (eid SFlowEnterpriseID) GetType() SFlowEnterpriseID { |
| return SFlowStandard |
| } |
| |
| // SFlowSampleType specifies the type of sample. Only flow samples |
| // and counter samples are supported |
| type SFlowSampleType uint32 |
| |
| const ( |
| SFlowTypeFlowSample SFlowSampleType = 1 |
| SFlowTypeCounterSample SFlowSampleType = 2 |
| SFlowTypeExpandedFlowSample SFlowSampleType = 3 |
| SFlowTypeExpandedCounterSample SFlowSampleType = 4 |
| ) |
| |
| func (st SFlowSampleType) GetType() SFlowSampleType { |
| switch st { |
| case SFlowTypeFlowSample: |
| return SFlowTypeFlowSample |
| case SFlowTypeCounterSample: |
| return SFlowTypeCounterSample |
| case SFlowTypeExpandedFlowSample: |
| return SFlowTypeExpandedFlowSample |
| case SFlowTypeExpandedCounterSample: |
| return SFlowTypeExpandedCounterSample |
| default: |
| panic("Invalid Sample Type") |
| } |
| } |
| |
| func (st SFlowSampleType) String() string { |
| switch st { |
| case SFlowTypeFlowSample: |
| return "Flow Sample" |
| case SFlowTypeCounterSample: |
| return "Counter Sample" |
| case SFlowTypeExpandedFlowSample: |
| return "Expanded Flow Sample" |
| case SFlowTypeExpandedCounterSample: |
| return "Expanded Counter Sample" |
| default: |
| return "" |
| } |
| } |
| |
| func (s *SFlowDatagram) LayerType() gopacket.LayerType { return LayerTypeSFlow } |
| |
| func (d *SFlowDatagram) Payload() []byte { return nil } |
| |
| func (d *SFlowDatagram) CanDecode() gopacket.LayerClass { return LayerTypeSFlow } |
| |
| func (d *SFlowDatagram) NextLayerType() gopacket.LayerType { return gopacket.LayerTypePayload } |
| |
| // SFlowIPType determines what form the IP address being decoded will |
| // take. This is an XDR union type allowing for both IPv4 and IPv6 |
| type SFlowIPType uint32 |
| |
| const ( |
| SFlowIPv4 SFlowIPType = 1 |
| SFlowIPv6 SFlowIPType = 2 |
| ) |
| |
| func (s SFlowIPType) String() string { |
| switch s { |
| case SFlowIPv4: |
| return "IPv4" |
| case SFlowIPv6: |
| return "IPv6" |
| default: |
| return "" |
| } |
| } |
| |
| func (s SFlowIPType) Length() int { |
| switch s { |
| case SFlowIPv4: |
| return 4 |
| case SFlowIPv6: |
| return 16 |
| default: |
| return 0 |
| } |
| } |
| |
| func (s *SFlowDatagram) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { |
| var agentAddressType SFlowIPType |
| |
| data, s.DatagramVersion = data[4:], binary.BigEndian.Uint32(data[:4]) |
| data, agentAddressType = data[4:], SFlowIPType(binary.BigEndian.Uint32(data[:4])) |
| data, s.AgentAddress = data[agentAddressType.Length():], data[:agentAddressType.Length()] |
| data, s.SubAgentID = data[4:], binary.BigEndian.Uint32(data[:4]) |
| data, s.SequenceNumber = data[4:], binary.BigEndian.Uint32(data[:4]) |
| data, s.AgentUptime = data[4:], binary.BigEndian.Uint32(data[:4]) |
| data, s.SampleCount = data[4:], binary.BigEndian.Uint32(data[:4]) |
| |
| if s.SampleCount < 1 { |
| return fmt.Errorf("SFlow Datagram has invalid sample length: %d", s.SampleCount) |
| } |
| for i := uint32(0); i < s.SampleCount; i++ { |
| sdf := SFlowDataFormat(binary.BigEndian.Uint32(data[:4])) |
| _, sampleType := sdf.decode() |
| switch sampleType { |
| case SFlowTypeFlowSample: |
| if flowSample, err := decodeFlowSample(&data, false); err == nil { |
| s.FlowSamples = append(s.FlowSamples, flowSample) |
| } else { |
| return err |
| } |
| case SFlowTypeCounterSample: |
| if counterSample, err := decodeCounterSample(&data, false); err == nil { |
| s.CounterSamples = append(s.CounterSamples, counterSample) |
| } else { |
| return err |
| } |
| case SFlowTypeExpandedFlowSample: |
| if flowSample, err := decodeFlowSample(&data, true); err == nil { |
| s.FlowSamples = append(s.FlowSamples, flowSample) |
| } else { |
| return err |
| } |
| case SFlowTypeExpandedCounterSample: |
| if counterSample, err := decodeCounterSample(&data, true); err == nil { |
| s.CounterSamples = append(s.CounterSamples, counterSample) |
| } else { |
| return err |
| } |
| |
| default: |
| return fmt.Errorf("Unsupported SFlow sample type %d", sampleType) |
| } |
| } |
| return nil |
| } |
| |
| // SFlowFlowSample represents a sampled packet and contains |
| // one or more records describing the packet |
| type SFlowFlowSample struct { |
| EnterpriseID SFlowEnterpriseID |
| Format SFlowSampleType |
| SampleLength uint32 |
| SequenceNumber uint32 |
| SourceIDClass SFlowSourceFormat |
| SourceIDIndex SFlowSourceValue |
| SamplingRate uint32 |
| SamplePool uint32 |
| Dropped uint32 |
| InputInterfaceFormat uint32 |
| InputInterface uint32 |
| OutputInterfaceFormat uint32 |
| OutputInterface uint32 |
| RecordCount uint32 |
| Records []SFlowRecord |
| } |
| |
| // Flow samples have the following structure. Note |
| // the bit fields to encode the Enterprise ID and the |
| // Flow record format: type 1 |
| |
| // 0 15 31 |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | 20 bit Interprise (0) |12 bit format | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | sample length | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | int sample sequence number | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // |id type | src id index value | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | int sampling rate | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | int sample pool | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | int drops | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | int input ifIndex | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | int output ifIndex | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | int number of records | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // / flow records / |
| // / / |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| |
| // Flow samples have the following structure. |
| // Flow record format: type 3 |
| |
| // 0 15 31 |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | 20 bit Interprise (0) |12 bit format | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | sample length | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | int sample sequence number | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | int src id type | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | int src id index value | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | int sampling rate | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | int sample pool | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | int drops | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | int input interface format | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | int input interface value | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | int output interface format | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | int output interface value | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | int number of records | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // / flow records / |
| // / / |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| |
| type SFlowFlowDataFormat uint32 |
| |
| func (fdf SFlowFlowDataFormat) decode() (SFlowEnterpriseID, SFlowFlowRecordType) { |
| leftField := fdf >> 12 |
| rightField := uint32(0xFFF) & uint32(fdf) |
| return SFlowEnterpriseID(leftField), SFlowFlowRecordType(rightField) |
| } |
| |
| func (fs SFlowFlowSample) GetRecords() []SFlowRecord { |
| return fs.Records |
| } |
| |
| func (fs SFlowFlowSample) GetType() SFlowSampleType { |
| return SFlowTypeFlowSample |
| } |
| |
| func skipRecord(data *[]byte) { |
| recordLength := int(binary.BigEndian.Uint32((*data)[4:])) |
| *data = (*data)[(recordLength+((4-recordLength)%4))+8:] |
| } |
| |
| func decodeFlowSample(data *[]byte, expanded bool) (SFlowFlowSample, error) { |
| s := SFlowFlowSample{} |
| var sdf SFlowDataFormat |
| *data, sdf = (*data)[4:], SFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) |
| var sdc SFlowDataSource |
| |
| s.EnterpriseID, s.Format = sdf.decode() |
| *data, s.SampleLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, s.SequenceNumber = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| if expanded { |
| *data, s.SourceIDClass = (*data)[4:], SFlowSourceFormat(binary.BigEndian.Uint32((*data)[:4])) |
| *data, s.SourceIDIndex = (*data)[4:], SFlowSourceValue(binary.BigEndian.Uint32((*data)[:4])) |
| } else { |
| *data, sdc = (*data)[4:], SFlowDataSource(binary.BigEndian.Uint32((*data)[:4])) |
| s.SourceIDClass, s.SourceIDIndex = sdc.decode() |
| } |
| *data, s.SamplingRate = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, s.SamplePool = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, s.Dropped = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| |
| if expanded { |
| *data, s.InputInterfaceFormat = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, s.InputInterface = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, s.OutputInterfaceFormat = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, s.OutputInterface = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| } else { |
| *data, s.InputInterface = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, s.OutputInterface = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| } |
| *data, s.RecordCount = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| |
| for i := uint32(0); i < s.RecordCount; i++ { |
| rdf := SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) |
| enterpriseID, flowRecordType := rdf.decode() |
| |
| // Try to decode when EnterpriseID is 0 signaling |
| // default sflow structs are used according specification |
| // Unexpected behavior detected for e.g. with pmacct |
| if enterpriseID == 0 { |
| switch flowRecordType { |
| case SFlowTypeRawPacketFlow: |
| if record, err := decodeRawPacketFlowRecord(data); err == nil { |
| s.Records = append(s.Records, record) |
| } else { |
| return s, err |
| } |
| case SFlowTypeExtendedUserFlow: |
| if record, err := decodeExtendedUserFlow(data); err == nil { |
| s.Records = append(s.Records, record) |
| } else { |
| return s, err |
| } |
| case SFlowTypeExtendedUrlFlow: |
| if record, err := decodeExtendedURLRecord(data); err == nil { |
| s.Records = append(s.Records, record) |
| } else { |
| return s, err |
| } |
| case SFlowTypeExtendedSwitchFlow: |
| if record, err := decodeExtendedSwitchFlowRecord(data); err == nil { |
| s.Records = append(s.Records, record) |
| } else { |
| return s, err |
| } |
| case SFlowTypeExtendedRouterFlow: |
| if record, err := decodeExtendedRouterFlowRecord(data); err == nil { |
| s.Records = append(s.Records, record) |
| } else { |
| return s, err |
| } |
| case SFlowTypeExtendedGatewayFlow: |
| if record, err := decodeExtendedGatewayFlowRecord(data); err == nil { |
| s.Records = append(s.Records, record) |
| } else { |
| return s, err |
| } |
| case SFlowTypeEthernetFrameFlow: |
| if record, err := decodeEthernetFrameFlowRecord(data); err == nil { |
| s.Records = append(s.Records, record) |
| } else { |
| return s, err |
| } |
| case SFlowTypeIpv4Flow: |
| if record, err := decodeSFlowIpv4Record(data); err == nil { |
| s.Records = append(s.Records, record) |
| } else { |
| return s, err |
| } |
| case SFlowTypeIpv6Flow: |
| if record, err := decodeSFlowIpv6Record(data); err == nil { |
| s.Records = append(s.Records, record) |
| } else { |
| return s, err |
| } |
| case SFlowTypeExtendedMlpsFlow: |
| // TODO |
| skipRecord(data) |
| return s, errors.New("skipping TypeExtendedMlpsFlow") |
| case SFlowTypeExtendedNatFlow: |
| // TODO |
| skipRecord(data) |
| return s, errors.New("skipping TypeExtendedNatFlow") |
| case SFlowTypeExtendedMlpsTunnelFlow: |
| // TODO |
| skipRecord(data) |
| return s, errors.New("skipping TypeExtendedMlpsTunnelFlow") |
| case SFlowTypeExtendedMlpsVcFlow: |
| // TODO |
| skipRecord(data) |
| return s, errors.New("skipping TypeExtendedMlpsVcFlow") |
| case SFlowTypeExtendedMlpsFecFlow: |
| // TODO |
| skipRecord(data) |
| return s, errors.New("skipping TypeExtendedMlpsFecFlow") |
| case SFlowTypeExtendedMlpsLvpFecFlow: |
| // TODO |
| skipRecord(data) |
| return s, errors.New("skipping TypeExtendedMlpsLvpFecFlow") |
| case SFlowTypeExtendedVlanFlow: |
| // TODO |
| skipRecord(data) |
| return s, errors.New("skipping TypeExtendedVlanFlow") |
| case SFlowTypeExtendedIpv4TunnelEgressFlow: |
| if record, err := decodeExtendedIpv4TunnelEgress(data); err == nil { |
| s.Records = append(s.Records, record) |
| } else { |
| return s, err |
| } |
| case SFlowTypeExtendedIpv4TunnelIngressFlow: |
| if record, err := decodeExtendedIpv4TunnelIngress(data); err == nil { |
| s.Records = append(s.Records, record) |
| } else { |
| return s, err |
| } |
| case SFlowTypeExtendedIpv6TunnelEgressFlow: |
| if record, err := decodeExtendedIpv6TunnelEgress(data); err == nil { |
| s.Records = append(s.Records, record) |
| } else { |
| return s, err |
| } |
| case SFlowTypeExtendedIpv6TunnelIngressFlow: |
| if record, err := decodeExtendedIpv6TunnelIngress(data); err == nil { |
| s.Records = append(s.Records, record) |
| } else { |
| return s, err |
| } |
| case SFlowTypeExtendedDecapsulateEgressFlow: |
| if record, err := decodeExtendedDecapsulateEgress(data); err == nil { |
| s.Records = append(s.Records, record) |
| } else { |
| return s, err |
| } |
| case SFlowTypeExtendedDecapsulateIngressFlow: |
| if record, err := decodeExtendedDecapsulateIngress(data); err == nil { |
| s.Records = append(s.Records, record) |
| } else { |
| return s, err |
| } |
| case SFlowTypeExtendedVniEgressFlow: |
| if record, err := decodeExtendedVniEgress(data); err == nil { |
| s.Records = append(s.Records, record) |
| } else { |
| return s, err |
| } |
| case SFlowTypeExtendedVniIngressFlow: |
| if record, err := decodeExtendedVniIngress(data); err == nil { |
| s.Records = append(s.Records, record) |
| } else { |
| return s, err |
| } |
| default: |
| return s, fmt.Errorf("Unsupported flow record type: %d", flowRecordType) |
| } |
| } else { |
| skipRecord(data) |
| } |
| } |
| return s, nil |
| } |
| |
| // Counter samples report information about various counter |
| // objects. Typically these are items like IfInOctets, or |
| // CPU / Memory stats, etc. SFlow will report these at regular |
| // intervals as configured on the agent. If one were sufficiently |
| // industrious, this could be used to replace the typical |
| // SNMP polling used for such things. |
| type SFlowCounterSample struct { |
| EnterpriseID SFlowEnterpriseID |
| Format SFlowSampleType |
| SampleLength uint32 |
| SequenceNumber uint32 |
| SourceIDClass SFlowSourceFormat |
| SourceIDIndex SFlowSourceValue |
| RecordCount uint32 |
| Records []SFlowRecord |
| } |
| |
| // Counter samples have the following structure: |
| |
| // 0 15 31 |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | int sample sequence number | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // |id type | src id index value | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | int number of records | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // / counter records / |
| // / / |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| |
| type SFlowCounterDataFormat uint32 |
| |
| func (cdf SFlowCounterDataFormat) decode() (SFlowEnterpriseID, SFlowCounterRecordType) { |
| leftField := cdf >> 12 |
| rightField := uint32(0xFFF) & uint32(cdf) |
| return SFlowEnterpriseID(leftField), SFlowCounterRecordType(rightField) |
| } |
| |
| // GetRecords will return a slice of interface types |
| // representing records. A type switch can be used to |
| // get at the underlying SFlowCounterRecordType. |
| func (cs SFlowCounterSample) GetRecords() []SFlowRecord { |
| return cs.Records |
| } |
| |
| // GetType will report the type of sample. Only the |
| // compact form of counter samples is supported |
| func (cs SFlowCounterSample) GetType() SFlowSampleType { |
| return SFlowTypeCounterSample |
| } |
| |
| type SFlowCounterRecordType uint32 |
| |
| const ( |
| SFlowTypeGenericInterfaceCounters SFlowCounterRecordType = 1 |
| SFlowTypeEthernetInterfaceCounters SFlowCounterRecordType = 2 |
| SFlowTypeTokenRingInterfaceCounters SFlowCounterRecordType = 3 |
| SFlowType100BaseVGInterfaceCounters SFlowCounterRecordType = 4 |
| SFlowTypeVLANCounters SFlowCounterRecordType = 5 |
| SFlowTypeLACPCounters SFlowCounterRecordType = 7 |
| SFlowTypeProcessorCounters SFlowCounterRecordType = 1001 |
| SFlowTypeOpenflowPortCounters SFlowCounterRecordType = 1004 |
| SFlowTypePORTNAMECounters SFlowCounterRecordType = 1005 |
| SFLowTypeAPPRESOURCESCounters SFlowCounterRecordType = 2203 |
| SFlowTypeOVSDPCounters SFlowCounterRecordType = 2207 |
| ) |
| |
| func (cr SFlowCounterRecordType) String() string { |
| switch cr { |
| case SFlowTypeGenericInterfaceCounters: |
| return "Generic Interface Counters" |
| case SFlowTypeEthernetInterfaceCounters: |
| return "Ethernet Interface Counters" |
| case SFlowTypeTokenRingInterfaceCounters: |
| return "Token Ring Interface Counters" |
| case SFlowType100BaseVGInterfaceCounters: |
| return "100BaseVG Interface Counters" |
| case SFlowTypeVLANCounters: |
| return "VLAN Counters" |
| case SFlowTypeLACPCounters: |
| return "LACP Counters" |
| case SFlowTypeProcessorCounters: |
| return "Processor Counters" |
| case SFlowTypeOpenflowPortCounters: |
| return "Openflow Port Counters" |
| case SFlowTypePORTNAMECounters: |
| return "PORT NAME Counters" |
| case SFLowTypeAPPRESOURCESCounters: |
| return "App Resources Counters" |
| case SFlowTypeOVSDPCounters: |
| return "OVSDP Counters" |
| default: |
| return "" |
| |
| } |
| } |
| |
| func decodeCounterSample(data *[]byte, expanded bool) (SFlowCounterSample, error) { |
| s := SFlowCounterSample{} |
| var sdc SFlowDataSource |
| var sdce SFlowDataSourceExpanded |
| var sdf SFlowDataFormat |
| |
| *data, sdf = (*data)[4:], SFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) |
| s.EnterpriseID, s.Format = sdf.decode() |
| *data, s.SampleLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, s.SequenceNumber = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| if expanded { |
| *data, sdce = (*data)[8:], SFlowDataSourceExpanded{SFlowSourceFormat(binary.BigEndian.Uint32((*data)[:4])), SFlowSourceValue(binary.BigEndian.Uint32((*data)[4:8]))} |
| s.SourceIDClass, s.SourceIDIndex = sdce.decode() |
| } else { |
| *data, sdc = (*data)[4:], SFlowDataSource(binary.BigEndian.Uint32((*data)[:4])) |
| s.SourceIDClass, s.SourceIDIndex = sdc.decode() |
| } |
| *data, s.RecordCount = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| |
| for i := uint32(0); i < s.RecordCount; i++ { |
| cdf := SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4])) |
| _, counterRecordType := cdf.decode() |
| switch counterRecordType { |
| case SFlowTypeGenericInterfaceCounters: |
| if record, err := decodeGenericInterfaceCounters(data); err == nil { |
| s.Records = append(s.Records, record) |
| } else { |
| return s, err |
| } |
| case SFlowTypeEthernetInterfaceCounters: |
| if record, err := decodeEthernetCounters(data); err == nil { |
| s.Records = append(s.Records, record) |
| } else { |
| return s, err |
| } |
| case SFlowTypeTokenRingInterfaceCounters: |
| skipRecord(data) |
| return s, errors.New("skipping TypeTokenRingInterfaceCounters") |
| case SFlowType100BaseVGInterfaceCounters: |
| skipRecord(data) |
| return s, errors.New("skipping Type100BaseVGInterfaceCounters") |
| case SFlowTypeVLANCounters: |
| if record, err := decodeVLANCounters(data); err == nil { |
| s.Records = append(s.Records, record) |
| } else { |
| return s, err |
| } |
| case SFlowTypeLACPCounters: |
| if record, err := decodeLACPCounters(data); err == nil { |
| s.Records = append(s.Records, record) |
| } else { |
| return s, err |
| } |
| case SFlowTypeProcessorCounters: |
| if record, err := decodeProcessorCounters(data); err == nil { |
| s.Records = append(s.Records, record) |
| } else { |
| return s, err |
| } |
| case SFlowTypeOpenflowPortCounters: |
| if record, err := decodeOpenflowportCounters(data); err == nil { |
| s.Records = append(s.Records, record) |
| } else { |
| return s, err |
| } |
| case SFlowTypePORTNAMECounters: |
| if record, err := decodePortnameCounters(data); err == nil { |
| s.Records = append(s.Records, record) |
| } else { |
| return s, err |
| } |
| case SFLowTypeAPPRESOURCESCounters: |
| if record, err := decodeAppresourcesCounters(data); err == nil { |
| s.Records = append(s.Records, record) |
| } else { |
| return s, err |
| } |
| case SFlowTypeOVSDPCounters: |
| if record, err := decodeOVSDPCounters(data); err == nil { |
| s.Records = append(s.Records, record) |
| } else { |
| return s, err |
| } |
| default: |
| return s, fmt.Errorf("Invalid counter record type: %d", counterRecordType) |
| } |
| } |
| return s, nil |
| } |
| |
| // SFlowBaseFlowRecord holds the fields common to all records |
| // of type SFlowFlowRecordType |
| type SFlowBaseFlowRecord struct { |
| EnterpriseID SFlowEnterpriseID |
| Format SFlowFlowRecordType |
| FlowDataLength uint32 |
| } |
| |
| func (bfr SFlowBaseFlowRecord) GetType() SFlowFlowRecordType { |
| return bfr.Format |
| } |
| |
| // SFlowFlowRecordType denotes what kind of Flow Record is |
| // represented. See RFC 3176 |
| type SFlowFlowRecordType uint32 |
| |
| const ( |
| SFlowTypeRawPacketFlow SFlowFlowRecordType = 1 |
| SFlowTypeEthernetFrameFlow SFlowFlowRecordType = 2 |
| SFlowTypeIpv4Flow SFlowFlowRecordType = 3 |
| SFlowTypeIpv6Flow SFlowFlowRecordType = 4 |
| SFlowTypeExtendedSwitchFlow SFlowFlowRecordType = 1001 |
| SFlowTypeExtendedRouterFlow SFlowFlowRecordType = 1002 |
| SFlowTypeExtendedGatewayFlow SFlowFlowRecordType = 1003 |
| SFlowTypeExtendedUserFlow SFlowFlowRecordType = 1004 |
| SFlowTypeExtendedUrlFlow SFlowFlowRecordType = 1005 |
| SFlowTypeExtendedMlpsFlow SFlowFlowRecordType = 1006 |
| SFlowTypeExtendedNatFlow SFlowFlowRecordType = 1007 |
| SFlowTypeExtendedMlpsTunnelFlow SFlowFlowRecordType = 1008 |
| SFlowTypeExtendedMlpsVcFlow SFlowFlowRecordType = 1009 |
| SFlowTypeExtendedMlpsFecFlow SFlowFlowRecordType = 1010 |
| SFlowTypeExtendedMlpsLvpFecFlow SFlowFlowRecordType = 1011 |
| SFlowTypeExtendedVlanFlow SFlowFlowRecordType = 1012 |
| SFlowTypeExtendedIpv4TunnelEgressFlow SFlowFlowRecordType = 1023 |
| SFlowTypeExtendedIpv4TunnelIngressFlow SFlowFlowRecordType = 1024 |
| SFlowTypeExtendedIpv6TunnelEgressFlow SFlowFlowRecordType = 1025 |
| SFlowTypeExtendedIpv6TunnelIngressFlow SFlowFlowRecordType = 1026 |
| SFlowTypeExtendedDecapsulateEgressFlow SFlowFlowRecordType = 1027 |
| SFlowTypeExtendedDecapsulateIngressFlow SFlowFlowRecordType = 1028 |
| SFlowTypeExtendedVniEgressFlow SFlowFlowRecordType = 1029 |
| SFlowTypeExtendedVniIngressFlow SFlowFlowRecordType = 1030 |
| ) |
| |
| func (rt SFlowFlowRecordType) String() string { |
| switch rt { |
| case SFlowTypeRawPacketFlow: |
| return "Raw Packet Flow Record" |
| case SFlowTypeEthernetFrameFlow: |
| return "Ethernet Frame Flow Record" |
| case SFlowTypeIpv4Flow: |
| return "IPv4 Flow Record" |
| case SFlowTypeIpv6Flow: |
| return "IPv6 Flow Record" |
| case SFlowTypeExtendedSwitchFlow: |
| return "Extended Switch Flow Record" |
| case SFlowTypeExtendedRouterFlow: |
| return "Extended Router Flow Record" |
| case SFlowTypeExtendedGatewayFlow: |
| return "Extended Gateway Flow Record" |
| case SFlowTypeExtendedUserFlow: |
| return "Extended User Flow Record" |
| case SFlowTypeExtendedUrlFlow: |
| return "Extended URL Flow Record" |
| case SFlowTypeExtendedMlpsFlow: |
| return "Extended MPLS Flow Record" |
| case SFlowTypeExtendedNatFlow: |
| return "Extended NAT Flow Record" |
| case SFlowTypeExtendedMlpsTunnelFlow: |
| return "Extended MPLS Tunnel Flow Record" |
| case SFlowTypeExtendedMlpsVcFlow: |
| return "Extended MPLS VC Flow Record" |
| case SFlowTypeExtendedMlpsFecFlow: |
| return "Extended MPLS FEC Flow Record" |
| case SFlowTypeExtendedMlpsLvpFecFlow: |
| return "Extended MPLS LVP FEC Flow Record" |
| case SFlowTypeExtendedVlanFlow: |
| return "Extended VLAN Flow Record" |
| case SFlowTypeExtendedIpv4TunnelEgressFlow: |
| return "Extended IPv4 Tunnel Egress Record" |
| case SFlowTypeExtendedIpv4TunnelIngressFlow: |
| return "Extended IPv4 Tunnel Ingress Record" |
| case SFlowTypeExtendedIpv6TunnelEgressFlow: |
| return "Extended IPv6 Tunnel Egress Record" |
| case SFlowTypeExtendedIpv6TunnelIngressFlow: |
| return "Extended IPv6 Tunnel Ingress Record" |
| case SFlowTypeExtendedDecapsulateEgressFlow: |
| return "Extended Decapsulate Egress Record" |
| case SFlowTypeExtendedDecapsulateIngressFlow: |
| return "Extended Decapsulate Ingress Record" |
| case SFlowTypeExtendedVniEgressFlow: |
| return "Extended VNI Ingress Record" |
| case SFlowTypeExtendedVniIngressFlow: |
| return "Extended VNI Ingress Record" |
| default: |
| return "" |
| } |
| } |
| |
| // SFlowRawPacketFlowRecords hold information about a sampled |
| // packet grabbed as it transited the agent. This is |
| // perhaps the most useful and interesting record type, |
| // as it holds the headers of the sampled packet and |
| // can be used to build up a complete picture of the |
| // traffic patterns on a network. |
| // |
| // The raw packet header is sent back into gopacket for |
| // decoding, and the resulting gopackt.Packet is stored |
| // in the Header member |
| type SFlowRawPacketFlowRecord struct { |
| SFlowBaseFlowRecord |
| HeaderProtocol SFlowRawHeaderProtocol |
| FrameLength uint32 |
| PayloadRemoved uint32 |
| HeaderLength uint32 |
| Header gopacket.Packet |
| } |
| |
| // Raw packet record types have the following structure: |
| |
| // 0 15 31 |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | 20 bit Interprise (0) |12 bit format | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | record length | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | Header Protocol | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | Frame Length | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | Payload Removed | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | Header Length | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // \ Header \ |
| // \ \ |
| // \ \ |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| |
| type SFlowRawHeaderProtocol uint32 |
| |
| const ( |
| SFlowProtoEthernet SFlowRawHeaderProtocol = 1 |
| SFlowProtoISO88024 SFlowRawHeaderProtocol = 2 |
| SFlowProtoISO88025 SFlowRawHeaderProtocol = 3 |
| SFlowProtoFDDI SFlowRawHeaderProtocol = 4 |
| SFlowProtoFrameRelay SFlowRawHeaderProtocol = 5 |
| SFlowProtoX25 SFlowRawHeaderProtocol = 6 |
| SFlowProtoPPP SFlowRawHeaderProtocol = 7 |
| SFlowProtoSMDS SFlowRawHeaderProtocol = 8 |
| SFlowProtoAAL5 SFlowRawHeaderProtocol = 9 |
| SFlowProtoAAL5_IP SFlowRawHeaderProtocol = 10 /* e.g. Cisco AAL5 mux */ |
| SFlowProtoIPv4 SFlowRawHeaderProtocol = 11 |
| SFlowProtoIPv6 SFlowRawHeaderProtocol = 12 |
| SFlowProtoMPLS SFlowRawHeaderProtocol = 13 |
| SFlowProtoPOS SFlowRawHeaderProtocol = 14 /* RFC 1662, 2615 */ |
| ) |
| |
| func (sfhp SFlowRawHeaderProtocol) String() string { |
| switch sfhp { |
| case SFlowProtoEthernet: |
| return "ETHERNET-ISO88023" |
| case SFlowProtoISO88024: |
| return "ISO88024-TOKENBUS" |
| case SFlowProtoISO88025: |
| return "ISO88025-TOKENRING" |
| case SFlowProtoFDDI: |
| return "FDDI" |
| case SFlowProtoFrameRelay: |
| return "FRAME-RELAY" |
| case SFlowProtoX25: |
| return "X25" |
| case SFlowProtoPPP: |
| return "PPP" |
| case SFlowProtoSMDS: |
| return "SMDS" |
| case SFlowProtoAAL5: |
| return "AAL5" |
| case SFlowProtoAAL5_IP: |
| return "AAL5-IP" |
| case SFlowProtoIPv4: |
| return "IPv4" |
| case SFlowProtoIPv6: |
| return "IPv6" |
| case SFlowProtoMPLS: |
| return "MPLS" |
| case SFlowProtoPOS: |
| return "POS" |
| } |
| return "UNKNOWN" |
| } |
| |
| func decodeRawPacketFlowRecord(data *[]byte) (SFlowRawPacketFlowRecord, error) { |
| rec := SFlowRawPacketFlowRecord{} |
| header := []byte{} |
| var fdf SFlowFlowDataFormat |
| |
| *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) |
| rec.EnterpriseID, rec.Format = fdf.decode() |
| *data, rec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, rec.HeaderProtocol = (*data)[4:], SFlowRawHeaderProtocol(binary.BigEndian.Uint32((*data)[:4])) |
| *data, rec.FrameLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, rec.PayloadRemoved = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, rec.HeaderLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| headerLenWithPadding := int(rec.HeaderLength + ((4 - rec.HeaderLength) % 4)) |
| *data, header = (*data)[headerLenWithPadding:], (*data)[:headerLenWithPadding] |
| rec.Header = gopacket.NewPacket(header, LayerTypeEthernet, gopacket.Default) |
| return rec, nil |
| } |
| |
| // SFlowExtendedSwitchFlowRecord give additional information |
| // about the sampled packet if it's available. It's mainly |
| // useful for getting at the incoming and outgoing VLANs |
| // An agent may or may not provide this information. |
| type SFlowExtendedSwitchFlowRecord struct { |
| SFlowBaseFlowRecord |
| IncomingVLAN uint32 |
| IncomingVLANPriority uint32 |
| OutgoingVLAN uint32 |
| OutgoingVLANPriority uint32 |
| } |
| |
| // Extended switch records have the following structure: |
| |
| // 0 15 31 |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | 20 bit Interprise (0) |12 bit format | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | record length | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | Incoming VLAN | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | Incoming VLAN Priority | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | Outgoing VLAN | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | Outgoing VLAN Priority | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| |
| func decodeExtendedSwitchFlowRecord(data *[]byte) (SFlowExtendedSwitchFlowRecord, error) { |
| es := SFlowExtendedSwitchFlowRecord{} |
| var fdf SFlowFlowDataFormat |
| |
| *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) |
| es.EnterpriseID, es.Format = fdf.decode() |
| *data, es.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, es.IncomingVLAN = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, es.IncomingVLANPriority = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, es.OutgoingVLAN = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, es.OutgoingVLANPriority = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| return es, nil |
| } |
| |
| // SFlowExtendedRouterFlowRecord gives additional information |
| // about the layer 3 routing information used to forward |
| // the packet |
| type SFlowExtendedRouterFlowRecord struct { |
| SFlowBaseFlowRecord |
| NextHop net.IP |
| NextHopSourceMask uint32 |
| NextHopDestinationMask uint32 |
| } |
| |
| // Extended router records have the following structure: |
| |
| // 0 15 31 |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | 20 bit Interprise (0) |12 bit format | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | record length | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | IP version of next hop router (1=v4|2=v6) | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // / Next Hop address (v4=4byte|v6=16byte) / |
| // / / |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | Next Hop Source Mask | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | Next Hop Destination Mask | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| |
| func decodeExtendedRouterFlowRecord(data *[]byte) (SFlowExtendedRouterFlowRecord, error) { |
| er := SFlowExtendedRouterFlowRecord{} |
| var fdf SFlowFlowDataFormat |
| var extendedRouterAddressType SFlowIPType |
| |
| *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) |
| er.EnterpriseID, er.Format = fdf.decode() |
| *data, er.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, extendedRouterAddressType = (*data)[4:], SFlowIPType(binary.BigEndian.Uint32((*data)[:4])) |
| *data, er.NextHop = (*data)[extendedRouterAddressType.Length():], (*data)[:extendedRouterAddressType.Length()] |
| *data, er.NextHopSourceMask = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, er.NextHopDestinationMask = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| return er, nil |
| } |
| |
| // SFlowExtendedGatewayFlowRecord describes information treasured by |
| // nework engineers everywhere: AS path information listing which |
| // BGP peer sent the packet, and various other BGP related info. |
| // This information is vital because it gives a picture of how much |
| // traffic is being sent from / received by various BGP peers. |
| |
| // Extended gateway records have the following structure: |
| |
| // 0 15 31 |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | 20 bit Interprise (0) |12 bit format | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | record length | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | IP version of next hop router (1=v4|2=v6) | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // / Next Hop address (v4=4byte|v6=16byte) / |
| // / / |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | AS | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | Source AS | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | Peer AS | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | AS Path Count | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // / AS Path / Sequence / |
| // / / |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // / Communities / |
| // / / |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | Local Pref | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| |
| // AS Path / Sequence: |
| |
| // 0 15 31 |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | AS Source Type (Path=1 / Sequence=2) | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | Path / Sequence length | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // / Path / Sequence Members / |
| // / / |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| |
| // Communities: |
| |
| // 0 15 31 |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | communitiy length | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // / communitiy Members / |
| // / / |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| |
| type SFlowExtendedGatewayFlowRecord struct { |
| SFlowBaseFlowRecord |
| NextHop net.IP |
| AS uint32 |
| SourceAS uint32 |
| PeerAS uint32 |
| ASPathCount uint32 |
| ASPath []SFlowASDestination |
| Communities []uint32 |
| LocalPref uint32 |
| } |
| |
| type SFlowASPathType uint32 |
| |
| const ( |
| SFlowASSet SFlowASPathType = 1 |
| SFlowASSequence SFlowASPathType = 2 |
| ) |
| |
| func (apt SFlowASPathType) String() string { |
| switch apt { |
| case SFlowASSet: |
| return "AS Set" |
| case SFlowASSequence: |
| return "AS Sequence" |
| default: |
| return "" |
| } |
| } |
| |
| type SFlowASDestination struct { |
| Type SFlowASPathType |
| Count uint32 |
| Members []uint32 |
| } |
| |
| func (asd SFlowASDestination) String() string { |
| switch asd.Type { |
| case SFlowASSet: |
| return fmt.Sprint("AS Set:", asd.Members) |
| case SFlowASSequence: |
| return fmt.Sprint("AS Sequence:", asd.Members) |
| default: |
| return "" |
| } |
| } |
| |
| func (ad *SFlowASDestination) decodePath(data *[]byte) { |
| *data, ad.Type = (*data)[4:], SFlowASPathType(binary.BigEndian.Uint32((*data)[:4])) |
| *data, ad.Count = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| ad.Members = make([]uint32, ad.Count) |
| for i := uint32(0); i < ad.Count; i++ { |
| var member uint32 |
| *data, member = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| ad.Members[i] = member |
| } |
| } |
| |
| func decodeExtendedGatewayFlowRecord(data *[]byte) (SFlowExtendedGatewayFlowRecord, error) { |
| eg := SFlowExtendedGatewayFlowRecord{} |
| var fdf SFlowFlowDataFormat |
| var extendedGatewayAddressType SFlowIPType |
| var communitiesLength uint32 |
| var community uint32 |
| |
| *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) |
| eg.EnterpriseID, eg.Format = fdf.decode() |
| *data, eg.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, extendedGatewayAddressType = (*data)[4:], SFlowIPType(binary.BigEndian.Uint32((*data)[:4])) |
| *data, eg.NextHop = (*data)[extendedGatewayAddressType.Length():], (*data)[:extendedGatewayAddressType.Length()] |
| *data, eg.AS = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, eg.SourceAS = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, eg.PeerAS = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, eg.ASPathCount = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| for i := uint32(0); i < eg.ASPathCount; i++ { |
| asPath := SFlowASDestination{} |
| asPath.decodePath(data) |
| eg.ASPath = append(eg.ASPath, asPath) |
| } |
| *data, communitiesLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| eg.Communities = make([]uint32, communitiesLength) |
| for j := uint32(0); j < communitiesLength; j++ { |
| *data, community = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| eg.Communities[j] = community |
| } |
| *data, eg.LocalPref = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| return eg, nil |
| } |
| |
| // ************************************************** |
| // Extended URL Flow Record |
| // ************************************************** |
| |
| // 0 15 31 |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | 20 bit Interprise (0) |12 bit format | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | record length | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | direction | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | URL | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | Host | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| |
| type SFlowURLDirection uint32 |
| |
| const ( |
| SFlowURLsrc SFlowURLDirection = 1 |
| SFlowURLdst SFlowURLDirection = 2 |
| ) |
| |
| func (urld SFlowURLDirection) String() string { |
| switch urld { |
| case SFlowURLsrc: |
| return "Source address is the server" |
| case SFlowURLdst: |
| return "Destination address is the server" |
| default: |
| return "" |
| } |
| } |
| |
| type SFlowExtendedURLRecord struct { |
| SFlowBaseFlowRecord |
| Direction SFlowURLDirection |
| URL string |
| Host string |
| } |
| |
| func decodeExtendedURLRecord(data *[]byte) (SFlowExtendedURLRecord, error) { |
| eur := SFlowExtendedURLRecord{} |
| var fdf SFlowFlowDataFormat |
| var urlLen uint32 |
| var urlLenWithPad int |
| var hostLen uint32 |
| var hostLenWithPad int |
| var urlBytes []byte |
| var hostBytes []byte |
| |
| *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) |
| eur.EnterpriseID, eur.Format = fdf.decode() |
| *data, eur.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, eur.Direction = (*data)[4:], SFlowURLDirection(binary.BigEndian.Uint32((*data)[:4])) |
| *data, urlLen = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| urlLenWithPad = int(urlLen + ((4 - urlLen) % 4)) |
| *data, urlBytes = (*data)[urlLenWithPad:], (*data)[:urlLenWithPad] |
| eur.URL = string(urlBytes[:urlLen]) |
| *data, hostLen = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| hostLenWithPad = int(hostLen + ((4 - hostLen) % 4)) |
| *data, hostBytes = (*data)[hostLenWithPad:], (*data)[:hostLenWithPad] |
| eur.Host = string(hostBytes[:hostLen]) |
| return eur, nil |
| } |
| |
| // ************************************************** |
| // Extended User Flow Record |
| // ************************************************** |
| |
| // 0 15 31 |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | 20 bit Interprise (0) |12 bit format | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | record length | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | Source Character Set | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | Source User Id | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | Destination Character Set | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | Destination User ID | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| |
| type SFlowExtendedUserFlow struct { |
| SFlowBaseFlowRecord |
| SourceCharSet SFlowCharSet |
| SourceUserID string |
| DestinationCharSet SFlowCharSet |
| DestinationUserID string |
| } |
| |
| type SFlowCharSet uint32 |
| |
| const ( |
| SFlowCSunknown SFlowCharSet = 2 |
| SFlowCSASCII SFlowCharSet = 3 |
| SFlowCSISOLatin1 SFlowCharSet = 4 |
| SFlowCSISOLatin2 SFlowCharSet = 5 |
| SFlowCSISOLatin3 SFlowCharSet = 6 |
| SFlowCSISOLatin4 SFlowCharSet = 7 |
| SFlowCSISOLatinCyrillic SFlowCharSet = 8 |
| SFlowCSISOLatinArabic SFlowCharSet = 9 |
| SFlowCSISOLatinGreek SFlowCharSet = 10 |
| SFlowCSISOLatinHebrew SFlowCharSet = 11 |
| SFlowCSISOLatin5 SFlowCharSet = 12 |
| SFlowCSISOLatin6 SFlowCharSet = 13 |
| SFlowCSISOTextComm SFlowCharSet = 14 |
| SFlowCSHalfWidthKatakana SFlowCharSet = 15 |
| SFlowCSJISEncoding SFlowCharSet = 16 |
| SFlowCSShiftJIS SFlowCharSet = 17 |
| SFlowCSEUCPkdFmtJapanese SFlowCharSet = 18 |
| SFlowCSEUCFixWidJapanese SFlowCharSet = 19 |
| SFlowCSISO4UnitedKingdom SFlowCharSet = 20 |
| SFlowCSISO11SwedishForNames SFlowCharSet = 21 |
| SFlowCSISO15Italian SFlowCharSet = 22 |
| SFlowCSISO17Spanish SFlowCharSet = 23 |
| SFlowCSISO21German SFlowCharSet = 24 |
| SFlowCSISO60DanishNorwegian SFlowCharSet = 25 |
| SFlowCSISO69French SFlowCharSet = 26 |
| SFlowCSISO10646UTF1 SFlowCharSet = 27 |
| SFlowCSISO646basic1983 SFlowCharSet = 28 |
| SFlowCSINVARIANT SFlowCharSet = 29 |
| SFlowCSISO2IntlRefVersion SFlowCharSet = 30 |
| SFlowCSNATSSEFI SFlowCharSet = 31 |
| SFlowCSNATSSEFIADD SFlowCharSet = 32 |
| SFlowCSNATSDANO SFlowCharSet = 33 |
| SFlowCSNATSDANOADD SFlowCharSet = 34 |
| SFlowCSISO10Swedish SFlowCharSet = 35 |
| SFlowCSKSC56011987 SFlowCharSet = 36 |
| SFlowCSISO2022KR SFlowCharSet = 37 |
| SFlowCSEUCKR SFlowCharSet = 38 |
| SFlowCSISO2022JP SFlowCharSet = 39 |
| SFlowCSISO2022JP2 SFlowCharSet = 40 |
| SFlowCSISO13JISC6220jp SFlowCharSet = 41 |
| SFlowCSISO14JISC6220ro SFlowCharSet = 42 |
| SFlowCSISO16Portuguese SFlowCharSet = 43 |
| SFlowCSISO18Greek7Old SFlowCharSet = 44 |
| SFlowCSISO19LatinGreek SFlowCharSet = 45 |
| SFlowCSISO25French SFlowCharSet = 46 |
| SFlowCSISO27LatinGreek1 SFlowCharSet = 47 |
| SFlowCSISO5427Cyrillic SFlowCharSet = 48 |
| SFlowCSISO42JISC62261978 SFlowCharSet = 49 |
| SFlowCSISO47BSViewdata SFlowCharSet = 50 |
| SFlowCSISO49INIS SFlowCharSet = 51 |
| SFlowCSISO50INIS8 SFlowCharSet = 52 |
| SFlowCSISO51INISCyrillic SFlowCharSet = 53 |
| SFlowCSISO54271981 SFlowCharSet = 54 |
| SFlowCSISO5428Greek SFlowCharSet = 55 |
| SFlowCSISO57GB1988 SFlowCharSet = 56 |
| SFlowCSISO58GB231280 SFlowCharSet = 57 |
| SFlowCSISO61Norwegian2 SFlowCharSet = 58 |
| SFlowCSISO70VideotexSupp1 SFlowCharSet = 59 |
| SFlowCSISO84Portuguese2 SFlowCharSet = 60 |
| SFlowCSISO85Spanish2 SFlowCharSet = 61 |
| SFlowCSISO86Hungarian SFlowCharSet = 62 |
| SFlowCSISO87JISX0208 SFlowCharSet = 63 |
| SFlowCSISO88Greek7 SFlowCharSet = 64 |
| SFlowCSISO89ASMO449 SFlowCharSet = 65 |
| SFlowCSISO90 SFlowCharSet = 66 |
| SFlowCSISO91JISC62291984a SFlowCharSet = 67 |
| SFlowCSISO92JISC62991984b SFlowCharSet = 68 |
| SFlowCSISO93JIS62291984badd SFlowCharSet = 69 |
| SFlowCSISO94JIS62291984hand SFlowCharSet = 70 |
| SFlowCSISO95JIS62291984handadd SFlowCharSet = 71 |
| SFlowCSISO96JISC62291984kana SFlowCharSet = 72 |
| SFlowCSISO2033 SFlowCharSet = 73 |
| SFlowCSISO99NAPLPS SFlowCharSet = 74 |
| SFlowCSISO102T617bit SFlowCharSet = 75 |
| SFlowCSISO103T618bit SFlowCharSet = 76 |
| SFlowCSISO111ECMACyrillic SFlowCharSet = 77 |
| SFlowCSa71 SFlowCharSet = 78 |
| SFlowCSa72 SFlowCharSet = 79 |
| SFlowCSISO123CSAZ24341985gr SFlowCharSet = 80 |
| SFlowCSISO88596E SFlowCharSet = 81 |
| SFlowCSISO88596I SFlowCharSet = 82 |
| SFlowCSISO128T101G2 SFlowCharSet = 83 |
| SFlowCSISO88598E SFlowCharSet = 84 |
| SFlowCSISO88598I SFlowCharSet = 85 |
| SFlowCSISO139CSN369103 SFlowCharSet = 86 |
| SFlowCSISO141JUSIB1002 SFlowCharSet = 87 |
| SFlowCSISO143IECP271 SFlowCharSet = 88 |
| SFlowCSISO146Serbian SFlowCharSet = 89 |
| SFlowCSISO147Macedonian SFlowCharSet = 90 |
| SFlowCSISO150 SFlowCharSet = 91 |
| SFlowCSISO151Cuba SFlowCharSet = 92 |
| SFlowCSISO6937Add SFlowCharSet = 93 |
| SFlowCSISO153GOST1976874 SFlowCharSet = 94 |
| SFlowCSISO8859Supp SFlowCharSet = 95 |
| SFlowCSISO10367Box SFlowCharSet = 96 |
| SFlowCSISO158Lap SFlowCharSet = 97 |
| SFlowCSISO159JISX02121990 SFlowCharSet = 98 |
| SFlowCSISO646Danish SFlowCharSet = 99 |
| SFlowCSUSDK SFlowCharSet = 100 |
| SFlowCSDKUS SFlowCharSet = 101 |
| SFlowCSKSC5636 SFlowCharSet = 102 |
| SFlowCSUnicode11UTF7 SFlowCharSet = 103 |
| SFlowCSISO2022CN SFlowCharSet = 104 |
| SFlowCSISO2022CNEXT SFlowCharSet = 105 |
| SFlowCSUTF8 SFlowCharSet = 106 |
| SFlowCSISO885913 SFlowCharSet = 109 |
| SFlowCSISO885914 SFlowCharSet = 110 |
| SFlowCSISO885915 SFlowCharSet = 111 |
| SFlowCSISO885916 SFlowCharSet = 112 |
| SFlowCSGBK SFlowCharSet = 113 |
| SFlowCSGB18030 SFlowCharSet = 114 |
| SFlowCSOSDEBCDICDF0415 SFlowCharSet = 115 |
| SFlowCSOSDEBCDICDF03IRV SFlowCharSet = 116 |
| SFlowCSOSDEBCDICDF041 SFlowCharSet = 117 |
| SFlowCSISO115481 SFlowCharSet = 118 |
| SFlowCSKZ1048 SFlowCharSet = 119 |
| SFlowCSUnicode SFlowCharSet = 1000 |
| SFlowCSUCS4 SFlowCharSet = 1001 |
| SFlowCSUnicodeASCII SFlowCharSet = 1002 |
| SFlowCSUnicodeLatin1 SFlowCharSet = 1003 |
| SFlowCSUnicodeJapanese SFlowCharSet = 1004 |
| SFlowCSUnicodeIBM1261 SFlowCharSet = 1005 |
| SFlowCSUnicodeIBM1268 SFlowCharSet = 1006 |
| SFlowCSUnicodeIBM1276 SFlowCharSet = 1007 |
| SFlowCSUnicodeIBM1264 SFlowCharSet = 1008 |
| SFlowCSUnicodeIBM1265 SFlowCharSet = 1009 |
| SFlowCSUnicode11 SFlowCharSet = 1010 |
| SFlowCSSCSU SFlowCharSet = 1011 |
| SFlowCSUTF7 SFlowCharSet = 1012 |
| SFlowCSUTF16BE SFlowCharSet = 1013 |
| SFlowCSUTF16LE SFlowCharSet = 1014 |
| SFlowCSUTF16 SFlowCharSet = 1015 |
| SFlowCSCESU8 SFlowCharSet = 1016 |
| SFlowCSUTF32 SFlowCharSet = 1017 |
| SFlowCSUTF32BE SFlowCharSet = 1018 |
| SFlowCSUTF32LE SFlowCharSet = 1019 |
| SFlowCSBOCU1 SFlowCharSet = 1020 |
| SFlowCSWindows30Latin1 SFlowCharSet = 2000 |
| SFlowCSWindows31Latin1 SFlowCharSet = 2001 |
| SFlowCSWindows31Latin2 SFlowCharSet = 2002 |
| SFlowCSWindows31Latin5 SFlowCharSet = 2003 |
| SFlowCSHPRoman8 SFlowCharSet = 2004 |
| SFlowCSAdobeStandardEncoding SFlowCharSet = 2005 |
| SFlowCSVenturaUS SFlowCharSet = 2006 |
| SFlowCSVenturaInternational SFlowCharSet = 2007 |
| SFlowCSDECMCS SFlowCharSet = 2008 |
| SFlowCSPC850Multilingual SFlowCharSet = 2009 |
| SFlowCSPCp852 SFlowCharSet = 2010 |
| SFlowCSPC8CodePage437 SFlowCharSet = 2011 |
| SFlowCSPC8DanishNorwegian SFlowCharSet = 2012 |
| SFlowCSPC862LatinHebrew SFlowCharSet = 2013 |
| SFlowCSPC8Turkish SFlowCharSet = 2014 |
| SFlowCSIBMSymbols SFlowCharSet = 2015 |
| SFlowCSIBMThai SFlowCharSet = 2016 |
| SFlowCSHPLegal SFlowCharSet = 2017 |
| SFlowCSHPPiFont SFlowCharSet = 2018 |
| SFlowCSHPMath8 SFlowCharSet = 2019 |
| SFlowCSHPPSMath SFlowCharSet = 2020 |
| SFlowCSHPDesktop SFlowCharSet = 2021 |
| SFlowCSVenturaMath SFlowCharSet = 2022 |
| SFlowCSMicrosoftPublishing SFlowCharSet = 2023 |
| SFlowCSWindows31J SFlowCharSet = 2024 |
| SFlowCSGB2312 SFlowCharSet = 2025 |
| SFlowCSBig5 SFlowCharSet = 2026 |
| SFlowCSMacintosh SFlowCharSet = 2027 |
| SFlowCSIBM037 SFlowCharSet = 2028 |
| SFlowCSIBM038 SFlowCharSet = 2029 |
| SFlowCSIBM273 SFlowCharSet = 2030 |
| SFlowCSIBM274 SFlowCharSet = 2031 |
| SFlowCSIBM275 SFlowCharSet = 2032 |
| SFlowCSIBM277 SFlowCharSet = 2033 |
| SFlowCSIBM278 SFlowCharSet = 2034 |
| SFlowCSIBM280 SFlowCharSet = 2035 |
| SFlowCSIBM281 SFlowCharSet = 2036 |
| SFlowCSIBM284 SFlowCharSet = 2037 |
| SFlowCSIBM285 SFlowCharSet = 2038 |
| SFlowCSIBM290 SFlowCharSet = 2039 |
| SFlowCSIBM297 SFlowCharSet = 2040 |
| SFlowCSIBM420 SFlowCharSet = 2041 |
| SFlowCSIBM423 SFlowCharSet = 2042 |
| SFlowCSIBM424 SFlowCharSet = 2043 |
| SFlowCSIBM500 SFlowCharSet = 2044 |
| SFlowCSIBM851 SFlowCharSet = 2045 |
| SFlowCSIBM855 SFlowCharSet = 2046 |
| SFlowCSIBM857 SFlowCharSet = 2047 |
| SFlowCSIBM860 SFlowCharSet = 2048 |
| SFlowCSIBM861 SFlowCharSet = 2049 |
| SFlowCSIBM863 SFlowCharSet = 2050 |
| SFlowCSIBM864 SFlowCharSet = 2051 |
| SFlowCSIBM865 SFlowCharSet = 2052 |
| SFlowCSIBM868 SFlowCharSet = 2053 |
| SFlowCSIBM869 SFlowCharSet = 2054 |
| SFlowCSIBM870 SFlowCharSet = 2055 |
| SFlowCSIBM871 SFlowCharSet = 2056 |
| SFlowCSIBM880 SFlowCharSet = 2057 |
| SFlowCSIBM891 SFlowCharSet = 2058 |
| SFlowCSIBM903 SFlowCharSet = 2059 |
| SFlowCSIBBM904 SFlowCharSet = 2060 |
| SFlowCSIBM905 SFlowCharSet = 2061 |
| SFlowCSIBM918 SFlowCharSet = 2062 |
| SFlowCSIBM1026 SFlowCharSet = 2063 |
| SFlowCSIBMEBCDICATDE SFlowCharSet = 2064 |
| SFlowCSEBCDICATDEA SFlowCharSet = 2065 |
| SFlowCSEBCDICCAFR SFlowCharSet = 2066 |
| SFlowCSEBCDICDKNO SFlowCharSet = 2067 |
| SFlowCSEBCDICDKNOA SFlowCharSet = 2068 |
| SFlowCSEBCDICFISE SFlowCharSet = 2069 |
| SFlowCSEBCDICFISEA SFlowCharSet = 2070 |
| SFlowCSEBCDICFR SFlowCharSet = 2071 |
| SFlowCSEBCDICIT SFlowCharSet = 2072 |
| SFlowCSEBCDICPT SFlowCharSet = 2073 |
| SFlowCSEBCDICES SFlowCharSet = 2074 |
| SFlowCSEBCDICESA SFlowCharSet = 2075 |
| SFlowCSEBCDICESS SFlowCharSet = 2076 |
| SFlowCSEBCDICUK SFlowCharSet = 2077 |
| SFlowCSEBCDICUS SFlowCharSet = 2078 |
| SFlowCSUnknown8BiT SFlowCharSet = 2079 |
| SFlowCSMnemonic SFlowCharSet = 2080 |
| SFlowCSMnem SFlowCharSet = 2081 |
| SFlowCSVISCII SFlowCharSet = 2082 |
| SFlowCSVIQR SFlowCharSet = 2083 |
| SFlowCSKOI8R SFlowCharSet = 2084 |
| SFlowCSHZGB2312 SFlowCharSet = 2085 |
| SFlowCSIBM866 SFlowCharSet = 2086 |
| SFlowCSPC775Baltic SFlowCharSet = 2087 |
| SFlowCSKOI8U SFlowCharSet = 2088 |
| SFlowCSIBM00858 SFlowCharSet = 2089 |
| SFlowCSIBM00924 SFlowCharSet = 2090 |
| SFlowCSIBM01140 SFlowCharSet = 2091 |
| SFlowCSIBM01141 SFlowCharSet = 2092 |
| SFlowCSIBM01142 SFlowCharSet = 2093 |
| SFlowCSIBM01143 SFlowCharSet = 2094 |
| SFlowCSIBM01144 SFlowCharSet = 2095 |
| SFlowCSIBM01145 SFlowCharSet = 2096 |
| SFlowCSIBM01146 SFlowCharSet = 2097 |
| SFlowCSIBM01147 SFlowCharSet = 2098 |
| SFlowCSIBM01148 SFlowCharSet = 2099 |
| SFlowCSIBM01149 SFlowCharSet = 2100 |
| SFlowCSBig5HKSCS SFlowCharSet = 2101 |
| SFlowCSIBM1047 SFlowCharSet = 2102 |
| SFlowCSPTCP154 SFlowCharSet = 2103 |
| SFlowCSAmiga1251 SFlowCharSet = 2104 |
| SFlowCSKOI7switched SFlowCharSet = 2105 |
| SFlowCSBRF SFlowCharSet = 2106 |
| SFlowCSTSCII SFlowCharSet = 2107 |
| SFlowCSCP51932 SFlowCharSet = 2108 |
| SFlowCSWindows874 SFlowCharSet = 2109 |
| SFlowCSWindows1250 SFlowCharSet = 2250 |
| SFlowCSWindows1251 SFlowCharSet = 2251 |
| SFlowCSWindows1252 SFlowCharSet = 2252 |
| SFlowCSWindows1253 SFlowCharSet = 2253 |
| SFlowCSWindows1254 SFlowCharSet = 2254 |
| SFlowCSWindows1255 SFlowCharSet = 2255 |
| SFlowCSWindows1256 SFlowCharSet = 2256 |
| SFlowCSWindows1257 SFlowCharSet = 2257 |
| SFlowCSWindows1258 SFlowCharSet = 2258 |
| SFlowCSTIS620 SFlowCharSet = 2259 |
| SFlowCS50220 SFlowCharSet = 2260 |
| SFlowCSreserved SFlowCharSet = 3000 |
| ) |
| |
| func decodeExtendedUserFlow(data *[]byte) (SFlowExtendedUserFlow, error) { |
| eu := SFlowExtendedUserFlow{} |
| var fdf SFlowFlowDataFormat |
| var srcUserLen uint32 |
| var srcUserLenWithPad int |
| var srcUserBytes []byte |
| var dstUserLen uint32 |
| var dstUserLenWithPad int |
| var dstUserBytes []byte |
| |
| *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) |
| eu.EnterpriseID, eu.Format = fdf.decode() |
| *data, eu.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, eu.SourceCharSet = (*data)[4:], SFlowCharSet(binary.BigEndian.Uint32((*data)[:4])) |
| *data, srcUserLen = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| srcUserLenWithPad = int(srcUserLen + ((4 - srcUserLen) % 4)) |
| *data, srcUserBytes = (*data)[srcUserLenWithPad:], (*data)[:srcUserLenWithPad] |
| eu.SourceUserID = string(srcUserBytes[:srcUserLen]) |
| *data, eu.DestinationCharSet = (*data)[4:], SFlowCharSet(binary.BigEndian.Uint32((*data)[:4])) |
| *data, dstUserLen = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| dstUserLenWithPad = int(dstUserLen + ((4 - dstUserLen) % 4)) |
| *data, dstUserBytes = (*data)[dstUserLenWithPad:], (*data)[:dstUserLenWithPad] |
| eu.DestinationUserID = string(dstUserBytes[:dstUserLen]) |
| return eu, nil |
| } |
| |
| // ************************************************** |
| // Packet IP version 4 Record |
| // ************************************************** |
| |
| // 0 15 31 |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | Length | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | Protocol | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | Source IPv4 | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | Destination IPv4 | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | Source Port | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | Destionation Port | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | TCP Flags | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | TOS | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| type SFlowIpv4Record struct { |
| // The length of the IP packet excluding ower layer encapsulations |
| Length uint32 |
| // IP Protocol type (for example, TCP = 6, UDP = 17) |
| Protocol uint32 |
| // Source IP Address |
| IPSrc net.IP |
| // Destination IP Address |
| IPDst net.IP |
| // TCP/UDP source port number or equivalent |
| PortSrc uint32 |
| // TCP/UDP destination port number or equivalent |
| PortDst uint32 |
| // TCP flags |
| TCPFlags uint32 |
| // IP type of service |
| TOS uint32 |
| } |
| |
| func decodeSFlowIpv4Record(data *[]byte) (SFlowIpv4Record, error) { |
| si := SFlowIpv4Record{} |
| |
| *data, si.Length = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, si.Protocol = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, si.IPSrc = (*data)[4:], net.IP((*data)[:4]) |
| *data, si.IPDst = (*data)[4:], net.IP((*data)[:4]) |
| *data, si.PortSrc = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, si.PortDst = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, si.TCPFlags = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, si.TOS = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| |
| return si, nil |
| } |
| |
| // ************************************************** |
| // Packet IP version 6 Record |
| // ************************************************** |
| |
| // 0 15 31 |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | Length | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | Protocol | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | Source IPv4 | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | Destination IPv4 | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | Source Port | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | Destionation Port | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | TCP Flags | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | Priority | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| type SFlowIpv6Record struct { |
| // The length of the IP packet excluding ower layer encapsulations |
| Length uint32 |
| // IP Protocol type (for example, TCP = 6, UDP = 17) |
| Protocol uint32 |
| // Source IP Address |
| IPSrc net.IP |
| // Destination IP Address |
| IPDst net.IP |
| // TCP/UDP source port number or equivalent |
| PortSrc uint32 |
| // TCP/UDP destination port number or equivalent |
| PortDst uint32 |
| // TCP flags |
| TCPFlags uint32 |
| // IP priority |
| Priority uint32 |
| } |
| |
| func decodeSFlowIpv6Record(data *[]byte) (SFlowIpv6Record, error) { |
| si := SFlowIpv6Record{} |
| |
| *data, si.Length = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, si.Protocol = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, si.IPSrc = (*data)[16:], net.IP((*data)[:16]) |
| *data, si.IPDst = (*data)[16:], net.IP((*data)[:16]) |
| *data, si.PortSrc = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, si.PortDst = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, si.TCPFlags = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, si.Priority = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| |
| return si, nil |
| } |
| |
| // ************************************************** |
| // Extended IPv4 Tunnel Egress |
| // ************************************************** |
| |
| // 0 15 31 |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | 20 bit Interprise (0) |12 bit format | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | record length | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // / Packet IP version 4 Record / |
| // / / |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| type SFlowExtendedIpv4TunnelEgressRecord struct { |
| SFlowBaseFlowRecord |
| SFlowIpv4Record SFlowIpv4Record |
| } |
| |
| func decodeExtendedIpv4TunnelEgress(data *[]byte) (SFlowExtendedIpv4TunnelEgressRecord, error) { |
| rec := SFlowExtendedIpv4TunnelEgressRecord{} |
| var fdf SFlowFlowDataFormat |
| |
| *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) |
| rec.EnterpriseID, rec.Format = fdf.decode() |
| *data, rec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| rec.SFlowIpv4Record, _ = decodeSFlowIpv4Record(data) |
| |
| return rec, nil |
| } |
| |
| // ************************************************** |
| // Extended IPv4 Tunnel Ingress |
| // ************************************************** |
| |
| // 0 15 31 |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | 20 bit Interprise (0) |12 bit format | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | record length | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // / Packet IP version 4 Record / |
| // / / |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| type SFlowExtendedIpv4TunnelIngressRecord struct { |
| SFlowBaseFlowRecord |
| SFlowIpv4Record SFlowIpv4Record |
| } |
| |
| func decodeExtendedIpv4TunnelIngress(data *[]byte) (SFlowExtendedIpv4TunnelIngressRecord, error) { |
| rec := SFlowExtendedIpv4TunnelIngressRecord{} |
| var fdf SFlowFlowDataFormat |
| |
| *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) |
| rec.EnterpriseID, rec.Format = fdf.decode() |
| *data, rec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| rec.SFlowIpv4Record, _ = decodeSFlowIpv4Record(data) |
| |
| return rec, nil |
| } |
| |
| // ************************************************** |
| // Extended IPv6 Tunnel Egress |
| // ************************************************** |
| |
| // 0 15 31 |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | 20 bit Interprise (0) |12 bit format | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | record length | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // / Packet IP version 6 Record / |
| // / / |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| type SFlowExtendedIpv6TunnelEgressRecord struct { |
| SFlowBaseFlowRecord |
| SFlowIpv6Record |
| } |
| |
| func decodeExtendedIpv6TunnelEgress(data *[]byte) (SFlowExtendedIpv6TunnelEgressRecord, error) { |
| rec := SFlowExtendedIpv6TunnelEgressRecord{} |
| var fdf SFlowFlowDataFormat |
| |
| *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) |
| rec.EnterpriseID, rec.Format = fdf.decode() |
| *data, rec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| rec.SFlowIpv6Record, _ = decodeSFlowIpv6Record(data) |
| |
| return rec, nil |
| } |
| |
| // ************************************************** |
| // Extended IPv6 Tunnel Ingress |
| // ************************************************** |
| |
| // 0 15 31 |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | 20 bit Interprise (0) |12 bit format | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | record length | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // / Packet IP version 6 Record / |
| // / / |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| type SFlowExtendedIpv6TunnelIngressRecord struct { |
| SFlowBaseFlowRecord |
| SFlowIpv6Record |
| } |
| |
| func decodeExtendedIpv6TunnelIngress(data *[]byte) (SFlowExtendedIpv6TunnelIngressRecord, error) { |
| rec := SFlowExtendedIpv6TunnelIngressRecord{} |
| var fdf SFlowFlowDataFormat |
| |
| *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) |
| rec.EnterpriseID, rec.Format = fdf.decode() |
| *data, rec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| rec.SFlowIpv6Record, _ = decodeSFlowIpv6Record(data) |
| |
| return rec, nil |
| } |
| |
| // ************************************************** |
| // Extended Decapsulate Egress |
| // ************************************************** |
| |
| // 0 15 31 |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | 20 bit Interprise (0) |12 bit format | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | record length | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | Inner Header Offset | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| type SFlowExtendedDecapsulateEgressRecord struct { |
| SFlowBaseFlowRecord |
| InnerHeaderOffset uint32 |
| } |
| |
| func decodeExtendedDecapsulateEgress(data *[]byte) (SFlowExtendedDecapsulateEgressRecord, error) { |
| rec := SFlowExtendedDecapsulateEgressRecord{} |
| var fdf SFlowFlowDataFormat |
| |
| *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) |
| rec.EnterpriseID, rec.Format = fdf.decode() |
| *data, rec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, rec.InnerHeaderOffset = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| |
| return rec, nil |
| } |
| |
| // ************************************************** |
| // Extended Decapsulate Ingress |
| // ************************************************** |
| |
| // 0 15 31 |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | 20 bit Interprise (0) |12 bit format | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | record length | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | Inner Header Offset | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| type SFlowExtendedDecapsulateIngressRecord struct { |
| SFlowBaseFlowRecord |
| InnerHeaderOffset uint32 |
| } |
| |
| func decodeExtendedDecapsulateIngress(data *[]byte) (SFlowExtendedDecapsulateIngressRecord, error) { |
| rec := SFlowExtendedDecapsulateIngressRecord{} |
| var fdf SFlowFlowDataFormat |
| |
| *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) |
| rec.EnterpriseID, rec.Format = fdf.decode() |
| *data, rec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, rec.InnerHeaderOffset = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| |
| return rec, nil |
| } |
| |
| // ************************************************** |
| // Extended VNI Egress |
| // ************************************************** |
| |
| // 0 15 31 |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | 20 bit Interprise (0) |12 bit format | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | record length | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | VNI | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| type SFlowExtendedVniEgressRecord struct { |
| SFlowBaseFlowRecord |
| VNI uint32 |
| } |
| |
| func decodeExtendedVniEgress(data *[]byte) (SFlowExtendedVniEgressRecord, error) { |
| rec := SFlowExtendedVniEgressRecord{} |
| var fdf SFlowFlowDataFormat |
| |
| *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) |
| rec.EnterpriseID, rec.Format = fdf.decode() |
| *data, rec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, rec.VNI = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| |
| return rec, nil |
| } |
| |
| // ************************************************** |
| // Extended VNI Ingress |
| // ************************************************** |
| |
| // 0 15 31 |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | 20 bit Interprise (0) |12 bit format | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | record length | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | VNI | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| type SFlowExtendedVniIngressRecord struct { |
| SFlowBaseFlowRecord |
| VNI uint32 |
| } |
| |
| func decodeExtendedVniIngress(data *[]byte) (SFlowExtendedVniIngressRecord, error) { |
| rec := SFlowExtendedVniIngressRecord{} |
| var fdf SFlowFlowDataFormat |
| |
| *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) |
| rec.EnterpriseID, rec.Format = fdf.decode() |
| *data, rec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, rec.VNI = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| |
| return rec, nil |
| } |
| |
| // ************************************************** |
| // Counter Record |
| // ************************************************** |
| |
| // 0 15 31 |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | 20 bit Interprise (0) |12 bit format | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | counter length | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // / counter data / |
| // / / |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| |
| type SFlowBaseCounterRecord struct { |
| EnterpriseID SFlowEnterpriseID |
| Format SFlowCounterRecordType |
| FlowDataLength uint32 |
| } |
| |
| func (bcr SFlowBaseCounterRecord) GetType() SFlowCounterRecordType { |
| switch bcr.Format { |
| case SFlowTypeGenericInterfaceCounters: |
| return SFlowTypeGenericInterfaceCounters |
| case SFlowTypeEthernetInterfaceCounters: |
| return SFlowTypeEthernetInterfaceCounters |
| case SFlowTypeTokenRingInterfaceCounters: |
| return SFlowTypeTokenRingInterfaceCounters |
| case SFlowType100BaseVGInterfaceCounters: |
| return SFlowType100BaseVGInterfaceCounters |
| case SFlowTypeVLANCounters: |
| return SFlowTypeVLANCounters |
| case SFlowTypeLACPCounters: |
| return SFlowTypeLACPCounters |
| case SFlowTypeProcessorCounters: |
| return SFlowTypeProcessorCounters |
| case SFlowTypeOpenflowPortCounters: |
| return SFlowTypeOpenflowPortCounters |
| case SFlowTypePORTNAMECounters: |
| return SFlowTypePORTNAMECounters |
| case SFLowTypeAPPRESOURCESCounters: |
| return SFLowTypeAPPRESOURCESCounters |
| case SFlowTypeOVSDPCounters: |
| return SFlowTypeOVSDPCounters |
| } |
| unrecognized := fmt.Sprint("Unrecognized counter record type:", bcr.Format) |
| panic(unrecognized) |
| } |
| |
| // ************************************************** |
| // Counter Record |
| // ************************************************** |
| |
| // 0 15 31 |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | 20 bit Interprise (0) |12 bit format | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | counter length | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | IfIndex | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | IfType | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | IfSpeed | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | IfDirection | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | IfStatus | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | IFInOctets | |
| // | | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | IfInUcastPkts | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | IfInMulticastPkts | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | IfInBroadcastPkts | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | IfInDiscards | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | InInErrors | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | IfInUnknownProtos | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | IfOutOctets | |
| // | | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | IfOutUcastPkts | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | IfOutMulticastPkts | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | IfOutBroadcastPkts | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | IfOutDiscards | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | IfOUtErrors | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | IfPromiscouousMode | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| |
| type SFlowGenericInterfaceCounters struct { |
| SFlowBaseCounterRecord |
| IfIndex uint32 |
| IfType uint32 |
| IfSpeed uint64 |
| IfDirection uint32 |
| IfStatus uint32 |
| IfInOctets uint64 |
| IfInUcastPkts uint32 |
| IfInMulticastPkts uint32 |
| IfInBroadcastPkts uint32 |
| IfInDiscards uint32 |
| IfInErrors uint32 |
| IfInUnknownProtos uint32 |
| IfOutOctets uint64 |
| IfOutUcastPkts uint32 |
| IfOutMulticastPkts uint32 |
| IfOutBroadcastPkts uint32 |
| IfOutDiscards uint32 |
| IfOutErrors uint32 |
| IfPromiscuousMode uint32 |
| } |
| |
| func decodeGenericInterfaceCounters(data *[]byte) (SFlowGenericInterfaceCounters, error) { |
| gic := SFlowGenericInterfaceCounters{} |
| var cdf SFlowCounterDataFormat |
| |
| *data, cdf = (*data)[4:], SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4])) |
| gic.EnterpriseID, gic.Format = cdf.decode() |
| *data, gic.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, gic.IfIndex = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, gic.IfType = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, gic.IfSpeed = (*data)[8:], binary.BigEndian.Uint64((*data)[:8]) |
| *data, gic.IfDirection = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, gic.IfStatus = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, gic.IfInOctets = (*data)[8:], binary.BigEndian.Uint64((*data)[:8]) |
| *data, gic.IfInUcastPkts = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, gic.IfInMulticastPkts = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, gic.IfInBroadcastPkts = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, gic.IfInDiscards = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, gic.IfInErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, gic.IfInUnknownProtos = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, gic.IfOutOctets = (*data)[8:], binary.BigEndian.Uint64((*data)[:8]) |
| *data, gic.IfOutUcastPkts = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, gic.IfOutMulticastPkts = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, gic.IfOutBroadcastPkts = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, gic.IfOutDiscards = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, gic.IfOutErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, gic.IfPromiscuousMode = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| return gic, nil |
| } |
| |
| // ************************************************** |
| // Counter Record |
| // ************************************************** |
| |
| // 0 15 31 |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | 20 bit Interprise (0) |12 bit format | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | counter length | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // / counter data / |
| // / / |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| |
| type SFlowEthernetCounters struct { |
| SFlowBaseCounterRecord |
| AlignmentErrors uint32 |
| FCSErrors uint32 |
| SingleCollisionFrames uint32 |
| MultipleCollisionFrames uint32 |
| SQETestErrors uint32 |
| DeferredTransmissions uint32 |
| LateCollisions uint32 |
| ExcessiveCollisions uint32 |
| InternalMacTransmitErrors uint32 |
| CarrierSenseErrors uint32 |
| FrameTooLongs uint32 |
| InternalMacReceiveErrors uint32 |
| SymbolErrors uint32 |
| } |
| |
| func decodeEthernetCounters(data *[]byte) (SFlowEthernetCounters, error) { |
| ec := SFlowEthernetCounters{} |
| var cdf SFlowCounterDataFormat |
| |
| *data, cdf = (*data)[4:], SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4])) |
| ec.EnterpriseID, ec.Format = cdf.decode() |
| *data, ec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, ec.AlignmentErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, ec.FCSErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, ec.SingleCollisionFrames = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, ec.MultipleCollisionFrames = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, ec.SQETestErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, ec.DeferredTransmissions = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, ec.LateCollisions = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, ec.ExcessiveCollisions = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, ec.InternalMacTransmitErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, ec.CarrierSenseErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, ec.FrameTooLongs = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, ec.InternalMacReceiveErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, ec.SymbolErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| return ec, nil |
| } |
| |
| // VLAN Counter |
| |
| type SFlowVLANCounters struct { |
| SFlowBaseCounterRecord |
| VlanID uint32 |
| Octets uint64 |
| UcastPkts uint32 |
| MulticastPkts uint32 |
| BroadcastPkts uint32 |
| Discards uint32 |
| } |
| |
| func decodeVLANCounters(data *[]byte) (SFlowVLANCounters, error) { |
| vc := SFlowVLANCounters{} |
| var cdf SFlowCounterDataFormat |
| |
| *data, cdf = (*data)[4:], SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4])) |
| vc.EnterpriseID, vc.Format = cdf.decode() |
| vc.EnterpriseID, vc.Format = cdf.decode() |
| *data, vc.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, vc.VlanID = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, vc.Octets = (*data)[8:], binary.BigEndian.Uint64((*data)[:8]) |
| *data, vc.UcastPkts = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, vc.MulticastPkts = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, vc.BroadcastPkts = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, vc.Discards = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| return vc, nil |
| } |
| |
| //SFLLACPportState : SFlow LACP Port State (All(4) - 32 bit) |
| type SFLLACPPortState struct { |
| PortStateAll uint32 |
| } |
| |
| //LACPcounters : LACP SFlow Counters ( 64 Bytes ) |
| type SFlowLACPCounters struct { |
| SFlowBaseCounterRecord |
| ActorSystemID net.HardwareAddr |
| PartnerSystemID net.HardwareAddr |
| AttachedAggID uint32 |
| LacpPortState SFLLACPPortState |
| LACPDUsRx uint32 |
| MarkerPDUsRx uint32 |
| MarkerResponsePDUsRx uint32 |
| UnknownRx uint32 |
| IllegalRx uint32 |
| LACPDUsTx uint32 |
| MarkerPDUsTx uint32 |
| MarkerResponsePDUsTx uint32 |
| } |
| |
| func decodeLACPCounters(data *[]byte) (SFlowLACPCounters, error) { |
| la := SFlowLACPCounters{} |
| var cdf SFlowCounterDataFormat |
| |
| *data, cdf = (*data)[4:], SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4])) |
| la.EnterpriseID, la.Format = cdf.decode() |
| *data, la.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, la.ActorSystemID = (*data)[6:], (*data)[:6] |
| *data = (*data)[2:] // remove padding |
| *data, la.PartnerSystemID = (*data)[6:], (*data)[:6] |
| *data = (*data)[2:] //remove padding |
| *data, la.AttachedAggID = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, la.LacpPortState.PortStateAll = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, la.LACPDUsRx = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, la.MarkerPDUsRx = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, la.MarkerResponsePDUsRx = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, la.UnknownRx = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, la.IllegalRx = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, la.LACPDUsTx = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, la.MarkerPDUsTx = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, la.MarkerResponsePDUsTx = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| |
| return la, nil |
| |
| } |
| |
| // ************************************************** |
| // Processor Counter Record |
| // ************************************************** |
| // 0 15 31 |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | 20 bit Interprise (0) |12 bit format | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | counter length | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | FiveSecCpu | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | OneMinCpu | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | GiveMinCpu | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | TotalMemory | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | FreeMemory | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| |
| type SFlowProcessorCounters struct { |
| SFlowBaseCounterRecord |
| FiveSecCpu uint32 // 5 second average CPU utilization |
| OneMinCpu uint32 // 1 minute average CPU utilization |
| FiveMinCpu uint32 // 5 minute average CPU utilization |
| TotalMemory uint64 // total memory (in bytes) |
| FreeMemory uint64 // free memory (in bytes) |
| } |
| |
| func decodeProcessorCounters(data *[]byte) (SFlowProcessorCounters, error) { |
| pc := SFlowProcessorCounters{} |
| var cdf SFlowCounterDataFormat |
| var high32, low32 uint32 |
| |
| *data, cdf = (*data)[4:], SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4])) |
| pc.EnterpriseID, pc.Format = cdf.decode() |
| *data, pc.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| |
| *data, pc.FiveSecCpu = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, pc.OneMinCpu = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, pc.FiveMinCpu = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, high32 = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, low32 = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| pc.TotalMemory = (uint64(high32) << 32) + uint64(low32) |
| *data, high32 = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, low32 = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| pc.FreeMemory = (uint64(high32)) + uint64(low32) |
| |
| return pc, nil |
| } |
| |
| // SFlowEthernetFrameFlowRecord give additional information |
| // about the sampled packet if it's available. |
| // An agent may or may not provide this information. |
| type SFlowEthernetFrameFlowRecord struct { |
| SFlowBaseFlowRecord |
| FrameLength uint32 |
| SrcMac net.HardwareAddr |
| DstMac net.HardwareAddr |
| Type uint32 |
| } |
| |
| // Ethernet frame flow records have the following structure: |
| |
| // 0 15 31 |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | 20 bit Interprise (0) |12 bit format | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | record length | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | Source Mac Address | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | Destination Mac Address | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| // | Ethernet Packet Type | |
| // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| |
| func decodeEthernetFrameFlowRecord(data *[]byte) (SFlowEthernetFrameFlowRecord, error) { |
| es := SFlowEthernetFrameFlowRecord{} |
| var fdf SFlowFlowDataFormat |
| |
| *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) |
| es.EnterpriseID, es.Format = fdf.decode() |
| *data, es.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| |
| *data, es.FrameLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, es.SrcMac = (*data)[8:], net.HardwareAddr((*data)[:6]) |
| *data, es.DstMac = (*data)[8:], net.HardwareAddr((*data)[:6]) |
| *data, es.Type = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| return es, nil |
| } |
| |
| //SFlowOpenflowPortCounters : OVS-Sflow OpenFlow Port Counter ( 20 Bytes ) |
| type SFlowOpenflowPortCounters struct { |
| SFlowBaseCounterRecord |
| DatapathID uint64 |
| PortNo uint32 |
| } |
| |
| func decodeOpenflowportCounters(data *[]byte) (SFlowOpenflowPortCounters, error) { |
| ofp := SFlowOpenflowPortCounters{} |
| var cdf SFlowCounterDataFormat |
| |
| *data, cdf = (*data)[4:], SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4])) |
| ofp.EnterpriseID, ofp.Format = cdf.decode() |
| *data, ofp.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, ofp.DatapathID = (*data)[8:], binary.BigEndian.Uint64((*data)[:8]) |
| *data, ofp.PortNo = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| |
| return ofp, nil |
| } |
| |
| //SFlowAppresourcesCounters : OVS_Sflow App Resources Counter ( 48 Bytes ) |
| type SFlowAppresourcesCounters struct { |
| SFlowBaseCounterRecord |
| UserTime uint32 |
| SystemTime uint32 |
| MemUsed uint64 |
| MemMax uint64 |
| FdOpen uint32 |
| FdMax uint32 |
| ConnOpen uint32 |
| ConnMax uint32 |
| } |
| |
| func decodeAppresourcesCounters(data *[]byte) (SFlowAppresourcesCounters, error) { |
| app := SFlowAppresourcesCounters{} |
| var cdf SFlowCounterDataFormat |
| |
| *data, cdf = (*data)[4:], SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4])) |
| app.EnterpriseID, app.Format = cdf.decode() |
| *data, app.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, app.UserTime = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, app.SystemTime = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, app.MemUsed = (*data)[8:], binary.BigEndian.Uint64((*data)[:8]) |
| *data, app.MemMax = (*data)[8:], binary.BigEndian.Uint64((*data)[:8]) |
| *data, app.FdOpen = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, app.FdMax = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, app.ConnOpen = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, app.ConnMax = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| |
| return app, nil |
| } |
| |
| //SFlowOVSDPCounters : OVS-Sflow DataPath Counter ( 32 Bytes ) |
| type SFlowOVSDPCounters struct { |
| SFlowBaseCounterRecord |
| NHit uint32 |
| NMissed uint32 |
| NLost uint32 |
| NMaskHit uint32 |
| NFlows uint32 |
| NMasks uint32 |
| } |
| |
| func decodeOVSDPCounters(data *[]byte) (SFlowOVSDPCounters, error) { |
| dp := SFlowOVSDPCounters{} |
| var cdf SFlowCounterDataFormat |
| |
| *data, cdf = (*data)[4:], SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4])) |
| dp.EnterpriseID, dp.Format = cdf.decode() |
| *data, dp.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, dp.NHit = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, dp.NMissed = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, dp.NLost = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, dp.NMaskHit = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, dp.NFlows = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| *data, dp.NMasks = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| |
| return dp, nil |
| } |
| |
| //SFlowPORTNAME : OVS-Sflow PORTNAME Counter Sampletype ( 20 Bytes ) |
| type SFlowPORTNAME struct { |
| SFlowBaseCounterRecord |
| Len uint32 |
| Str string |
| } |
| |
| func decodeString(data *[]byte) (len uint32, str string) { |
| *data, len = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| str = string((*data)[:len]) |
| if (len % 4) != 0 { |
| len += 4 - len%4 |
| } |
| *data = (*data)[len:] |
| return |
| } |
| |
| func decodePortnameCounters(data *[]byte) (SFlowPORTNAME, error) { |
| pn := SFlowPORTNAME{} |
| var cdf SFlowCounterDataFormat |
| |
| *data, cdf = (*data)[4:], SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4])) |
| pn.EnterpriseID, pn.Format = cdf.decode() |
| *data, pn.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) |
| pn.Len, pn.Str = decodeString(data) |
| |
| return pn, nil |
| } |