aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/google/gopacket/examples/reassemblydump/main.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/google/gopacket/examples/reassemblydump/main.go')
-rw-r--r--vendor/github.com/google/gopacket/examples/reassemblydump/main.go650
1 files changed, 0 insertions, 650 deletions
diff --git a/vendor/github.com/google/gopacket/examples/reassemblydump/main.go b/vendor/github.com/google/gopacket/examples/reassemblydump/main.go
deleted file mode 100644
index 9fc3791..0000000
--- a/vendor/github.com/google/gopacket/examples/reassemblydump/main.go
+++ /dev/null
@@ -1,650 +0,0 @@
-// 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 main
-
-import (
- "bufio"
- "bytes"
- "compress/gzip"
- "encoding/binary"
- "encoding/hex"
- "flag"
- "fmt"
- "io"
- "io/ioutil"
- "log"
- "net/http"
- "net/url"
- "os"
- "os/signal"
- "path"
- "runtime/pprof"
- "strings"
- "sync"
- "time"
-
- "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"
-)
-
-var maxcount = flag.Int("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", 1000, "Output statistics every N packets")
-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")
-var nooptcheck = flag.Bool("nooptcheck", false, "Do not check TCP options (useful to ignore MSS on captures with TSO)")
-var ignorefsmerr = flag.Bool("ignorefsmerr", false, "Ignore TCP FSM errors")
-var allowmissinginit = flag.Bool("allowmissinginit", false, "Support streams without SYN/SYN+ACK/ACK sequence")
-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")
-
-// http
-var nohttp = flag.Bool("nohttp", false, "Disable HTTP parsing")
-var output = flag.String("output", "", "Path to create file for HTTP 200 OK responses")
-var writeincomplete = flag.Bool("writeincomplete", false, "Write incomplete response")
-
-var hexdump = flag.Bool("dump", false, "Dump HTTP request/response as hex")
-var hexdumppkt = flag.Bool("dumppkt", false, "Dump packet as hex")
-
-// capture
-var iface = flag.String("i", "eth0", "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 memprofile = flag.String("memprofile", "", "Write memory profile")
-
-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
-}
-
-const closeTimeout time.Duration = time.Hour * 24 // Closing inactive: TODO: from CLI
-const timeout time.Duration = time.Minute * 5 // Pending bytes: TODO: from CLI
-
-/*
- * HTTP part
- */
-
-type httpReader struct {
- ident string
- isClient bool
- bytes chan []byte
- data []byte
- hexdump bool
- parent *tcpStream
-}
-
-func (h *httpReader) Read(p []byte) (int, error) {
- ok := true
- for ok && len(h.data) == 0 {
- h.data, ok = <-h.bytes
- }
- if !ok || len(h.data) == 0 {
- return 0, io.EOF
- }
-
- l := copy(p, h.data)
- h.data = h.data[l:]
- return l, nil
-}
-
-var outputLevel int
-var errorsMap map[string]uint
-var errors uint
-
-// Too bad for perf that a... is evaluated
-func Error(t string, s string, a ...interface{}) {
- errors++
- nb, _ := errorsMap[t]
- errorsMap[t] = nb + 1
- if outputLevel >= 0 {
- fmt.Printf(s, a...)
- }
-}
-func Info(s string, a ...interface{}) {
- if outputLevel >= 1 {
- fmt.Printf(s, a...)
- }
-}
-func Debug(s string, a ...interface{}) {
- if outputLevel >= 2 {
- fmt.Printf(s, a...)
- }
-}
-
-func (h *httpReader) run(wg *sync.WaitGroup) {
- defer wg.Done()
- b := bufio.NewReader(h)
- for true {
- if h.isClient {
- req, err := http.ReadRequest(b)
- if err == io.EOF || err == io.ErrUnexpectedEOF {
- break
- } else if err != nil {
- Error("HTTP-request", "HTTP/%s Request error: %s (%v,%+v)\n", h.ident, err, err, err)
- continue
- }
- body, err := ioutil.ReadAll(req.Body)
- s := len(body)
- if err != nil {
- Error("HTTP-request-body", "Got body err: %s\n", err)
- } else if h.hexdump {
- Info("Body(%d/0x%x)\n%s\n", len(body), len(body), hex.Dump(body))
- }
- req.Body.Close()
- Info("HTTP/%s Request: %s %s (body:%d)\n", h.ident, req.Method, req.URL, s)
- h.parent.urls = append(h.parent.urls, req.URL.String())
- } else {
- res, err := http.ReadResponse(b, nil)
- var req string
- if len(h.parent.urls) == 0 {
- req = fmt.Sprintf("<no-request-seen>")
- } else {
- req, h.parent.urls = h.parent.urls[0], h.parent.urls[1:]
- }
- if err == io.EOF || err == io.ErrUnexpectedEOF {
- break
- } else if err != nil {
- Error("HTTP-response", "HTTP/%s Response error: %s (%v,%+v)\n", h.ident, err, err, err)
- continue
- }
- body, err := ioutil.ReadAll(res.Body)
- s := len(body)
- if err != nil {
- Error("HTTP-response-body", "HTTP/%s: failed to get body(parsed len:%d): %s\n", h.ident, s, err)
- }
- if h.hexdump {
- Info("Body(%d/0x%x)\n%s\n", len(body), len(body), hex.Dump(body))
- }
- res.Body.Close()
- sym := ","
- if res.ContentLength > 0 && res.ContentLength != int64(s) {
- sym = "!="
- }
- contentType, ok := res.Header["Content-Type"]
- if !ok {
- contentType = []string{http.DetectContentType(body)}
- }
- encoding := res.Header["Content-Encoding"]
- Info("HTTP/%s Response: %s URL:%s (%d%s%d%s) -> %s\n", h.ident, res.Status, req, res.ContentLength, sym, s, contentType, encoding)
- if (err == nil || *writeincomplete) && *output != "" {
- base := url.QueryEscape(path.Base(req))
- if err != nil {
- base = "incomplete-" + base
- }
- base = path.Join(*output, base)
- if len(base) > 250 {
- base = base[:250] + "..."
- }
- if base == *output {
- base = path.Join(*output, "noname")
- }
- target := base
- n := 0
- for true {
- _, err := os.Stat(target)
- //if os.IsNotExist(err) != nil {
- if err != nil {
- break
- }
- target = fmt.Sprintf("%s-%d", base, n)
- n++
- }
- f, err := os.Create(target)
- if err != nil {
- Error("HTTP-create", "Cannot create %s: %s\n", target, err)
- continue
- }
- var r io.Reader
- r = bytes.NewBuffer(body)
- if len(encoding) > 0 && (encoding[0] == "gzip" || encoding[0] == "deflate") {
- r, err = gzip.NewReader(r)
- if err != nil {
- Error("HTTP-gunzip", "Failed to gzip decode: %s", err)
- }
- }
- if err == nil {
- w, err := io.Copy(f, r)
- if _, ok := r.(*gzip.Reader); ok {
- r.(*gzip.Reader).Close()
- }
- f.Close()
- if err != nil {
- Error("HTTP-save", "%s: failed to save %s (l:%d): %s\n", h.ident, target, w, err)
- } else {
- Info("%s: Saved %s (l:%d)\n", h.ident, target, w)
- }
- }
- }
- }
- }
-}
-
-/*
- * The TCP factory: returns a new Stream
- */
-type tcpStreamFactory struct {
- wg sync.WaitGroup
- doHTTP bool
-}
-
-func (factory *tcpStreamFactory) New(net, transport gopacket.Flow, tcp *layers.TCP, ac reassembly.AssemblerContext) reassembly.Stream {
- Debug("* NEW: %s %s\n", net, transport)
- fsmOptions := reassembly.TCPSimpleFSMOptions{
- SupportMissingEstablishment: *allowmissinginit,
- }
- stream := &tcpStream{
- net: net,
- transport: transport,
- isDNS: tcp.SrcPort == 53 || tcp.DstPort == 53,
- isHTTP: (tcp.SrcPort == 80 || tcp.DstPort == 80) && factory.doHTTP,
- reversed: tcp.SrcPort == 80,
- tcpstate: reassembly.NewTCPSimpleFSM(fsmOptions),
- ident: fmt.Sprintf("%s:%s", net, transport),
- optchecker: reassembly.NewTCPOptionCheck(),
- }
- if stream.isHTTP {
- stream.client = httpReader{
- bytes: make(chan []byte),
- ident: fmt.Sprintf("%s %s", net, transport),
- hexdump: *hexdump,
- parent: stream,
- isClient: true,
- }
- stream.server = httpReader{
- bytes: make(chan []byte),
- ident: fmt.Sprintf("%s %s", net.Reverse(), transport.Reverse()),
- hexdump: *hexdump,
- parent: stream,
- }
- factory.wg.Add(2)
- go stream.client.run(&factory.wg)
- go stream.server.run(&factory.wg)
- }
- return stream
-}
-
-func (factory *tcpStreamFactory) WaitGoRoutines() {
- factory.wg.Wait()
-}
-
-/*
- * The assembler context
- */
-type Context struct {
- CaptureInfo gopacket.CaptureInfo
-}
-
-func (c *Context) GetCaptureInfo() gopacket.CaptureInfo {
- return c.CaptureInfo
-}
-
-/*
- * TCP stream
- */
-
-/* It's a connection (bidirectional) */
-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
-}
-
-func (t *tcpStream) Accept(tcp *layers.TCP, ci gopacket.CaptureInfo, dir reassembly.TCPFlowDirection, acked reassembly.Sequence, start *bool, ac reassembly.AssemblerContext) bool {
- // FSM
- if !t.tcpstate.CheckState(tcp, dir) {
- Error("FSM", "%s: Packet rejected by FSM (state:%s)\n", 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, acked, start)
- if err != nil {
- Error("OptionChecker", "%s: Packet rejected by OptionChecker: %s\n", t.ident, err)
- stats.rejectOpt++
- if !*nooptcheck {
- return false
- }
- }
- // Checksum
- accept := true
- if *checksum {
- c, err := tcp.ComputeChecksum()
- if err != nil {
- Error("ChecksumCompute", "%s: Got error computing checksum: %s\n", t.ident, err)
- accept = false
- } else if c != 0x0 {
- Error("Checksum", "%s: Invalid checksum: 0x%x\n", 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 {
- fmt.Printf("bytes:%d, pkts:%d\n", sgStats.OverlapBytes, sgStats.OverlapPackets)
- panic("Invalid overlap")
- }
- 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)\n", 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\n", dnsSize, missing)
- if missing > 0 {
- Info("Missing some bytes: %d\n", missing)
- sg.KeepFrom(0)
- return
- }
- p := gopacket.NewDecodingLayerParser(layers.LayerTypeDNS, dns)
- err := p.DecodeLayers(data[2:], &decoded)
- if err != nil {
- Error("DNS-parser", "Failed to decode DNS: %v\n", err)
- } else {
- Debug("DNS: %s\n", 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:\n%s", hex.Dump(data))
- }
- if dir == reassembly.TCPDirClientToServer && !t.reversed {
- t.client.bytes <- data
- } else {
- t.server.bytes <- data
- }
- }
- }
-}
-
-func (t *tcpStream) ReassemblyComplete(ac reassembly.AssemblerContext) bool {
- Debug("%s: Connection closed\n", t.ident)
- if t.isHTTP {
- close(t.client.bytes)
- close(t.server.bytes)
- }
- // do not remove the connection to allow last ACK
- return false
-}
-
-func main() {
- defer util.Run()()
- var handle *pcap.Handle
- var err error
- if *debug {
- outputLevel = 2
- } else if *verbose {
- outputLevel = 1
- } else if *quiet {
- outputLevel = -1
- }
- errorsMap = make(map[string]uint)
- if *fname != "" {
- if handle, err = pcap.OpenOffline(*fname); err != nil {
- log.Fatal("PCAP OpenOffline error:", 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.Fatal("could not create: %v", err)
- }
- defer inactive.CleanUp()
- if err = inactive.SetSnapLen(*snaplen); err != nil {
- log.Fatal("could not set snap length: %v", err)
- } else if err = inactive.SetPromisc(*promisc); err != nil {
- log.Fatal("could not set promisc mode: %v", err)
- } else if err = inactive.SetTimeout(time.Second); err != nil {
- log.Fatal("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.Fatal("PCAP Activate error:", err)
- }
- defer handle.Close()
- }
- if len(flag.Args()) > 0 {
- bpffilter := strings.Join(flag.Args(), " ")
- Info("Using BPF filter %q\n", bpffilter)
- if err = handle.SetBPFFilter(bpffilter); err != nil {
- log.Fatal("BPF filter error:", err)
- }
- }
-
- var dec gopacket.Decoder
- var ok bool
- decoder_name := *decoder
- if decoder_name == "" {
- decoder_name = fmt.Sprintf("%s", handle.LinkType())
- }
- if dec, ok = gopacket.DecodersByLayerName[decoder_name]; !ok {
- log.Fatalln("No decoder named", decoder_name)
- }
- source := gopacket.NewPacketSource(handle, dec)
- source.Lazy = *lazy
- source.NoCopy = true
- Info("Starting to read packets\n")
- count := 0
- bytes := int64(0)
- start := time.Now()
- defragger := ip4defrag.NewIPv4Defragmenter()
-
- streamFactory := &tcpStreamFactory{doHTTP: !*nohttp}
- streamPool := reassembly.NewStreamPool(streamFactory)
- assembler := reassembly.NewAssembler(streamPool)
-
- signalChan := make(chan os.Signal, 1)
- signal.Notify(signalChan, os.Interrupt)
-
- for packet := range source.Packets() {
- count++
- Debug("PACKET #%d\n", count)
- data := packet.Data()
- bytes += int64(len(data))
- if *hexdumppkt {
- Debug("Packet content (%d/0x%x)\n%s\n", 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 {
- Debug("Fragment...\n")
- continue // packet fragment, we don't have whole packet yet.
- }
- if newip4.Length != l {
- stats.ipdefrag++
- Debug("Decoding re-assembled packet: %s\n", newip4.NextLayerType())
- pb, ok := packet.(gopacket.PacketBuilder)
- if !ok {
- panic("Not a PacketBuilder")
- }
- nextDecoder := newip4.NextLayerType()
- nextDecoder.Decode(newip4.Payload, pb)
- }
- }
-
- tcp := packet.Layer(layers.LayerTypeTCP)
- if tcp != nil {
- 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)
- assembler.AssembleWithContext(packet.NetworkLayer().NetworkFlow(), tcp, &c)
- }
- if count%*statsevery == 0 {
- ref := packet.Metadata().CaptureInfo.Timestamp
- flushed, closed := assembler.FlushWithOptions(reassembly.FlushOptions{T: ref.Add(-timeout), TC: ref.Add(-closeTimeout)})
- Debug("Forced flush: %d flushed, %d closed (%s)", flushed, closed, ref)
- }
-
- done := *maxcount > 0 && count >= *maxcount
- if count%*statsevery == 0 || done {
- fmt.Fprintf(os.Stderr, "Processed %v packets (%v bytes) in %v (errors: %v, type:%v)\n", count, bytes, time.Since(start), errors, len(errorsMap))
- }
- select {
- case <-signalChan:
- fmt.Fprintf(os.Stderr, "\nCaught SIGINT: aborting\n")
- done = true
- default:
- // NOP: continue
- }
- if done {
- break
- }
- }
-
- closed := assembler.FlushAll()
- Debug("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()
- Debug("%s\n", assembler.Dump())
- if !*nodefrag {
- fmt.Printf("IPdefrag:\t\t%d\n", stats.ipdefrag)
- }
- fmt.Printf("TCP stats:\n")
- fmt.Printf(" missed bytes:\t\t%d\n", stats.missedBytes)
- fmt.Printf(" total packets:\t\t%d\n", stats.pkt)
- fmt.Printf(" rejected FSM:\t\t%d\n", stats.rejectFsm)
- fmt.Printf(" rejected Options:\t%d\n", stats.rejectOpt)
- fmt.Printf(" reassembled bytes:\t%d\n", stats.sz)
- fmt.Printf(" total TCP bytes:\t%d\n", stats.totalsz)
- fmt.Printf(" conn rejected FSM:\t%d\n", stats.rejectConnFsm)
- fmt.Printf(" reassembled chunks:\t%d\n", stats.reassembled)
- fmt.Printf(" out-of-order packets:\t%d\n", stats.outOfOrderPackets)
- fmt.Printf(" out-of-order bytes:\t%d\n", stats.outOfOrderBytes)
- fmt.Printf(" biggest-chunk packets:\t%d\n", stats.biggestChunkPackets)
- fmt.Printf(" biggest-chunk bytes:\t%d\n", stats.biggestChunkBytes)
- fmt.Printf(" overlap packets:\t%d\n", stats.overlapPackets)
- fmt.Printf(" overlap bytes:\t\t%d\n", stats.overlapBytes)
- fmt.Printf("Errors: %d\n", errors)
- for e, _ := range errorsMap {
- fmt.Printf(" %s:\t\t%d\n", e, errorsMap[e])
- }
-}