mirror of
https://github.com/akvorado/akvorado.git
synced 2025-12-11 22:14:02 +01:00
183 lines
4.8 KiB
Go
183 lines
4.8 KiB
Go
// SPDX-FileCopyrightText: 2023 Free Mobile
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
package decoder
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"net/netip"
|
|
|
|
"akvorado/common/helpers"
|
|
"akvorado/common/schema"
|
|
)
|
|
|
|
// ParseIPv4 parses an IPv4 packet and returns layer-3 length.
|
|
func ParseIPv4(sch *schema.Component, bf *schema.FlowMessage, data []byte) uint64 {
|
|
var l3length uint64
|
|
var proto uint8
|
|
if len(data) < 20 {
|
|
return 0
|
|
}
|
|
bf.AppendUint(schema.ColumnEType, helpers.ETypeIPv4)
|
|
l3length = uint64(binary.BigEndian.Uint16(data[2:4]))
|
|
bf.SrcAddr = DecodeIP(data[12:16])
|
|
bf.DstAddr = DecodeIP(data[16:20])
|
|
proto = data[9]
|
|
fragoffset := binary.BigEndian.Uint16(data[6:8]) & 0x1fff
|
|
if !sch.IsDisabled(schema.ColumnGroupL3L4) {
|
|
bf.AppendUint(schema.ColumnIPTos, uint64(data[1]))
|
|
bf.AppendUint(schema.ColumnIPTTL, uint64(data[8]))
|
|
bf.AppendUint(schema.ColumnIPFragmentID,
|
|
uint64(binary.BigEndian.Uint16(data[4:6])))
|
|
bf.AppendUint(schema.ColumnIPFragmentOffset,
|
|
uint64(fragoffset))
|
|
}
|
|
ihl := int((data[0] & 0xf) * 4)
|
|
if len(data) >= ihl {
|
|
data = data[ihl:]
|
|
} else {
|
|
data = data[:0]
|
|
}
|
|
bf.AppendUint(schema.ColumnProto, uint64(proto))
|
|
if fragoffset == 0 {
|
|
ParseL4(sch, bf, data, proto)
|
|
}
|
|
return l3length
|
|
}
|
|
|
|
// ParseIPv6 parses an IPv6 packet and returns layer-3 length.
|
|
func ParseIPv6(sch *schema.Component, bf *schema.FlowMessage, data []byte) uint64 {
|
|
var l3length uint64
|
|
var proto uint8
|
|
if len(data) < 40 {
|
|
return 0
|
|
}
|
|
l3length = uint64(binary.BigEndian.Uint16(data[4:6])) + 40
|
|
bf.AppendUint(schema.ColumnEType, helpers.ETypeIPv6)
|
|
bf.SrcAddr = DecodeIP(data[8:24])
|
|
bf.DstAddr = DecodeIP(data[24:40])
|
|
proto = data[6]
|
|
bf.AppendUint(schema.ColumnProto, uint64(proto))
|
|
if !sch.IsDisabled(schema.ColumnGroupL3L4) {
|
|
bf.AppendUint(schema.ColumnIPTos,
|
|
uint64(binary.BigEndian.Uint16(data[0:2])&0xff0>>4))
|
|
bf.AppendUint(schema.ColumnIPTTL, uint64(data[7]))
|
|
bf.AppendUint(schema.ColumnIPv6FlowLabel,
|
|
uint64(binary.BigEndian.Uint32(data[0:4])&0xfffff))
|
|
// TODO fragmentID/fragmentOffset are in a separate header
|
|
}
|
|
data = data[40:]
|
|
bf.AppendUint(schema.ColumnProto, uint64(proto))
|
|
ParseL4(sch, bf, data, proto)
|
|
return l3length
|
|
}
|
|
|
|
// ParseL4 parses L4 layer.
|
|
func ParseL4(sch *schema.Component, bf *schema.FlowMessage, data []byte, proto uint8) {
|
|
if proto == 6 || proto == 17 {
|
|
// UDP or TCP
|
|
if len(data) > 4 {
|
|
bf.AppendUint(schema.ColumnSrcPort,
|
|
uint64(binary.BigEndian.Uint16(data[0:2])))
|
|
bf.AppendUint(schema.ColumnDstPort,
|
|
uint64(binary.BigEndian.Uint16(data[2:4])))
|
|
}
|
|
}
|
|
if !sch.IsDisabled(schema.ColumnGroupL3L4) {
|
|
if proto == 6 {
|
|
// TCP
|
|
if len(data) > 13 {
|
|
bf.AppendUint(schema.ColumnTCPFlags,
|
|
uint64(data[13]))
|
|
}
|
|
} else if proto == 1 {
|
|
// ICMPv4
|
|
if len(data) > 2 {
|
|
bf.AppendUint(schema.ColumnICMPv4Type,
|
|
uint64(data[0]))
|
|
bf.AppendUint(schema.ColumnICMPv4Code,
|
|
uint64(data[1]))
|
|
}
|
|
} else if proto == 58 {
|
|
// ICMPv6
|
|
if len(data) > 2 {
|
|
bf.AppendUint(schema.ColumnICMPv6Type,
|
|
uint64(data[0]))
|
|
bf.AppendUint(schema.ColumnICMPv6Code,
|
|
uint64(data[1]))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ParseEthernet parses an Ethernet packet and returns L3 length.
|
|
func ParseEthernet(sch *schema.Component, bf *schema.FlowMessage, data []byte) uint64 {
|
|
if len(data) < 14 {
|
|
return 0
|
|
}
|
|
if !sch.IsDisabled(schema.ColumnGroupL2) {
|
|
bf.AppendUint(schema.ColumnDstMAC,
|
|
binary.BigEndian.Uint64([]byte{0, 0, data[0], data[1], data[2], data[3], data[4], data[5]}))
|
|
bf.AppendUint(schema.ColumnSrcMAC,
|
|
binary.BigEndian.Uint64([]byte{0, 0, data[6], data[7], data[8], data[9], data[10], data[11]}))
|
|
}
|
|
etherType := data[12:14]
|
|
data = data[14:]
|
|
var vlan uint16
|
|
for etherType[0] == 0x81 && etherType[1] == 0x00 {
|
|
// 802.1q
|
|
if len(data) < 4 {
|
|
return 0
|
|
}
|
|
if !sch.IsDisabled(schema.ColumnGroupL2) {
|
|
vlan = (uint16(data[0]&0xf) << 8) + uint16(data[1])
|
|
}
|
|
etherType = data[2:4]
|
|
data = data[4:]
|
|
}
|
|
if vlan != 0 && bf.SrcVlan == 0 {
|
|
bf.SrcVlan = vlan
|
|
}
|
|
if etherType[0] == 0x88 && etherType[1] == 0x47 {
|
|
// MPLS
|
|
mplsLabels := make([]uint32, 0, 5)
|
|
for {
|
|
if len(data) < 5 {
|
|
return 0
|
|
}
|
|
label := binary.BigEndian.Uint32(append([]byte{0}, data[:3]...)) >> 4
|
|
bottom := data[2] & 1
|
|
data = data[4:]
|
|
mplsLabels = append(mplsLabels, label)
|
|
if bottom == 1 || label <= 15 {
|
|
switch data[0] & 0xf0 >> 4 {
|
|
case 4:
|
|
etherType = []byte{0x8, 0x0}
|
|
case 6:
|
|
etherType = []byte{0x86, 0xdd}
|
|
default:
|
|
return 0
|
|
}
|
|
break
|
|
}
|
|
}
|
|
if len(mplsLabels) > 0 {
|
|
bf.AppendArrayUInt32(schema.ColumnMPLSLabels, mplsLabels)
|
|
}
|
|
}
|
|
if etherType[0] == 0x8 && etherType[1] == 0x0 {
|
|
return ParseIPv4(sch, bf, data)
|
|
} else if etherType[0] == 0x86 && etherType[1] == 0xdd {
|
|
return ParseIPv6(sch, bf, data)
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// DecodeIP decodes an IP address
|
|
func DecodeIP(b []byte) netip.Addr {
|
|
if ip, ok := netip.AddrFromSlice(b); ok {
|
|
return helpers.AddrTo6(ip)
|
|
}
|
|
return netip.Addr{}
|
|
}
|