mirror of
https://github.com/wader/fq.git
synced 2024-11-30 09:58:13 +03:00
141 lines
3.6 KiB
Go
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
|
|
}
|