youtubebeat/vendor/github.com/elastic/beats/packetbeat/flows/flowid.go

499 lines
10 KiB
Go

// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package flows
import (
"bytes"
"encoding/base64"
"encoding/binary"
"net"
)
type FlowID struct {
rawFlowID
flow Flow // remember associated flow for faster lookup
}
type rawFlowID struct {
flowID []byte
flowIDMeta
dir flowDirection
}
type flowIDMeta struct {
flags FlowIDFlag
// offsets into flowID
offEth uint8
offOutterVlan uint8
offVlan uint8
offOutterIPv4 uint8
offIPv4 uint8
offOutterIPv6 uint8
offIPv6 uint8
offICMPv4 uint8
offICMPv6 uint8
offUDP uint8
offTCP uint8
offID uint8
cntEth uint8
cntVlan uint8
cntIP uint8
}
type FlowIDFlag uint16
const (
EthFlow FlowIDFlag = (1 << iota)
OutterVlanFlow
VLanFlow
OutterIPv4Flow
IPv4Flow
OutterIPv6Flow
IPv6Flow
ICMPv4Flow
ICMPv6Flow
UDPFlow
TCPFlow
ConnectionID
)
const (
SizeEthAddr = 6
SizeVlan = 2
SizeIPv4Addr = 4
SizeIPv6Addr = 16
SizeICMPID = 2
SizePortNumber = 2
SizeEthFlowID = 2 * SizeEthAddr // source + dest mac address
SizeVlanFlowID = SizeVlan // raw vlan id
SizeIPv4FlowID = 2 * SizeIPv4Addr // source + dest ip
SizeIPv6FlowID = 2 * SizeIPv6Addr // source + dest ip
SizeICMPFlowID = SizeICMPID // icmp identifier (if present)
SizeTCPFlowID = 2 * SizePortNumber // source + dest port
SizeUDPFlowID = 2 * SizePortNumber // source + dest port
SizeConnectionID = 8 // 64bit internal connection id
SizeFlowIDMax int = SizeEthFlowID +
2*(SizeVlanFlowID+SizeIPv4FlowID+SizeIPv6FlowID) +
SizeICMPFlowID +
SizeTCPFlowID +
SizeUDPFlowID +
SizeConnectionID
)
const offUnset uint8 = 0xff
var flowIDEmptyMeta = flowIDMeta{
flags: 0,
offEth: offUnset,
offOutterVlan: offUnset,
offVlan: offUnset,
offOutterIPv4: offUnset,
offIPv4: offUnset,
offOutterIPv6: offUnset,
offIPv6: offUnset,
offICMPv4: offUnset,
offICMPv6: offUnset,
offUDP: offUnset,
offTCP: offUnset,
offID: offUnset,
cntEth: 0,
cntVlan: 0,
cntIP: 0,
}
type flowDirection int8
const (
flowDirUnset flowDirection = iota - 1
flowDirForward
flowDirReversed
)
func init() {
if SizeFlowIDMax > 255 {
panic("SizeFlowIDMax exceeds size limit")
}
}
func newFlowID() *FlowID {
f := &FlowID{}
f.init()
return f
}
func (f *FlowID) init() {
f.Reset(nil)
}
func (f *FlowID) Reset(buf []byte) {
f.flowID = buf
f.flowIDMeta = flowIDEmptyMeta
f.dir = flowDirUnset
f.flow.stats = nil
}
func (f *FlowID) AddEth(src, dst net.HardwareAddr) {
debugf("flowid: add eth")
f.addID(&f.offEth, EthFlow, src, dst, flowDirUnset)
f.cntEth++
}
func (f *FlowID) AddIPv4(src, dst net.IP) {
debugf("flowid: add ipv4")
f.addMultLayerID(
&f.offIPv4, &f.offOutterIPv4,
IPv4Flow, OutterIPv4Flow,
src, dst, flowDirUnset)
f.cntIP++
}
func (f *FlowID) AddIPv6(src, dst net.IP) {
debugf("flowid: add ipv6")
f.addMultLayerID(
&f.offIPv6, &f.offOutterIPv6,
IPv6Flow, OutterIPv6Flow,
src, dst, flowDirUnset)
f.cntIP++
}
func (f *FlowID) AddVLan(id uint16) {
debugf("flowid: add vlan")
var tmp [2]byte
binary.LittleEndian.PutUint16(tmp[:], id)
f.addMultLayerID(
&f.offVlan, &f.offOutterVlan,
VLanFlow, OutterVlanFlow,
tmp[:], nil, flowDirUnset)
f.cntVlan++
}
func (f *FlowID) AddICMPv4Request(id uint16) {
debugf("flowid: add icmp4 request")
var tmp [2]byte
binary.LittleEndian.PutUint16(tmp[:], id)
f.addID(&f.offICMPv4, ICMPv4Flow, tmp[:], nil, flowDirForward)
}
func (f *FlowID) AddICMPv4Response(id uint16) {
debugf("flowid: add icmp4 response")
var tmp [2]byte
binary.LittleEndian.PutUint16(tmp[:], id)
f.addID(&f.offICMPv4, ICMPv4Flow, tmp[:], nil, flowDirReversed)
}
func (f *FlowID) AddICMPv6Request(id uint16) {
debugf("flowid: add icmp6 request")
var tmp [2]byte
binary.LittleEndian.PutUint16(tmp[:], id)
f.addID(&f.offICMPv6, ICMPv6Flow, tmp[:], nil, flowDirForward)
}
func (f *FlowID) AddICMPv6Response(id uint16) {
debugf("flowid: add icmp6 response")
var tmp [2]byte
binary.LittleEndian.PutUint16(tmp[:], id)
f.addID(&f.offICMPv6, ICMPv6Flow, tmp[:], nil, flowDirReversed)
}
func (f *FlowID) AddUDP(src, dst uint16) {
debugf("flowid: add udp")
f.addWithPorts(&f.offUDP, UDPFlow, src, dst)
}
func (f *FlowID) AddTCP(src, dst uint16) {
debugf("flowid: add tcp")
f.addWithPorts(&f.offTCP, TCPFlow, src, dst)
}
func (f *FlowID) AddConnectionID(id uint64) {
debugf("flowid: add tcp connection id")
var tmp [8]byte
binary.LittleEndian.PutUint64(tmp[:], id)
f.addID(&f.offID, ConnectionID, tmp[:], nil, flowDirUnset)
}
func (f *FlowID) addMultLayerID(
off, outerOff *uint8,
flag, outerFlag FlowIDFlag,
a, b []byte,
hint flowDirection,
) {
a, b = f.sortAddrWrite(a, b, hint)
flags := f.flags & (flag | outerFlag)
switch flags {
case flag | outerFlag:
*outerOff, *off = *off, *outerOff
la := copy(f.flowID[(*off):], a)
copy(f.flowID[la+int(*off):], b)
case flag:
*outerOff = *off
*off = uint8(len(f.flowID))
f.flowID = append(append(f.flowID, a...), b...)
f.flags |= outerFlag
default:
*off = uint8(len(f.flowID))
f.flowID = append(append(f.flowID, a...), b...)
f.flags |= flag
}
}
func (f *FlowID) addID(
off *uint8,
flag FlowIDFlag,
a, b []byte,
hint flowDirection,
) {
a, b = f.sortAddrWrite(a, b, hint)
if *off == offUnset {
*off = uint8(len(f.flowID))
f.flowID = append(append(f.flowID, a...), b...)
f.flags |= flag
} else {
la := copy(f.flowID[(*off):], a)
copy(f.flowID[la+int(*off):], b)
}
}
func (f *FlowID) addWithPorts(
off *uint8,
flag FlowIDFlag,
src, dst uint16,
) {
var a, b [2]byte
binary.LittleEndian.PutUint16(a[:], src)
binary.LittleEndian.PutUint16(b[:], dst)
f.addID(off, flag, a[:], b[:], flowDirUnset)
}
func (f *FlowID) sortAddrWrite(a, b []byte, hint flowDirection) ([]byte, []byte) {
if b == nil {
if f.dir == flowDirUnset {
f.dir = hint
}
return a, b
}
switch f.dir {
case flowDirForward:
return a, b
case flowDirReversed:
return b, a
}
switch bytes.Compare(a, b) {
case -1:
f.dir = flowDirForward
case 1:
f.dir = flowDirReversed
a, b = b, a
case 0:
f.dir = hint
}
return a, b
}
func (f rawFlowID) clone() rawFlowID {
n := f
n.flowID = make([]byte, len(f.flowID))
copy(n.flowID, f.flowID)
return n
}
func FlowIDsEqual(f1, f2 *FlowID) bool {
return f1.flags == f2.flags && bytes.Equal(f1.flowID, f2.flowID)
}
func (f *rawFlowID) Flags() FlowIDFlag {
return f.flags
}
func (f *rawFlowID) Get(i FlowIDFlag) []byte {
switch i {
case EthFlow:
return f.Eth()
case OutterVlanFlow:
return f.OutterVLan()
case VLanFlow:
return f.VLan()
case OutterIPv4Flow:
return f.OutterIPv4()
case OutterIPv6Flow:
return f.OutterIPv6()
case IPv4Flow:
return f.IPv4()
case IPv6Flow:
return f.IPv6()
case ICMPv4Flow:
return f.ICMPv4()
case ICMPv6Flow:
return f.ICMPv6()
case UDPFlow:
return f.UDP()
case TCPFlow:
return f.TCP()
default:
return nil
}
}
func (f *rawFlowID) Serialize() []byte {
buf := bytes.NewBuffer(nil)
enc := base64.NewEncoder(base64.RawStdEncoding, buf)
enc.Write([]byte{
byte(f.flags & 0xff),
byte(f.flags >> 8),
f.offEth,
f.offOutterVlan,
f.offVlan,
f.offOutterIPv4,
f.offIPv4,
f.offOutterIPv6,
f.offIPv6,
f.offICMPv4,
f.offICMPv6,
f.offUDP,
f.offTCP,
f.offID,
f.cntEth,
f.cntVlan,
f.cntIP,
})
enc.Write(f.flowID)
enc.Close()
return buf.Bytes()
}
func (f *rawFlowID) Eth() []byte {
return f.extractID(f.offEth, SizeEthFlowID)
}
func (f rawFlowID) EthAddr() ([]byte, []byte, bool) {
return f.sortAddrRead(f.offEth, SizeEthAddr)
}
func (f *rawFlowID) OutterVLan() []byte {
return f.extractID(f.offOutterVlan, SizeVlanFlowID)
}
func (f *rawFlowID) VLan() []byte {
return f.extractID(f.offVlan, SizeVlanFlowID)
}
func (f *rawFlowID) OutterIPv4() []byte {
return f.extractID(f.offOutterIPv4, SizeIPv4FlowID)
}
func (f *rawFlowID) OutterIPv4Addr() ([]byte, []byte, bool) {
return f.sortAddrRead(f.offOutterIPv4, SizeIPv4Addr)
}
func (f *rawFlowID) IPv4() []byte {
return f.extractID(f.offIPv4, SizeIPv4FlowID)
}
func (f *rawFlowID) IPv4Addr() ([]byte, []byte, bool) {
return f.sortAddrRead(f.offIPv4, SizeIPv4Addr)
}
func (f *rawFlowID) OutterIPv6() []byte {
return f.extractID(f.offOutterIPv6, SizeIPv6FlowID)
}
func (f *rawFlowID) OutterIPv6Addr() ([]byte, []byte, bool) {
return f.sortAddrRead(f.offOutterIPv6, SizeIPv6Addr)
}
func (f *rawFlowID) IPv6() []byte {
return f.extractID(f.offIPv6, SizeIPv6FlowID)
}
func (f *rawFlowID) IPv6Addr() ([]byte, []byte, bool) {
return f.sortAddrRead(f.offIPv6, SizeIPv6Addr)
}
func (f *rawFlowID) ICMPv4() []byte {
return f.extractID(f.offICMPv4, SizeICMPFlowID)
}
func (f *rawFlowID) ICMPv6() []byte {
return f.extractID(f.offICMPv6, SizeICMPFlowID)
}
func (f *rawFlowID) UDP() []byte {
return f.extractID(f.offUDP, SizeUDPFlowID)
}
func (f *rawFlowID) UDPAddr() ([]byte, []byte, bool) {
return f.sortAddrRead(f.offUDP, SizePortNumber)
}
func (f *rawFlowID) TCP() []byte {
return f.extractID(f.offTCP, SizeTCPFlowID)
}
func (f *rawFlowID) TCPAddr() ([]byte, []byte, bool) {
return f.sortAddrRead(f.offTCP, SizePortNumber)
}
func (f *rawFlowID) ConnectionID() []byte {
return f.extractID(f.offID, SizeConnectionID)
}
func (f *rawFlowID) extractID(off, sz uint8) []byte {
if off == offUnset {
return nil
}
{
off := int(off)
sz := int(sz)
return f.flowID[off : off+sz]
}
}
func (f *rawFlowID) sortAddrRead(off, sz uint8) ([]byte, []byte, bool) {
if off == offUnset {
return nil, nil, false
}
tmp := f.flowID[int(off) : int(off)+2*int(sz)]
if f.dir == flowDirReversed {
return tmp[int(sz):], tmp[:int(sz)], true
}
return tmp[:int(sz)], tmp[int(sz):], true
}