1
0
mirror of https://github.com/netsampler/goflow2.git synced 2024-05-06 15:54:52 +00:00
Louis 0020934574 Observation domain (#98)
* netflow: Add observation domain and point to message

The ObservationDomainID and ObservationPointID are two IPFIX fields that
identify the entity that is capturing flows and can be used to enrich
the context around a specific sample.

Parse these fields from the sample and add them to the FlowMessage.

Signed-off-by: Adrian Moreno <amorenoz@redhat.com>
Co-authored-by: Adrian Moreno <amorenoz@redhat.com>
2022-07-20 21:27:51 -07:00

277 lines
6.1 KiB
Go

package common
import (
"encoding/binary"
"fmt"
"net"
"reflect"
"strings"
"github.com/golang/protobuf/proto"
)
const (
FORMAT_TYPE_UNKNOWN = iota
FORMAT_TYPE_STRING_FUNC
FORMAT_TYPE_STRING
FORMAT_TYPE_INTEGER
FORMAT_TYPE_IP
FORMAT_TYPE_MAC
FORMAT_TYPE_BYTES
)
var (
EtypeName = map[uint32]string{
0x806: "ARP",
0x800: "IPv4",
0x86dd: "IPv6",
}
ProtoName = map[uint32]string{
1: "ICMP",
6: "TCP",
17: "UDP",
58: "ICMPv6",
132: "SCTP",
}
IcmpTypeName = map[uint32]string{
0: "EchoReply",
3: "DestinationUnreachable",
8: "Echo",
9: "RouterAdvertisement",
10: "RouterSolicitation",
11: "TimeExceeded",
}
Icmp6TypeName = map[uint32]string{
1: "DestinationUnreachable",
2: "PacketTooBig",
3: "TimeExceeded",
128: "EchoRequest",
129: "EchoReply",
133: "RouterSolicitation",
134: "RouterAdvertisement",
}
TextFields = []string{
"Type",
"ObservationPointID",
"ObservationDomainID",
"TimeReceived",
"SequenceNum",
"SamplingRate",
"SamplerAddress",
"TimeFlowStart",
"TimeFlowEnd",
"TimeFlowStartMs",
"TimeFlowEndMs",
"Bytes",
"Packets",
"SrcAddr",
"DstAddr",
"Etype",
"Proto",
"SrcPort",
"DstPort",
"InIf",
"OutIf",
"SrcMac",
"DstMac",
"SrcVlan",
"DstVlan",
"VlanId",
"IngressVrfID",
"EgressVrfID",
"IPTos",
"ForwardingStatus",
"IPTTL",
"TCPFlags",
"IcmpType",
"IcmpCode",
"IPv6FlowLabel",
"FragmentId",
"FragmentOffset",
"BiFlowDirection",
"SrcAS",
"DstAS",
"NextHop",
"NextHopAS",
"SrcNet",
"DstNet",
}
TextFieldsTypes = []int{
FORMAT_TYPE_STRING_FUNC,
FORMAT_TYPE_INTEGER,
FORMAT_TYPE_INTEGER,
FORMAT_TYPE_INTEGER,
FORMAT_TYPE_INTEGER,
FORMAT_TYPE_INTEGER,
FORMAT_TYPE_IP,
FORMAT_TYPE_INTEGER,
FORMAT_TYPE_INTEGER,
FORMAT_TYPE_INTEGER,
FORMAT_TYPE_INTEGER,
FORMAT_TYPE_INTEGER,
FORMAT_TYPE_INTEGER,
FORMAT_TYPE_IP,
FORMAT_TYPE_IP,
FORMAT_TYPE_INTEGER,
FORMAT_TYPE_INTEGER,
FORMAT_TYPE_INTEGER,
FORMAT_TYPE_INTEGER,
FORMAT_TYPE_INTEGER,
FORMAT_TYPE_INTEGER,
FORMAT_TYPE_MAC,
FORMAT_TYPE_MAC,
FORMAT_TYPE_INTEGER,
FORMAT_TYPE_INTEGER,
FORMAT_TYPE_INTEGER,
FORMAT_TYPE_INTEGER,
FORMAT_TYPE_INTEGER,
FORMAT_TYPE_INTEGER,
FORMAT_TYPE_INTEGER,
FORMAT_TYPE_INTEGER,
FORMAT_TYPE_INTEGER,
FORMAT_TYPE_INTEGER,
FORMAT_TYPE_INTEGER,
FORMAT_TYPE_INTEGER,
FORMAT_TYPE_INTEGER,
FORMAT_TYPE_INTEGER,
FORMAT_TYPE_INTEGER,
FORMAT_TYPE_INTEGER,
FORMAT_TYPE_INTEGER,
FORMAT_TYPE_IP,
FORMAT_TYPE_INTEGER,
FORMAT_TYPE_INTEGER,
FORMAT_TYPE_INTEGER,
}
RenderExtras = []string{
"EtypeName",
"ProtoName",
"IcmpName",
}
RenderExtraCall = []RenderExtraFunction{
RenderExtraFunctionEtypeName,
RenderExtraFunctionProtoName,
RenderExtraFunctionIcmpName,
}
)
func AddTextField(name string, jtype int) {
TextFields = append(TextFields, name)
TextFieldsTypes = append(TextFieldsTypes, jtype)
}
type RenderExtraFunction func(proto.Message) string
func RenderExtraFetchNumbers(msg proto.Message, fields []string) []uint64 {
vfm := reflect.ValueOf(msg)
vfm = reflect.Indirect(vfm)
values := make([]uint64, len(fields))
for i, kf := range fields {
fieldValue := vfm.FieldByName(kf)
if fieldValue.IsValid() {
values[i] = fieldValue.Uint()
}
}
return values
}
func RenderExtraFunctionEtypeName(msg proto.Message) string {
num := RenderExtraFetchNumbers(msg, []string{"Etype"})
return EtypeName[uint32(num[0])]
}
func RenderExtraFunctionProtoName(msg proto.Message) string {
num := RenderExtraFetchNumbers(msg, []string{"Proto"})
return ProtoName[uint32(num[0])]
}
func RenderExtraFunctionIcmpName(msg proto.Message) string {
num := RenderExtraFetchNumbers(msg, []string{"Proto", "IcmpCode", "IcmpType"})
return IcmpCodeType(uint32(num[0]), uint32(num[1]), uint32(num[2]))
}
func IcmpCodeType(proto, icmpCode, icmpType uint32) string {
if proto == 1 {
return IcmpTypeName[icmpType]
} else if proto == 58 {
return Icmp6TypeName[icmpType]
}
return ""
}
func RenderIP(addr []byte) string {
if addr == nil || (len(addr) != 4 && len(addr) != 16) {
return ""
}
return net.IP(addr).String()
}
func FormatMessageReflectText(msg proto.Message, ext string) string {
return FormatMessageReflectCustom(msg, ext, "", " ", "=", false)
}
func FormatMessageReflectJSON(msg proto.Message, ext string) string {
return fmt.Sprintf("{%s}", FormatMessageReflectCustom(msg, ext, "\"", ",", ":", true))
}
func FormatMessageReflectCustom(msg proto.Message, ext, quotes, sep, sign string, null bool) string {
fstr := make([]string, len(TextFields)+len(RenderExtras))
vfm := reflect.ValueOf(msg)
vfm = reflect.Indirect(vfm)
var i int
for j, kf := range TextFields {
fieldValue := vfm.FieldByName(kf)
if fieldValue.IsValid() {
switch TextFieldsTypes[j] {
case FORMAT_TYPE_STRING_FUNC:
strMethod := fieldValue.MethodByName("String").Call([]reflect.Value{})
fstr[i] = fmt.Sprintf("%s%s%s%s%q", quotes, kf, quotes, sign, strMethod[0].String())
case FORMAT_TYPE_STRING:
fstr[i] = fmt.Sprintf("%s%s%s%s%q", quotes, kf, quotes, sign, fieldValue.String())
case FORMAT_TYPE_INTEGER:
fstr[i] = fmt.Sprintf("%s%s%s%s%d", quotes, kf, quotes, sign, fieldValue.Uint())
case FORMAT_TYPE_IP:
ip := fieldValue.Bytes()
fstr[i] = fmt.Sprintf("%s%s%s%s%q", quotes, kf, quotes, sign, RenderIP(ip))
case FORMAT_TYPE_MAC:
mac := make([]byte, 8)
binary.BigEndian.PutUint64(mac, fieldValue.Uint())
fstr[i] = fmt.Sprintf("%s%s%s%s%q", quotes, kf, quotes, sign, net.HardwareAddr(mac[2:]).String())
case FORMAT_TYPE_BYTES:
fstr[i] = fmt.Sprintf("%s%s%s%s%.2x", quotes, kf, quotes, sign, fieldValue.Bytes())
default:
if null {
fstr[i] = fmt.Sprintf("%s%s%s%snull", quotes, kf, quotes, sign)
}
}
} else {
if null {
fstr[i] = fmt.Sprintf("%s%s%s%snull", quotes, kf, quotes, sign)
}
}
if len(selectorMap) == 0 || selectorMap[kf] {
i++
}
}
for j, e := range RenderExtras {
fstr[i] = fmt.Sprintf("%s%s%s%s%q", quotes, e, quotes, sign, RenderExtraCall[j](msg))
if len(selectorMap) == 0 || selectorMap[e] {
i++
}
}
if len(selectorMap) > 0 {
fstr = fstr[0:i]
}
return strings.Join(fstr, sep)
}