1
1
mirror of https://github.com/wader/fq.git synced 2024-11-30 09:58:13 +03:00
fq/format/pcap/pcap.go
2023-05-01 13:19:04 +02:00

141 lines
3.6 KiB
Go

package pcap
// https://wiki.wireshark.org/Development/LibpcapFileFormat
// TODO: tshark seems to not support sll2 in pcap, confusing
import (
"embed"
"github.com/wader/fq/format"
"github.com/wader/fq/format/inet/flowsdecoder"
"github.com/wader/fq/pkg/decode"
"github.com/wader/fq/pkg/interp"
"github.com/wader/fq/pkg/scalar"
)
//go:embed pcap.md
var pcapFS embed.FS
var pcapLinkFrameGroup decode.Group
var pcapTCPStreamGroup decode.Group
var pcapIPv4PacketGroup decode.Group
// writing application writes 0xa1b2c3d4 in native endian
const (
// timestamp is seconds + microseconds
bigEndian = 0xa1b2c3d4
littleEndian = 0xd4c3b2a1
// timestamp is seconds + nanoseconds
bigEndianNS = 0xa1b23c4d
littleEndianNS = 0x4d3cb2a1
)
var endianMap = scalar.UintMapSymStr{
bigEndian: "big_endian",
littleEndian: "little_endian",
bigEndianNS: "big_endian_ns",
littleEndianNS: "little_endian_ns",
}
func init() {
interp.RegisterFormat(
format.PCAP,
&decode.Format{
Description: "PCAP packet capture",
Groups: []*decode.Group{format.Probe},
Dependencies: []decode.Dependency{
{Groups: []*decode.Group{format.Link_Frame}, Out: &pcapLinkFrameGroup},
{Groups: []*decode.Group{format.TCP_Stream}, Out: &pcapTCPStreamGroup},
{Groups: []*decode.Group{format.IPv4Packet}, Out: &pcapIPv4PacketGroup},
},
DecodeFn: decodePcap,
})
interp.RegisterFS(pcapFS)
}
func decodePcap(d *decode.D) any {
var endian decode.Endian
linkType := 0
timestampUNSStr := "ts_usec"
d.FieldStruct("header", func(d *decode.D) {
magic := d.FieldU32("magic", d.UintAssert(
bigEndian,
littleEndian,
bigEndianNS,
littleEndianNS,
), endianMap, scalar.UintHex)
switch magic {
case bigEndian:
endian = decode.BigEndian
case littleEndian:
endian = decode.LittleEndian
case bigEndianNS:
endian = decode.BigEndian
timestampUNSStr = "ts_nsec"
case littleEndianNS:
endian = decode.LittleEndian
timestampUNSStr = "ts_nsec"
}
d.Endian = endian
d.FieldU16("version_major")
d.FieldU16("version_minor")
d.FieldS32("thiszone")
d.FieldU32("sigfigs")
d.FieldU32("snaplen")
linkType = int(d.FieldU32("network", format.LinkTypeMap))
})
d.Endian = endian
fd := flowsdecoder.New(flowsdecoder.DecoderOptions{CheckTCPOptions: false})
d.FieldArray("packets", func(d *decode.D) {
for !d.End() {
d.FieldStruct("packet", func(d *decode.D) {
d.FieldU32("ts_sec")
d.FieldU32(timestampUNSStr)
inclLen := d.FieldU32("incl_len")
origLen := d.FieldU32("orig_len")
// "incl_len: the number of bytes of packet data actually captured and saved in the file. This value should never become larger than orig_len or the snaplen value of the global header"
// "orig_len: the length of the packet as it appeared on the network when it was captured. If incl_len and orig_len differ, the actually saved packet size was limited by snaplen."
// TODO: incl_len seems to be larger than snaplen in real pcap files
// if inclLen > snapLen {
// d.Errorf("incl_len %d > snaplen %d", inclLen, snapLen)
// }
if inclLen > origLen {
d.Errorf("incl_len %d > orig_len %d", inclLen, origLen)
}
bs := d.ReadAllBits(d.BitBufRange(d.Pos(), int64(inclLen)*8))
if fn, ok := linkToDecodeFn[linkType]; ok {
// TODO: report decode errors
_ = fn(fd, bs)
}
d.FieldFormatOrRawLen(
"packet",
int64(inclLen)*8,
&pcapLinkFrameGroup,
format.Link_Frame_In{
Type: linkType,
IsLittleEndian: d.Endian == decode.LittleEndian,
},
)
})
}
})
fd.Flush()
fieldFlows(d, fd, pcapTCPStreamGroup, pcapIPv4PacketGroup)
return nil
}