1
1
mirror of https://github.com/wader/fq.git synced 2024-12-27 07:24:48 +03:00
fq/format/inet/flowsdecoder/flowsdecoder.go
Mattias Wadman edd0ae198b tcp,flow: By default allow missing syn/ack for now
Is probably what you usually want
2022-01-07 12:02:38 +01:00

206 lines
5.1 KiB
Go

package flowsdecoder
// TODO: option to not allow missing syn/ack?
import (
"bytes"
"encoding/binary"
"net"
"github.com/google/gopacket"
"github.com/google/gopacket/ip4defrag"
"github.com/google/gopacket/layers"
"github.com/google/gopacket/reassembly"
)
type IPEndpoint struct {
IP net.IP
Port int
}
type TCPConnection struct {
ClientEndpoint IPEndpoint
ServerEndpoint IPEndpoint
ClientToServer *bytes.Buffer
ServerToClient *bytes.Buffer
tcpState *reassembly.TCPSimpleFSM
optChecker reassembly.TCPOptionCheck
net gopacket.Flow
transport gopacket.Flow
}
func (t *TCPConnection) Accept(tcp *layers.TCP, ci gopacket.CaptureInfo, dir reassembly.TCPFlowDirection, nextSeq reassembly.Sequence, start *bool, ac reassembly.AssemblerContext) bool {
// has ok state?
if !t.tcpState.CheckState(tcp, dir) {
// TODO: handle err?
return false
}
// has ok options?
if err := t.optChecker.Accept(tcp, ci, dir, nextSeq, start); err != nil {
// TODO: handle err?
return false
}
// TODO: checksum?
// accept
return true
}
func (t *TCPConnection) ReassembledSG(sg reassembly.ScatterGather, ac reassembly.AssemblerContext) {
dir, _, _, skip := sg.Info()
length, _ := sg.Lengths()
if skip == -1 {
// can't find where skip == -1 is documented but this is what gopacket reassemblydump does
// to allow missing syn/ack
} else if skip != 0 {
// stream has missing bytes
return
}
data := sg.Fetch(length)
switch dir {
case reassembly.TCPDirClientToServer:
t.ClientToServer.Write(data)
case reassembly.TCPDirServerToClient:
t.ServerToClient.Write(data)
}
}
func (t *TCPConnection) ReassemblyComplete(ac reassembly.AssemblerContext) bool {
// do not remove the connection to allow last ACK
return false
}
type IPV4Reassembled struct {
SourceIP net.IP
DestinationIP net.IP
Datagram []byte
}
func (fd *Decoder) New(net, transport gopacket.Flow, tcp *layers.TCP, ac reassembly.AssemblerContext) reassembly.Stream {
fsmOptions := reassembly.TCPSimpleFSMOptions{
SupportMissingEstablishment: true,
}
// TODO: get ip layer somehow?
// TODO: understand how gopacket handles broken/too short packets, seems like
// we can get here when lots of things are missing, assume zero port for now
var clientPort int
if len(transport.Src().Raw()) == 2 {
clientPort = int(binary.BigEndian.Uint16(transport.Src().Raw()))
}
var serverPort int
if len(transport.Dst().Raw()) == 2 {
serverPort = int(binary.BigEndian.Uint16(transport.Dst().Raw()))
}
stream := &TCPConnection{
ClientEndpoint: IPEndpoint{
IP: append([]byte(nil), net.Src().Raw()...),
Port: clientPort,
},
ServerEndpoint: IPEndpoint{
IP: append([]byte(nil), net.Dst().Raw()...),
Port: serverPort,
},
ClientToServer: &bytes.Buffer{},
ServerToClient: &bytes.Buffer{},
net: net,
transport: transport,
tcpState: reassembly.NewTCPSimpleFSM(fsmOptions),
optChecker: reassembly.NewTCPOptionCheck(),
}
fd.TCPConnections = append(fd.TCPConnections, stream)
return stream
}
type Decoder struct {
TCPConnections []*TCPConnection
IPV4Reassembled []IPV4Reassembled
ipv4Defrag *ip4defrag.IPv4Defragmenter
tcpAssembler *reassembly.Assembler
}
func New() *Decoder {
flowDecoder := &Decoder{}
streamPool := reassembly.NewStreamPool(flowDecoder)
tcpAssembler := reassembly.NewAssembler(streamPool)
flowDecoder.tcpAssembler = tcpAssembler
flowDecoder.ipv4Defrag = ip4defrag.NewIPv4Defragmenter()
return flowDecoder
}
func (fd *Decoder) SLLPacket(bs []byte) error {
return fd.packet(gopacket.NewPacket(bs, layers.LayerTypeLinuxSLL, gopacket.Lazy))
}
func (fd *Decoder) EthernetFrame(bs []byte) error {
return fd.packet(gopacket.NewPacket(bs, layers.LayerTypeEthernet, gopacket.Lazy))
}
func (fd *Decoder) LoopbackFrame(bs []byte) error {
return fd.packet(gopacket.NewPacket(bs, layers.LayerTypeLoopback, gopacket.Lazy))
}
func (fd *Decoder) packet(p gopacket.Packet) error {
// TODO: linkType
ip4Layer := p.Layer(layers.LayerTypeIPv4)
if ip4Layer != nil {
ip4, _ := ip4Layer.(*layers.IPv4)
l := ip4.Length
newIPv4, err := fd.ipv4Defrag.DefragIPv4(ip4)
if err != nil {
return err
} else if newIPv4 != nil {
// TODO: correct way to detect finished reassemble?
if newIPv4.Length != l {
// TODO: better way to reconstruct package?
sb := gopacket.NewSerializeBuffer()
b, _ := sb.PrependBytes(len(newIPv4.Payload))
copy(b, newIPv4.Payload)
if err := newIPv4.SerializeTo(sb, gopacket.SerializeOptions{
FixLengths: true,
ComputeChecksums: true,
}); err != nil {
return err
}
fd.IPV4Reassembled = append(fd.IPV4Reassembled, IPV4Reassembled{
SourceIP: ip4.SrcIP,
DestinationIP: ip4.DstIP,
Datagram: sb.Bytes(),
})
pb, ok := p.(gopacket.PacketBuilder)
if !ok {
panic("not a PacketBuilder")
}
nextDecoder := newIPv4.NextLayerType()
if err := nextDecoder.Decode(newIPv4.Payload, pb); err != nil {
return err
}
}
}
}
tcp := p.Layer(layers.LayerTypeTCP)
if tcp != nil {
tcp, _ := tcp.(*layers.TCP)
fd.tcpAssembler.Assemble(p.NetworkLayer().NetworkFlow(), tcp)
}
return nil
}
func (fd *Decoder) Flush() {
fd.tcpAssembler.FlushAll()
}