mirror of
https://github.com/kubeshark/kubeshark.git
synced 2025-09-28 13:55:47 +00:00
* Use log in tap package instead of fmt. * Moved api/pkg/tap to root. * Added go.mod and go.sum for tap. * Added replace for shared. * api uses tap module instead of tap package. * Removed dependency of tap in shared by moving env var out of tap. * Fixed compilation bugs. * Fixed: Forgot to export struct field HostMode. * Removed unused flag. * Close har output channel when done. * Moved websocket out of mizu and into passive-tapper. * Send connection details over har output channel. * Fixed compilation errors. * Removed unused info from request response cache. * Renamed connection -> connectionID. * Fixed rename bug. * Export setters and getters for filter ips and ports. * Added tap dependency to Dockerfile. * Uncomment error messages. * Renamed `filterIpAddresses` -> `filterAuthorities`. * Renamed ConnectionID -> ConnectionInfo. * Fixed: Missed one replace.
169 lines
5.0 KiB
Go
169 lines
5.0 KiB
Go
package tap
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"sync"
|
|
|
|
"github.com/google/gopacket"
|
|
"github.com/google/gopacket/layers" // pulls in all layers decoders
|
|
"github.com/google/gopacket/reassembly"
|
|
)
|
|
|
|
/* It's a connection (bidirectional)
|
|
* Implements gopacket.reassembly.Stream interface (Accept, ReassembledSG, ReassemblyComplete)
|
|
* ReassembledSG gets called when new reassembled data is ready (i.e. bytes in order, no duplicates, complete)
|
|
* In our implementation, we pass information from ReassembledSG to the httpReader through a shared channel.
|
|
*/
|
|
type tcpStream struct {
|
|
tcpstate *reassembly.TCPSimpleFSM
|
|
fsmerr bool
|
|
optchecker reassembly.TCPOptionCheck
|
|
net, transport gopacket.Flow
|
|
isDNS bool
|
|
isHTTP bool
|
|
reversed bool
|
|
client httpReader
|
|
server httpReader
|
|
urls []string
|
|
ident string
|
|
sync.Mutex
|
|
}
|
|
|
|
func (t *tcpStream) Accept(tcp *layers.TCP, ci gopacket.CaptureInfo, dir reassembly.TCPFlowDirection, nextSeq reassembly.Sequence, start *bool, ac reassembly.AssemblerContext) bool {
|
|
// FSM
|
|
if !t.tcpstate.CheckState(tcp, dir) {
|
|
SilentError("FSM-rejection", "%s: Packet rejected by FSM (state:%s)", t.ident, t.tcpstate.String())
|
|
stats.rejectFsm++
|
|
if !t.fsmerr {
|
|
t.fsmerr = true
|
|
stats.rejectConnFsm++
|
|
}
|
|
if !*ignorefsmerr {
|
|
return false
|
|
}
|
|
}
|
|
// Options
|
|
err := t.optchecker.Accept(tcp, ci, dir, nextSeq, start)
|
|
if err != nil {
|
|
SilentError("OptionChecker-rejection", "%s: Packet rejected by OptionChecker: %s", t.ident, err)
|
|
stats.rejectOpt++
|
|
if !*nooptcheck {
|
|
return false
|
|
}
|
|
}
|
|
// Checksum
|
|
accept := true
|
|
if *checksum {
|
|
c, err := tcp.ComputeChecksum()
|
|
if err != nil {
|
|
SilentError("ChecksumCompute", "%s: Got error computing checksum: %s", t.ident, err)
|
|
accept = false
|
|
} else if c != 0x0 {
|
|
SilentError("Checksum", "%s: Invalid checksum: 0x%x", t.ident, c)
|
|
accept = false
|
|
}
|
|
}
|
|
if !accept {
|
|
stats.rejectOpt++
|
|
}
|
|
return accept
|
|
}
|
|
|
|
func (t *tcpStream) ReassembledSG(sg reassembly.ScatterGather, ac reassembly.AssemblerContext) {
|
|
dir, start, end, skip := sg.Info()
|
|
length, saved := sg.Lengths()
|
|
// update stats
|
|
sgStats := sg.Stats()
|
|
if skip > 0 {
|
|
stats.missedBytes += skip
|
|
}
|
|
stats.sz += length - saved
|
|
stats.pkt += sgStats.Packets
|
|
if sgStats.Chunks > 1 {
|
|
stats.reassembled++
|
|
}
|
|
stats.outOfOrderPackets += sgStats.QueuedPackets
|
|
stats.outOfOrderBytes += sgStats.QueuedBytes
|
|
if length > stats.biggestChunkBytes {
|
|
stats.biggestChunkBytes = length
|
|
}
|
|
if sgStats.Packets > stats.biggestChunkPackets {
|
|
stats.biggestChunkPackets = sgStats.Packets
|
|
}
|
|
if sgStats.OverlapBytes != 0 && sgStats.OverlapPackets == 0 {
|
|
// In the original example this was handled with panic().
|
|
// I don't know what this error means or how to handle it properly.
|
|
SilentError("Invalid-Overlap", "bytes:%d, pkts:%d", sgStats.OverlapBytes, sgStats.OverlapPackets)
|
|
}
|
|
stats.overlapBytes += sgStats.OverlapBytes
|
|
stats.overlapPackets += sgStats.OverlapPackets
|
|
|
|
var ident string
|
|
if dir == reassembly.TCPDirClientToServer {
|
|
ident = fmt.Sprintf("%v %v(%s): ", t.net, t.transport, dir)
|
|
} else {
|
|
ident = fmt.Sprintf("%v %v(%s): ", t.net.Reverse(), t.transport.Reverse(), dir)
|
|
}
|
|
Debug("%s: SG reassembled packet with %d bytes (start:%v,end:%v,skip:%d,saved:%d,nb:%d,%d,overlap:%d,%d)", ident, length, start, end, skip, saved, sgStats.Packets, sgStats.Chunks, sgStats.OverlapBytes, sgStats.OverlapPackets)
|
|
if skip == -1 && *allowmissinginit {
|
|
// this is allowed
|
|
} else if skip != 0 {
|
|
// Missing bytes in stream: do not even try to parse it
|
|
return
|
|
}
|
|
data := sg.Fetch(length)
|
|
if t.isDNS {
|
|
dns := &layers.DNS{}
|
|
var decoded []gopacket.LayerType
|
|
if len(data) < 2 {
|
|
if len(data) > 0 {
|
|
sg.KeepFrom(0)
|
|
}
|
|
return
|
|
}
|
|
dnsSize := binary.BigEndian.Uint16(data[:2])
|
|
missing := int(dnsSize) - len(data[2:])
|
|
Debug("dnsSize: %d, missing: %d", dnsSize, missing)
|
|
if missing > 0 {
|
|
Info("Missing some bytes: %d", missing)
|
|
sg.KeepFrom(0)
|
|
return
|
|
}
|
|
p := gopacket.NewDecodingLayerParser(layers.LayerTypeDNS, dns)
|
|
err := p.DecodeLayers(data[2:], &decoded)
|
|
if err != nil {
|
|
SilentError("DNS-parser", "Failed to decode DNS: %v", err)
|
|
} else {
|
|
Debug("DNS: %s", gopacket.LayerDump(dns))
|
|
}
|
|
if len(data) > 2+int(dnsSize) {
|
|
sg.KeepFrom(2 + int(dnsSize))
|
|
}
|
|
} else if t.isHTTP {
|
|
if length > 0 {
|
|
if *hexdump {
|
|
Debug("Feeding http with:%s", hex.Dump(data))
|
|
}
|
|
// This is where we pass the reassembled information onwards
|
|
// This channel is read by an httpReader object
|
|
if dir == reassembly.TCPDirClientToServer && !t.reversed {
|
|
t.client.msgQueue <- httpReaderDataMsg{data, ac.GetCaptureInfo().Timestamp}
|
|
} else {
|
|
t.server.msgQueue <- httpReaderDataMsg{data, ac.GetCaptureInfo().Timestamp}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (t *tcpStream) ReassemblyComplete(ac reassembly.AssemblerContext) bool {
|
|
Debug("%s: Connection closed", t.ident)
|
|
if t.isHTTP {
|
|
close(t.client.msgQueue)
|
|
close(t.server.msgQueue)
|
|
}
|
|
// do not remove the connection to allow last ACK
|
|
return false
|
|
}
|