mirror of
https://github.com/kubeshark/kubeshark.git
synced 2026-07-10 00:51:09 +00:00
* Separate HTTP related code into `extensions/http` as a Go plugin * Move `extensions` folder into `tap` folder * Move HTTP files into `tap/extensions/lib` for now * Replace `orcaman/concurrent-map` with `sync.Map` * Remove `grpc_assembler.go` * Remove `github.com/up9inc/mizu/tap/extensions/http/lib` * Add a build script to automatically build extensions from a known path and load them * Start to define the extension API * Implement the `run()` function for the TCP stream * Add support of defining multiple ports to the extension API * Set the extension name inside the extension * Declare the `Dissect` function in the extension API * Dissect HTTP request from inside the HTTP extension * Make the distinction of outbound and inbound ports * Dissect HTTP response from inside the HTTP extension * Bring back the HTTP request-response pair matcher * Return a `*api.RequestResponsePair` from the dissection * Bring back the gRPC-HTTP/2 parser * Fix the issues in `handleHTTP1ClientStream` and `handleHTTP1ServerStream` * Call a function pointer to emit dissected data back to the `tap` package * roee changes - trying to fix agent to work with the "api" object) - ***still not working*** * small mistake in the conflicts * Fix the issues that are introduced by the merge conflict * Add `Emitter` interface to the API and send `OutputChannelItem`(s) to `OutputChannel` * Fix the `HTTP1` handlers * Set `ConnectionInfo` in HTTP handlers * Fix the `Dockerfile` to build the extensions * remove some unwanted code * no message * Re-enable `getStreamProps` function * Migrate back from `gopacket/tcpassembly` to `gopacket/reassembly` * Introduce `HTTPPayload` struct and `HTTPPayloader` interface to `MarshalJSON()` all the data structures that are returned by the HTTP protocol * Read `socketHarOutChannel` instead of `filteredHarChannel` * Connect `OutputChannelItem` to the last WebSocket means that finally the web UI started to work again * Add `.env.example` to React app * Marshal and unmarshal `*http.Request`, `*http.Response` pairs * Move `loadExtensions` into `main.go` and map extensions into `extensionsMap` * Add `Summarize()` method to the `Dissector` interface * Add `Analyze` method to the `Dissector` interface and `MizuEntry` to the extension API * Add `Protocol` struct and make it effect the UI * Refactor `BaseEntryDetails` struct and display the source and destination ports in the UI * Display the protocol name inside the details layout * Add `Represent` method to the `Dissector` interface and manipulate the UI through this method * Make the protocol color affect the details layout color and write protocol abbreviation vertically * Remove everything HTTP related from the `tap` package and make the extension system fully functional * Fix the TypeScript warnings * Bring in the files related AMQP into `amqp` directory * Add `--nodefrag` flag to the tapper and bring in the main AMQP code * Implement the AMQP `BasicPublish` and fix some issues in the UI when the response payload is missing * Implement `representBasicPublish` method * Fix several minor issues * Implement the AMQP `BasicDeliver` * Implement the AMQP `QueueDeclare` * Implement the AMQP `ExchangeDeclare` * Implement the AMQP `ConnectionStart` * Implement the AMQP `ConnectionClose` * Implement the AMQP `QueueBind` * Implement the AMQP `BasicConsume` * Fix an issue in `ConnectionStart` * Fix a linter error * Bring in the files related Kafka into `kafka` directory * Fix the build errors in Kafka Go files * Implement `Dissect` method of Kafka and adapt request-response pair matcher to asynchronous client-server stream * Do the "Is reversed?" checked inside `getStreamProps` and fix an issue in Kafka `Dissect` method * Implement `Analyze`, `Summarize` methods of Kafka * Implement the representations for Kafka `Metadata`, `RequestHeader` and `ResponseHeader` * Refactor the AMQP and Kafka implementations to create the summary string only inside the `Analyze` method * Implement the representations for Kafka `ApiVersions` * Implement the representations for Kafka `Produce` * Implement the representations for Kafka `Fetch` * Implement the representations for Kafka `ListOffsets`, `CreateTopics` and `DeleteTopics` * Fix the encoding of AMQP `BasicPublish` and `BasicDeliver` body * Remove the unnecessary logging * Remove more logging * Introduce `Version` field to `Protocol` struct for dynamically switching the HTTP protocol to HTTP/2 * Fix the issues in analysis and representation of HTTP/2 (gRPC) protocol * Fix the issues in summary section of details layout for HTTP/2 (gRPC) protocol * Fix the read errors that freezes the sniffer in HTTP and Kafka * Fix the issues in HTTP POST data * Fix one more issue in HTTP POST data * Fix an infinite loop in Kafka * Fix another freezing issue in Kafka * Revert "UI Infra - Support multiple entry types + refactoring (#211)" This reverts commitf74a52d4dc. * Fix more issues that are introduced by the merge * Fix the status code in the summary section * adding the cleaner again (why we removed it?). add TODO: on the extension loop . * fix dockerfile (remove deleting .env file) - it is found in dockerignore and fails to build if the file not exists * fix GetEntrties ("/entries" endpoint) - working with "tapApi.BaseEntryDetail" (moved from shared) * Fix an issue in the UI summary section * Refactor the protocol payload structs * Fix a log message in the passive tapper * Adapt `APP_PORTS` environment variable to the new extension system and change its format to `APP_PORTS='{"http": ["8001"]}' ` * Revert "fix dockerfile (remove deleting .env file) - it is found in dockerignore and fails to build if the file not exists" This reverts commit4f514ae1f4. * Bring in the necessary changes fromf74a52d4dc* Open the API server URL in the web browser as soon as Mizu is ready * Make the TCP reader consists of a single Go routine (instead of two) and try to dissect in both client and server mode by rewinding * Swap `TcpID` without overwriting it * Sort extension by priority * Try to dissect with looping through all the extensions * fix getStreamProps function. (it should be passed from CLI as it was before). * Turn TCP reader back into two Goroutines (client and server) * typo * Learn `isClient` from the TCP stream * Set `viewer` style `overflow: "auto"` * Fix the memory leaks in AMQP and Kafka dissectors * Revert some of the changes inbe7c65eb6d* Remove `allExtensionPorts` since it's no longer needed * Remove `APP_PORTS` since it's no longer needed * Fix all of the minor issues in the React code * Check Kafka header size and fail-fast * Break the dissectors loop upon a successful dissection * Don't break the dissector loop. Protocols might collide * Improve the HTTP request-response counter (still not perfect) * Make the HTTP request-response counter perfect * Revert "Revert some of the changes in be7c65eb6d3fb657a059707da3ca559937e59739" This reverts commit08e7d786d8. * Bring back `filterItems` and `isHealthCheckByUserAgent` functions * Remove some development artifacts * remove unused and commented lines that are not relevant * Fix the performance in TCP stream factory. Make it create two `tcpReader`(s) per extension * Change a log to debug * Make `*api.CounterPair` a field of `tcpReader` * Set `isTapTarget` to always `true` again since `filterAuthorities` implementation has problems * Remove a variable that's only used for logging even though not introduced by this branch * Bring back the `NumberOfRules` field of `ApplicableRules` struct * Remove the unused `NewEntry` function * Move `k8sResolver == nil` check to a more appropriate place * default healthChecksUserAgentHeaders should be empty array (like the default config value) * remove spam console.log * Rules button cause app to crash (access the service via incorrect property) * Ignore all .env* files in docker build. * Better caching in dockerfile: only copy go.mod before go mod download. * Check for errors while loading an extension * Add a comment about why `Protocol` is not a pointer * Bring back the call to `deleteOlderThan` * Remove the `nil` check * Reduce the maximum allowed AMQP message from 128MB to 1MB * Fix an error that only occurs when a Kafka broker is initiating * Revert the change inb2abd7b990* Fix the service name resolution in all protocols * Remove the `anydirection` flag and fix the issue in `filterAuthorities` * Pass `sync.Map` by reference to `deleteOlderThan` method * Fix the packet capture issue in standalone mode that's introduced by the removal of `anydirection` * Temporarily resolve the memory exhaustion in AMQP * Fix a nil pointer dereference error * Fix the CLI build error * Fix a memory leak that's identified by `pprof` Co-authored-by: Roee Gadot <roee.gadot@up9.com> Co-authored-by: Nimrod Gilboa Markevich <nimrod@up9.com>
482 lines
14 KiB
Go
482 lines
14 KiB
Go
// Copyright 2012 Google, Inc. All rights reserved.
|
|
//
|
|
// Use of this source code is governed by a BSD-style license
|
|
// that can be found in the LICENSE file in the root of the source
|
|
// tree.
|
|
|
|
// The pcapdump binary implements a tcpdump-like command line tool with gopacket
|
|
// using pcap as a backend data collection mechanism.
|
|
package tap
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"os/signal"
|
|
"runtime"
|
|
"runtime/pprof"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/romana/rlog"
|
|
|
|
"github.com/google/gopacket"
|
|
"github.com/google/gopacket/examples/util"
|
|
"github.com/google/gopacket/ip4defrag"
|
|
"github.com/google/gopacket/layers" // pulls in all layers decoders
|
|
"github.com/google/gopacket/pcap"
|
|
"github.com/google/gopacket/reassembly"
|
|
"github.com/up9inc/mizu/tap/api"
|
|
)
|
|
|
|
const cleanPeriod = time.Second * 10
|
|
|
|
var remoteOnlyOutboundPorts = []int{80, 443}
|
|
|
|
var maxcount = flag.Int64("c", -1, "Only grab this many packets, then exit")
|
|
var decoder = flag.String("decoder", "", "Name of the decoder to use (default: guess from capture)")
|
|
var statsevery = flag.Int("stats", 60, "Output statistics every N seconds")
|
|
var lazy = flag.Bool("lazy", false, "If true, do lazy decoding")
|
|
var nodefrag = flag.Bool("nodefrag", false, "If true, do not do IPv4 defrag")
|
|
var checksum = flag.Bool("checksum", false, "Check TCP checksum") // global
|
|
var nooptcheck = flag.Bool("nooptcheck", true, "Do not check TCP options (useful to ignore MSS on captures with TSO)") // global
|
|
var ignorefsmerr = flag.Bool("ignorefsmerr", true, "Ignore TCP FSM errors") // global
|
|
var allowmissinginit = flag.Bool("allowmissinginit", true, "Support streams without SYN/SYN+ACK/ACK sequence") // global
|
|
var verbose = flag.Bool("verbose", false, "Be verbose")
|
|
var debug = flag.Bool("debug", false, "Display debug information")
|
|
var quiet = flag.Bool("quiet", false, "Be quiet regarding errors")
|
|
var hexdumppkt = flag.Bool("dumppkt", false, "Dump packet as hex")
|
|
|
|
// capture
|
|
var iface = flag.String("i", "en0", "Interface to read packets from")
|
|
var fname = flag.String("r", "", "Filename to read from, overrides -i")
|
|
var snaplen = flag.Int("s", 65536, "Snap length (number of bytes max to read per packet")
|
|
var tstype = flag.String("timestamp_type", "", "Type of timestamps to use")
|
|
var promisc = flag.Bool("promisc", true, "Set promiscuous mode")
|
|
var staleTimeoutSeconds = flag.Int("staletimout", 120, "Max time in seconds to keep connections which don't transmit data")
|
|
|
|
var memprofile = flag.String("memprofile", "", "Write memory profile")
|
|
|
|
var statsTracker = StatsTracker{}
|
|
|
|
// global
|
|
var stats struct {
|
|
ipdefrag int
|
|
missedBytes int
|
|
pkt int
|
|
sz int
|
|
totalsz int
|
|
rejectFsm int
|
|
rejectOpt int
|
|
rejectConnFsm int
|
|
reassembled int
|
|
outOfOrderBytes int
|
|
outOfOrderPackets int
|
|
biggestChunkBytes int
|
|
biggestChunkPackets int
|
|
overlapBytes int
|
|
overlapPackets int
|
|
}
|
|
|
|
type TapOpts struct {
|
|
HostMode bool
|
|
}
|
|
|
|
var outputLevel int
|
|
var errorsMap map[string]uint
|
|
var errorsMapMutex sync.Mutex
|
|
var nErrors uint
|
|
var ownIps []string // global
|
|
var hostMode bool // global
|
|
var extensions []*api.Extension // global
|
|
|
|
/* minOutputLevel: Error will be printed only if outputLevel is above this value
|
|
* t: key for errorsMap (counting errors)
|
|
* s, a: arguments log.Printf
|
|
* Note: Too bad for perf that a... is evaluated
|
|
*/
|
|
func logError(minOutputLevel int, t string, s string, a ...interface{}) {
|
|
errorsMapMutex.Lock()
|
|
nErrors++
|
|
nb, _ := errorsMap[t]
|
|
errorsMap[t] = nb + 1
|
|
errorsMapMutex.Unlock()
|
|
|
|
if outputLevel >= minOutputLevel {
|
|
formatStr := fmt.Sprintf("%s: %s", t, s)
|
|
rlog.Errorf(formatStr, a...)
|
|
}
|
|
}
|
|
func Error(t string, s string, a ...interface{}) {
|
|
logError(0, t, s, a...)
|
|
}
|
|
func SilentError(t string, s string, a ...interface{}) {
|
|
logError(2, t, s, a...)
|
|
}
|
|
func Debug(s string, a ...interface{}) {
|
|
rlog.Debugf(s, a...)
|
|
}
|
|
func Trace(s string, a ...interface{}) {
|
|
rlog.Tracef(1, s, a...)
|
|
}
|
|
|
|
func inArrayInt(arr []int, valueToCheck int) bool {
|
|
for _, value := range arr {
|
|
if value == valueToCheck {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func inArrayString(arr []string, valueToCheck string) bool {
|
|
for _, value := range arr {
|
|
if value == valueToCheck {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Context
|
|
// The assembler context
|
|
type Context struct {
|
|
CaptureInfo gopacket.CaptureInfo
|
|
}
|
|
|
|
func GetStats() AppStats {
|
|
return statsTracker.appStats
|
|
}
|
|
|
|
func (c *Context) GetCaptureInfo() gopacket.CaptureInfo {
|
|
return c.CaptureInfo
|
|
}
|
|
|
|
func StartPassiveTapper(opts *TapOpts, outputItems chan *api.OutputChannelItem, extensionsRef []*api.Extension) {
|
|
hostMode = opts.HostMode
|
|
extensions = extensionsRef
|
|
|
|
if GetMemoryProfilingEnabled() {
|
|
startMemoryProfiler()
|
|
}
|
|
|
|
go startPassiveTapper(outputItems)
|
|
}
|
|
|
|
func startMemoryProfiler() {
|
|
dirname := "/app/pprof"
|
|
rlog.Info("Profiling is on, results will be written to %s", dirname)
|
|
go func() {
|
|
if _, err := os.Stat(dirname); os.IsNotExist(err) {
|
|
if err := os.Mkdir(dirname, 0777); err != nil {
|
|
log.Fatal("could not create directory for profile: ", err)
|
|
}
|
|
}
|
|
|
|
for true {
|
|
t := time.Now()
|
|
|
|
filename := fmt.Sprintf("%s/%s__mem.prof", dirname, t.Format("15_04_05"))
|
|
|
|
rlog.Info("Writing memory profile to %s\n", filename)
|
|
|
|
f, err := os.Create(filename)
|
|
if err != nil {
|
|
log.Fatal("could not create memory profile: ", err)
|
|
}
|
|
runtime.GC() // get up-to-date statistics
|
|
if err := pprof.WriteHeapProfile(f); err != nil {
|
|
log.Fatal("could not write memory profile: ", err)
|
|
}
|
|
_ = f.Close()
|
|
time.Sleep(time.Minute)
|
|
}
|
|
}()
|
|
}
|
|
|
|
func startPassiveTapper(outputItems chan *api.OutputChannelItem) {
|
|
log.SetFlags(log.LstdFlags | log.LUTC | log.Lshortfile)
|
|
|
|
defer util.Run()()
|
|
if *debug {
|
|
outputLevel = 2
|
|
} else if *verbose {
|
|
outputLevel = 1
|
|
} else if *quiet {
|
|
outputLevel = -1
|
|
}
|
|
errorsMap = make(map[string]uint)
|
|
|
|
if localhostIPs, err := getLocalhostIPs(); err != nil {
|
|
// TODO: think this over
|
|
rlog.Info("Failed to get self IP addresses")
|
|
rlog.Errorf("Getting-Self-Address", "Error getting self ip address: %s (%v,%+v)", err, err, err)
|
|
ownIps = make([]string, 0)
|
|
} else {
|
|
ownIps = localhostIPs
|
|
}
|
|
|
|
var handle *pcap.Handle
|
|
var err error
|
|
if *fname != "" {
|
|
if handle, err = pcap.OpenOffline(*fname); err != nil {
|
|
log.Fatalf("PCAP OpenOffline error: %v", err)
|
|
}
|
|
} else {
|
|
// This is a little complicated because we want to allow all possible options
|
|
// for creating the packet capture handle... instead of all this you can
|
|
// just call pcap.OpenLive if you want a simple handle.
|
|
inactive, err := pcap.NewInactiveHandle(*iface)
|
|
if err != nil {
|
|
log.Fatalf("could not create: %v", err)
|
|
}
|
|
defer inactive.CleanUp()
|
|
if err = inactive.SetSnapLen(*snaplen); err != nil {
|
|
log.Fatalf("could not set snap length: %v", err)
|
|
} else if err = inactive.SetPromisc(*promisc); err != nil {
|
|
log.Fatalf("could not set promisc mode: %v", err)
|
|
} else if err = inactive.SetTimeout(time.Second); err != nil {
|
|
log.Fatalf("could not set timeout: %v", err)
|
|
}
|
|
if *tstype != "" {
|
|
if t, err := pcap.TimestampSourceFromString(*tstype); err != nil {
|
|
log.Fatalf("Supported timestamp types: %v", inactive.SupportedTimestamps())
|
|
} else if err := inactive.SetTimestampSource(t); err != nil {
|
|
log.Fatalf("Supported timestamp types: %v", inactive.SupportedTimestamps())
|
|
}
|
|
}
|
|
if handle, err = inactive.Activate(); err != nil {
|
|
log.Fatalf("PCAP Activate error: %v", err)
|
|
}
|
|
defer handle.Close()
|
|
}
|
|
if len(flag.Args()) > 0 {
|
|
bpffilter := strings.Join(flag.Args(), " ")
|
|
rlog.Infof("Using BPF filter %q", bpffilter)
|
|
if err = handle.SetBPFFilter(bpffilter); err != nil {
|
|
log.Fatalf("BPF filter error: %v", err)
|
|
}
|
|
}
|
|
|
|
var dec gopacket.Decoder
|
|
var ok bool
|
|
decoderName := *decoder
|
|
if decoderName == "" {
|
|
decoderName = fmt.Sprintf("%s", handle.LinkType())
|
|
}
|
|
if dec, ok = gopacket.DecodersByLayerName[decoderName]; !ok {
|
|
log.Fatalln("No decoder named", decoderName)
|
|
}
|
|
source := gopacket.NewPacketSource(handle, dec)
|
|
source.Lazy = *lazy
|
|
source.NoCopy = true
|
|
rlog.Info("Starting to read packets")
|
|
statsTracker.setStartTime(time.Now())
|
|
defragger := ip4defrag.NewIPv4Defragmenter()
|
|
|
|
var emitter api.Emitter = &api.Emitting{
|
|
OutputChannel: outputItems,
|
|
}
|
|
|
|
streamFactory := &tcpStreamFactory{
|
|
Emitter: emitter,
|
|
}
|
|
streamPool := reassembly.NewStreamPool(streamFactory)
|
|
assembler := reassembly.NewAssembler(streamPool)
|
|
|
|
maxBufferedPagesTotal := GetMaxBufferedPagesPerConnection()
|
|
maxBufferedPagesPerConnection := GetMaxBufferedPagesTotal()
|
|
rlog.Infof("Assembler options: maxBufferedPagesTotal=%d, maxBufferedPagesPerConnection=%d", maxBufferedPagesTotal, maxBufferedPagesPerConnection)
|
|
assembler.AssemblerOptions.MaxBufferedPagesTotal = maxBufferedPagesTotal
|
|
assembler.AssemblerOptions.MaxBufferedPagesPerConnection = maxBufferedPagesPerConnection
|
|
|
|
var assemblerMutex sync.Mutex
|
|
|
|
signalChan := make(chan os.Signal, 1)
|
|
signal.Notify(signalChan, os.Interrupt)
|
|
|
|
staleConnectionTimeout := time.Second * time.Duration(*staleTimeoutSeconds)
|
|
cleaner := Cleaner{
|
|
assembler: assembler,
|
|
assemblerMutex: &assemblerMutex,
|
|
cleanPeriod: cleanPeriod,
|
|
connectionTimeout: staleConnectionTimeout,
|
|
}
|
|
cleaner.start()
|
|
|
|
go func() {
|
|
statsPeriod := time.Second * time.Duration(*statsevery)
|
|
ticker := time.NewTicker(statsPeriod)
|
|
|
|
for true {
|
|
<-ticker.C
|
|
|
|
// Since the start
|
|
errorsMapMutex.Lock()
|
|
errorMapLen := len(errorsMap)
|
|
errorsSummery := fmt.Sprintf("%v", errorsMap)
|
|
errorsMapMutex.Unlock()
|
|
log.Printf("%v (errors: %v, errTypes:%v) - Errors Summary: %s",
|
|
time.Since(statsTracker.appStats.StartTime),
|
|
nErrors,
|
|
errorMapLen,
|
|
errorsSummery,
|
|
)
|
|
|
|
// At this moment
|
|
memStats := runtime.MemStats{}
|
|
runtime.ReadMemStats(&memStats)
|
|
log.Printf(
|
|
"mem: %d, goroutines: %d",
|
|
memStats.HeapAlloc,
|
|
runtime.NumGoroutine(),
|
|
)
|
|
|
|
// Since the last print
|
|
cleanStats := cleaner.dumpStats()
|
|
log.Printf(
|
|
"cleaner - flushed connections: %d, closed connections: %d, deleted messages: %d",
|
|
cleanStats.flushed,
|
|
cleanStats.closed,
|
|
cleanStats.deleted,
|
|
)
|
|
currentAppStats := statsTracker.dumpStats()
|
|
appStatsJSON, _ := json.Marshal(currentAppStats)
|
|
log.Printf("app stats - %v", string(appStatsJSON))
|
|
}
|
|
}()
|
|
|
|
if GetMemoryProfilingEnabled() {
|
|
startMemoryProfiler()
|
|
}
|
|
|
|
for packet := range source.Packets() {
|
|
packetsCount := statsTracker.incPacketsCount()
|
|
rlog.Debugf("PACKET #%d", packetsCount)
|
|
data := packet.Data()
|
|
statsTracker.updateProcessedBytes(int64(len(data)))
|
|
if *hexdumppkt {
|
|
rlog.Debugf("Packet content (%d/0x%x) - %s", len(data), len(data), hex.Dump(data))
|
|
}
|
|
|
|
// defrag the IPv4 packet if required
|
|
if !*nodefrag {
|
|
ip4Layer := packet.Layer(layers.LayerTypeIPv4)
|
|
if ip4Layer == nil {
|
|
continue
|
|
}
|
|
ip4 := ip4Layer.(*layers.IPv4)
|
|
l := ip4.Length
|
|
newip4, err := defragger.DefragIPv4(ip4)
|
|
if err != nil {
|
|
log.Fatalln("Error while de-fragmenting", err)
|
|
} else if newip4 == nil {
|
|
rlog.Debugf("Fragment...")
|
|
continue // packet fragment, we don't have whole packet yet.
|
|
}
|
|
if newip4.Length != l {
|
|
stats.ipdefrag++
|
|
rlog.Debugf("Decoding re-assembled packet: %s", newip4.NextLayerType())
|
|
pb, ok := packet.(gopacket.PacketBuilder)
|
|
if !ok {
|
|
log.Panic("Not a PacketBuilder")
|
|
}
|
|
nextDecoder := newip4.NextLayerType()
|
|
_ = nextDecoder.Decode(newip4.Payload, pb)
|
|
}
|
|
}
|
|
|
|
tcp := packet.Layer(layers.LayerTypeTCP)
|
|
if tcp != nil {
|
|
statsTracker.incTcpPacketsCount()
|
|
tcp := tcp.(*layers.TCP)
|
|
if *checksum {
|
|
err := tcp.SetNetworkLayerForChecksum(packet.NetworkLayer())
|
|
if err != nil {
|
|
log.Fatalf("Failed to set network layer for checksum: %s\n", err)
|
|
}
|
|
}
|
|
c := Context{
|
|
CaptureInfo: packet.Metadata().CaptureInfo,
|
|
}
|
|
stats.totalsz += len(tcp.Payload)
|
|
rlog.Debugf("%s : %v -> %s : %v", packet.NetworkLayer().NetworkFlow().Src(), tcp.SrcPort, packet.NetworkLayer().NetworkFlow().Dst(), tcp.DstPort)
|
|
assemblerMutex.Lock()
|
|
assembler.AssembleWithContext(packet.NetworkLayer().NetworkFlow(), tcp, &c)
|
|
assemblerMutex.Unlock()
|
|
}
|
|
|
|
done := *maxcount > 0 && statsTracker.appStats.PacketsCount >= *maxcount
|
|
if done {
|
|
errorsMapMutex.Lock()
|
|
errorMapLen := len(errorsMap)
|
|
errorsMapMutex.Unlock()
|
|
log.Printf("Processed %v packets (%v bytes) in %v (errors: %v, errTypes:%v)",
|
|
statsTracker.appStats.PacketsCount,
|
|
statsTracker.appStats.ProcessedBytes,
|
|
time.Since(statsTracker.appStats.StartTime),
|
|
nErrors,
|
|
errorMapLen)
|
|
}
|
|
select {
|
|
case <-signalChan:
|
|
log.Printf("Caught SIGINT: aborting")
|
|
done = true
|
|
default:
|
|
// NOP: continue
|
|
}
|
|
if done {
|
|
break
|
|
}
|
|
}
|
|
|
|
assemblerMutex.Lock()
|
|
closed := assembler.FlushAll()
|
|
assemblerMutex.Unlock()
|
|
rlog.Debugf("Final flush: %d closed", closed)
|
|
if outputLevel >= 2 {
|
|
streamPool.Dump()
|
|
}
|
|
|
|
if *memprofile != "" {
|
|
f, err := os.Create(*memprofile)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
_ = pprof.WriteHeapProfile(f)
|
|
_ = f.Close()
|
|
}
|
|
|
|
streamFactory.WaitGoRoutines()
|
|
assemblerMutex.Lock()
|
|
rlog.Debugf("%s", assembler.Dump())
|
|
assemblerMutex.Unlock()
|
|
if !*nodefrag {
|
|
log.Printf("IPdefrag:\t\t%d", stats.ipdefrag)
|
|
}
|
|
log.Printf("TCP stats:")
|
|
log.Printf(" missed bytes:\t\t%d", stats.missedBytes)
|
|
log.Printf(" total packets:\t\t%d", stats.pkt)
|
|
log.Printf(" rejected FSM:\t\t%d", stats.rejectFsm)
|
|
log.Printf(" rejected Options:\t%d", stats.rejectOpt)
|
|
log.Printf(" reassembled bytes:\t%d", stats.sz)
|
|
log.Printf(" total TCP bytes:\t%d", stats.totalsz)
|
|
log.Printf(" conn rejected FSM:\t%d", stats.rejectConnFsm)
|
|
log.Printf(" reassembled chunks:\t%d", stats.reassembled)
|
|
log.Printf(" out-of-order packets:\t%d", stats.outOfOrderPackets)
|
|
log.Printf(" out-of-order bytes:\t%d", stats.outOfOrderBytes)
|
|
log.Printf(" biggest-chunk packets:\t%d", stats.biggestChunkPackets)
|
|
log.Printf(" biggest-chunk bytes:\t%d", stats.biggestChunkBytes)
|
|
log.Printf(" overlap packets:\t%d", stats.overlapPackets)
|
|
log.Printf(" overlap bytes:\t\t%d", stats.overlapBytes)
|
|
log.Printf("Errors: %d", nErrors)
|
|
for e := range errorsMap {
|
|
log.Printf(" %s:\t\t%d", e, errorsMap[e])
|
|
}
|
|
log.Printf("AppStats: %v", GetStats())
|
|
}
|