aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/google/gopacket/examples
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/google/gopacket/examples')
-rw-r--r--vendor/github.com/google/gopacket/examples/arpscan/arpscan.go188
-rw-r--r--vendor/github.com/google/gopacket/examples/bidirectional/main.go192
-rw-r--r--vendor/github.com/google/gopacket/examples/bytediff/bytediff.pngbin0 -> 47462 bytes
-rw-r--r--vendor/github.com/google/gopacket/examples/bytediff/main.go96
-rw-r--r--vendor/github.com/google/gopacket/examples/httpassembly/main.go127
-rw-r--r--vendor/github.com/google/gopacket/examples/pcapdump/main.go73
-rw-r--r--vendor/github.com/google/gopacket/examples/pcaplay/main.go163
-rw-r--r--vendor/github.com/google/gopacket/examples/pfdump/main.go52
-rwxr-xr-xvendor/github.com/google/gopacket/examples/reassemblydump/compare.sh103
-rw-r--r--vendor/github.com/google/gopacket/examples/reassemblydump/main.go650
-rw-r--r--vendor/github.com/google/gopacket/examples/statsassembly/main.go211
-rw-r--r--vendor/github.com/google/gopacket/examples/synscan/main.go259
-rw-r--r--vendor/github.com/google/gopacket/examples/util/util.go40
13 files changed, 2154 insertions, 0 deletions
diff --git a/vendor/github.com/google/gopacket/examples/arpscan/arpscan.go b/vendor/github.com/google/gopacket/examples/arpscan/arpscan.go
new file mode 100644
index 0000000..1a0e33e
--- /dev/null
+++ b/vendor/github.com/google/gopacket/examples/arpscan/arpscan.go
@@ -0,0 +1,188 @@
+// 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.
+
+// arpscan implements ARP scanning of all interfaces' local networks using
+// gopacket and its subpackages. This example shows, among other things:
+// * Generating and sending packet data
+// * Reading in packet data and interpreting it
+// * Use of the 'pcap' subpackage for reading/writing
+package main
+
+import (
+ "bytes"
+ "encoding/binary"
+ "errors"
+ "log"
+ "net"
+ "sync"
+ "time"
+
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/layers"
+ "github.com/google/gopacket/pcap"
+)
+
+func main() {
+ // Get a list of all interfaces.
+ ifaces, err := net.Interfaces()
+ if err != nil {
+ panic(err)
+ }
+
+ var wg sync.WaitGroup
+ for _, iface := range ifaces {
+ wg.Add(1)
+ // Start up a scan on each interface.
+ go func(iface net.Interface) {
+ defer wg.Done()
+ if err := scan(&iface); err != nil {
+ log.Printf("interface %v: %v", iface.Name, err)
+ }
+ }(iface)
+ }
+ // Wait for all interfaces' scans to complete. They'll try to run
+ // forever, but will stop on an error, so if we get past this Wait
+ // it means all attempts to write have failed.
+ wg.Wait()
+}
+
+// scan scans an individual interface's local network for machines using ARP requests/replies.
+//
+// scan loops forever, sending packets out regularly. It returns an error if
+// it's ever unable to write a packet.
+func scan(iface *net.Interface) error {
+ // We just look for IPv4 addresses, so try to find if the interface has one.
+ var addr *net.IPNet
+ if addrs, err := iface.Addrs(); err != nil {
+ return err
+ } else {
+ for _, a := range addrs {
+ if ipnet, ok := a.(*net.IPNet); ok {
+ if ip4 := ipnet.IP.To4(); ip4 != nil {
+ addr = &net.IPNet{
+ IP: ip4,
+ Mask: ipnet.Mask[len(ipnet.Mask)-4:],
+ }
+ break
+ }
+ }
+ }
+ }
+ // Sanity-check that the interface has a good address.
+ if addr == nil {
+ return errors.New("no good IP network found")
+ } else if addr.IP[0] == 127 {
+ return errors.New("skipping localhost")
+ } else if addr.Mask[0] != 0xff || addr.Mask[1] != 0xff {
+ return errors.New("mask means network is too large")
+ }
+ log.Printf("Using network range %v for interface %v", addr, iface.Name)
+
+ // Open up a pcap handle for packet reads/writes.
+ handle, err := pcap.OpenLive(iface.Name, 65536, true, pcap.BlockForever)
+ if err != nil {
+ return err
+ }
+ defer handle.Close()
+
+ // Start up a goroutine to read in packet data.
+ stop := make(chan struct{})
+ go readARP(handle, iface, stop)
+ defer close(stop)
+ for {
+ // Write our scan packets out to the handle.
+ if err := writeARP(handle, iface, addr); err != nil {
+ log.Printf("error writing packets on %v: %v", iface.Name, err)
+ return err
+ }
+ // We don't know exactly how long it'll take for packets to be
+ // sent back to us, but 10 seconds should be more than enough
+ // time ;)
+ time.Sleep(10 * time.Second)
+ }
+}
+
+// readARP watches a handle for incoming ARP responses we might care about, and prints them.
+//
+// readARP loops until 'stop' is closed.
+func readARP(handle *pcap.Handle, iface *net.Interface, stop chan struct{}) {
+ src := gopacket.NewPacketSource(handle, layers.LayerTypeEthernet)
+ in := src.Packets()
+ for {
+ var packet gopacket.Packet
+ select {
+ case <-stop:
+ return
+ case packet = <-in:
+ arpLayer := packet.Layer(layers.LayerTypeARP)
+ if arpLayer == nil {
+ continue
+ }
+ arp := arpLayer.(*layers.ARP)
+ if arp.Operation != layers.ARPReply || bytes.Equal([]byte(iface.HardwareAddr), arp.SourceHwAddress) {
+ // This is a packet I sent.
+ continue
+ }
+ // Note: we might get some packets here that aren't responses to ones we've sent,
+ // if for example someone else sends US an ARP request. Doesn't much matter, though...
+ // all information is good information :)
+ log.Printf("IP %v is at %v", net.IP(arp.SourceProtAddress), net.HardwareAddr(arp.SourceHwAddress))
+ }
+ }
+}
+
+// writeARP writes an ARP request for each address on our local network to the
+// pcap handle.
+func writeARP(handle *pcap.Handle, iface *net.Interface, addr *net.IPNet) error {
+ // Set up all the layers' fields we can.
+ eth := layers.Ethernet{
+ SrcMAC: iface.HardwareAddr,
+ DstMAC: net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ EthernetType: layers.EthernetTypeARP,
+ }
+ arp := layers.ARP{
+ AddrType: layers.LinkTypeEthernet,
+ Protocol: layers.EthernetTypeIPv4,
+ HwAddressSize: 6,
+ ProtAddressSize: 4,
+ Operation: layers.ARPRequest,
+ SourceHwAddress: []byte(iface.HardwareAddr),
+ SourceProtAddress: []byte(addr.IP),
+ DstHwAddress: []byte{0, 0, 0, 0, 0, 0},
+ }
+ // Set up buffer and options for serialization.
+ buf := gopacket.NewSerializeBuffer()
+ opts := gopacket.SerializeOptions{
+ FixLengths: true,
+ ComputeChecksums: true,
+ }
+ // Send one packet for every address.
+ for _, ip := range ips(addr) {
+ arp.DstProtAddress = []byte(ip)
+ gopacket.SerializeLayers(buf, opts, &eth, &arp)
+ if err := handle.WritePacketData(buf.Bytes()); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// ips is a simple and not very good method for getting all IPv4 addresses from a
+// net.IPNet. It returns all IPs it can over the channel it sends back, closing
+// the channel when done.
+func ips(n *net.IPNet) (out []net.IP) {
+ num := binary.BigEndian.Uint32([]byte(n.IP))
+ mask := binary.BigEndian.Uint32([]byte(n.Mask))
+ num &= mask
+ for mask < 0xffffffff {
+ var buf [4]byte
+ binary.BigEndian.PutUint32(buf[:], num)
+ out = append(out, net.IP(buf[:]))
+ mask += 1
+ num += 1
+ }
+ return
+}
diff --git a/vendor/github.com/google/gopacket/examples/bidirectional/main.go b/vendor/github.com/google/gopacket/examples/bidirectional/main.go
new file mode 100644
index 0000000..4b0b240
--- /dev/null
+++ b/vendor/github.com/google/gopacket/examples/bidirectional/main.go
@@ -0,0 +1,192 @@
+// 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.
+
+// This binary provides an example of connecting up bidirectional streams from
+// the unidirectional streams provided by gopacket/tcpassembly.
+package main
+
+import (
+ "flag"
+ "fmt"
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/examples/util"
+ "github.com/google/gopacket/layers"
+ "github.com/google/gopacket/pcap"
+ "github.com/google/gopacket/tcpassembly"
+ "log"
+ "time"
+)
+
+var iface = flag.String("i", "eth0", "Interface to get packets from")
+var snaplen = flag.Int("s", 16<<10, "SnapLen for pcap packet capture")
+var filter = flag.String("f", "tcp", "BPF filter for pcap")
+var logAllPackets = flag.Bool("v", false, "Logs every packet in great detail")
+
+// key is used to map bidirectional streams to each other.
+type key struct {
+ net, transport gopacket.Flow
+}
+
+// String prints out the key in a human-readable fashion.
+func (k key) String() string {
+ return fmt.Sprintf("%v:%v", k.net, k.transport)
+}
+
+// timeout is the length of time to wait befor flushing connections and
+// bidirectional stream pairs.
+const timeout time.Duration = time.Minute * 5
+
+// myStream implements tcpassembly.Stream
+type myStream struct {
+ bytes int64 // total bytes seen on this stream.
+ bidi *bidi // maps to my bidirectional twin.
+ done bool // if true, we've seen the last packet we're going to for this stream.
+}
+
+// bidi stores each unidirectional side of a bidirectional stream.
+//
+// When a new stream comes in, if we don't have an opposite stream, a bidi is
+// created with 'a' set to the new stream. If we DO have an opposite stream,
+// 'b' is set to the new stream.
+type bidi struct {
+ key key // Key of the first stream, mostly for logging.
+ a, b *myStream // the two bidirectional streams.
+ lastPacketSeen time.Time // last time we saw a packet from either stream.
+}
+
+// myFactory implements tcpassmebly.StreamFactory
+type myFactory struct {
+ // bidiMap maps keys to bidirectional stream pairs.
+ bidiMap map[key]*bidi
+}
+
+// New handles creating a new tcpassembly.Stream.
+func (f *myFactory) New(netFlow, tcpFlow gopacket.Flow) tcpassembly.Stream {
+ // Create a new stream.
+ s := &myStream{}
+
+ // Find the bidi bidirectional struct for this stream, creating a new one if
+ // one doesn't already exist in the map.
+ k := key{netFlow, tcpFlow}
+ bd := f.bidiMap[k]
+ if bd == nil {
+ bd = &bidi{a: s, key: k}
+ log.Printf("[%v] created first side of bidirectional stream", bd.key)
+ // Register bidirectional with the reverse key, so the matching stream going
+ // the other direction will find it.
+ f.bidiMap[key{netFlow.Reverse(), tcpFlow.Reverse()}] = bd
+ } else {
+ log.Printf("[%v] found second side of bidirectional stream", bd.key)
+ bd.b = s
+ // Clear out the bidi we're using from the map, just in case.
+ delete(f.bidiMap, k)
+ }
+ s.bidi = bd
+ return s
+}
+
+// emptyStream is used to finish bidi that only have one stream, in
+// collectOldStreams.
+var emptyStream = &myStream{done: true}
+
+// collectOldStreams finds any streams that haven't received a packet within
+// 'timeout', and sets/finishes the 'b' stream inside them. The 'a' stream may
+// still receive packets after this.
+func (f *myFactory) collectOldStreams() {
+ cutoff := time.Now().Add(-timeout)
+ for k, bd := range f.bidiMap {
+ if bd.lastPacketSeen.Before(cutoff) {
+ log.Printf("[%v] timing out old stream", bd.key)
+ bd.b = emptyStream // stub out b with an empty stream.
+ delete(f.bidiMap, k) // remove it from our map.
+ bd.maybeFinish() // if b was the last stream we were waiting for, finish up.
+ }
+ }
+}
+
+// Reassembled handles reassembled TCP stream data.
+func (s *myStream) Reassembled(rs []tcpassembly.Reassembly) {
+ for _, r := range rs {
+ // For now, we'll simply count the bytes on each side of the TCP stream.
+ s.bytes += int64(len(r.Bytes))
+ if r.Skip > 0 {
+ s.bytes += int64(r.Skip)
+ }
+ // Mark that we've received new packet data.
+ // We could just use time.Now, but by using r.Seen we handle the case
+ // where packets are being read from a file and could be very old.
+ if s.bidi.lastPacketSeen.After(r.Seen) {
+ s.bidi.lastPacketSeen = r.Seen
+ }
+ }
+}
+
+// ReassemblyComplete marks this stream as finished.
+func (s *myStream) ReassemblyComplete() {
+ s.done = true
+ s.bidi.maybeFinish()
+}
+
+// maybeFinish will wait until both directions are complete, then print out
+// stats.
+func (bd *bidi) maybeFinish() {
+ switch {
+ case bd.a == nil:
+ log.Fatalf("[%v] a should always be non-nil, since it's set when bidis are created", bd.key)
+ case !bd.a.done:
+ log.Printf("[%v] still waiting on first stream", bd.key)
+ case bd.b == nil:
+ log.Printf("[%v] no second stream yet", bd.key)
+ case !bd.b.done:
+ log.Printf("[%v] still waiting on second stream", bd.key)
+ default:
+ log.Printf("[%v] FINISHED, bytes: %d tx, %d rx", bd.key, bd.a.bytes, bd.b.bytes)
+ }
+}
+
+func main() {
+ defer util.Run()()
+ log.Printf("starting capture on interface %q", *iface)
+ // Set up pcap packet capture
+ handle, err := pcap.OpenLive(*iface, int32(*snaplen), true, pcap.BlockForever)
+ if err != nil {
+ panic(err)
+ }
+ if err := handle.SetBPFFilter(*filter); err != nil {
+ panic(err)
+ }
+
+ // Set up assembly
+ streamFactory := &myFactory{bidiMap: make(map[key]*bidi)}
+ streamPool := tcpassembly.NewStreamPool(streamFactory)
+ assembler := tcpassembly.NewAssembler(streamPool)
+
+ log.Println("reading in packets")
+ // Read in packets, pass to assembler.
+ packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
+ packets := packetSource.Packets()
+ ticker := time.Tick(timeout / 4)
+ for {
+ select {
+ case packet := <-packets:
+ if *logAllPackets {
+ log.Println(packet)
+ }
+ if packet.NetworkLayer() == nil || packet.TransportLayer() == nil || packet.TransportLayer().LayerType() != layers.LayerTypeTCP {
+ log.Println("Unusable packet")
+ continue
+ }
+ tcp := packet.TransportLayer().(*layers.TCP)
+ assembler.AssembleWithTimestamp(packet.NetworkLayer().NetworkFlow(), tcp, packet.Metadata().Timestamp)
+
+ case <-ticker:
+ // Every minute, flush connections that haven't seen activity in the past minute.
+ log.Println("---- FLUSHING ----")
+ assembler.FlushOlderThan(time.Now().Add(-timeout))
+ streamFactory.collectOldStreams()
+ }
+ }
+}
diff --git a/vendor/github.com/google/gopacket/examples/bytediff/bytediff.png b/vendor/github.com/google/gopacket/examples/bytediff/bytediff.png
new file mode 100644
index 0000000..5aa3c8a
--- /dev/null
+++ b/vendor/github.com/google/gopacket/examples/bytediff/bytediff.png
Binary files differ
diff --git a/vendor/github.com/google/gopacket/examples/bytediff/main.go b/vendor/github.com/google/gopacket/examples/bytediff/main.go
new file mode 100644
index 0000000..2a4c11b
--- /dev/null
+++ b/vendor/github.com/google/gopacket/examples/bytediff/main.go
@@ -0,0 +1,96 @@
+// 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.
+
+// This binary shows how to display byte differences to users via the bytediff
+// library.
+package main
+
+import (
+ "fmt"
+ "github.com/google/gopacket/bytediff"
+)
+
+var sliceA = []byte{
+ 0x00, 0x00, 0x0c, 0x9f, 0xf0, 0x20, 0xbc, 0x30, 0x5b, 0xe8, 0xd3, 0x49,
+ 0x08, 0x00, 0x45, 0x00, 0x01, 0xa4, 0x39, 0xdf, 0x40, 0x00, 0x40, 0x06,
+ 0x55, 0x5a, 0xac, 0x11, 0x51, 0x49, 0xad, 0xde, 0xfe, 0xe1, 0xc5, 0xf7,
+ 0x00, 0x50, 0xc5, 0x7e, 0x0e, 0x48, 0x49, 0x07, 0x42, 0x32, 0x80, 0x18,
+ 0x00, 0x73, 0x9a, 0x8f, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0x03, 0x77,
+ 0x37, 0x9c, 0x42, 0x77, 0x5e, 0x3a, 0x47, 0x45, 0x54, 0x20, 0x2f, 0x20,
+ 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31, 0x0d, 0x0a, 0x48, 0x6f,
+ 0x73, 0x74, 0x3a, 0x20, 0x77, 0x77, 0x77, 0x2e, 0x66, 0x69, 0x73, 0x68,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63,
+ 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x6b, 0x65, 0x65, 0x70, 0x2d, 0x61,
+ 0x6c, 0x69, 0x76, 0x65, 0x0d, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x2d, 0x41,
+ 0x67, 0x65, 0x6e, 0x74, 0x3a, 0x20, 0x4d, 0x6f, 0x7a, 0x69, 0x6c, 0x6c,
+ 0x61, 0x2f, 0x35, 0x2e, 0x30, 0x20, 0x28, 0x58, 0x31, 0x31, 0x3b, 0x20,
+ 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x78, 0x38, 0x36, 0x5f, 0x36, 0x34,
+ 0x29, 0x20, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x57, 0x65, 0x62, 0x4b, 0x69,
+ 0x74, 0x2f, 0x35, 0x33, 0x35, 0x2e, 0x32, 0x20, 0x28, 0x4b, 0x48, 0x54,
+ 0x4d, 0x4c, 0x2c, 0x20, 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x47, 0x65, 0x63,
+ 0x6b, 0x6f, 0x29, 0x20, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x2f, 0x31,
+ 0x35, 0x2e, 0x30, 0x2e, 0x38, 0x37, 0x34, 0x2e, 0x31, 0x32, 0x31, 0x20,
+ 0x53, 0x61, 0x66, 0x61, 0x72, 0x69, 0x2f, 0x35, 0x2e, 0x31,
+ 0x0d, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x3a, 0x20, 0x74, 0x65,
+ 0x78, 0x74, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c,
+ 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, 0x68, 0x74, 0x6d,
+ 0x6c, 0x2b, 0x78, 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, 0x6d, 0x6c, 0x3b, 0x71, 0x3d,
+ 0x30, 0x2e, 0x39, 0x2c, 0x2a, 0x2f, 0x2a, 0x3b, 0x71, 0x3d, 0x30, 0x2e,
+ 0x38, 0x0d, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x45, 0x6e,
+ 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x3a, 0x20, 0x67, 0x7a, 0x69, 0x70,
+ 0x2c, 0x64, 0x65, 0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64, 0x63,
+ 0x68, 0x0d, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x4c, 0x61,
+ 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x3a, 0x20, 0x65, 0x6e, 0x2d, 0x55,
+ 0x53, 0x2c, 0x65, 0x6e, 0x3b, 0x71, 0x3d, 0x30, 0x2e, 0x38, 0x0d, 0x0a,
+ 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x43, 0x68, 0x61, 0x72, 0x73,
+ 0x65, 0x74, 0x3a, 0x20, 0x49, 0x53, 0x4f, 0x2d, 0x38, 0x38, 0x35, 0x39,
+ 0x2d, 0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x3b, 0x71, 0x3d, 0x30,
+ 0x2e, 0x37, 0x2c, 0x2a, 0x3b, 0x71, 0x3d, 0x30, 0x2e, 0x33, 0x0d, 0x0a,
+ 0x0d, 0x0a,
+}
+var sliceB = []byte{
+ 0x00, 0x00, 0x0c, 0x9f, 0xf0, 0x20, 0xbc, 0x30, 0x5b, 0xe8, 0xd3, 0x49,
+ 0x08, 0x00, 0x45, 0x00, 0x01, 0xa4, 0x39, 0xdf, 0x40, 0x00, 0x40, 0x06,
+ 0x55, 0x5a, 0xac, 0x11, 0x51, 0x49, 0xad, 0xde, 0xfe, 0xe1, 0xc5, 0xf7,
+ 0x00, 0x50, 0xc5, 0x7e, 0x0e, 0x48, 0x49, 0x07, 0x42, 0x32, 0x80, 0x18,
+ 0x00, 0x73, 0x9a, 0x8f, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0x03, 0x77,
+ 0x37, 0x9c, 0x42, 0x77, 0x5e, 0x3a, 0x47, 0x45, 0x54, 0x20, 0x2f, 0x20,
+ 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31, 0x0d, 0x0a, 0x48, 0x6f,
+ 0x73, 0x74, 0x3a, 0x20, 0x77, 0x77, 0x77, 0x2e, 0x66, 0x69, 0x73, 0x68,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63,
+ 0x6c, 0x69, 0x76, 0x65, 0x0d, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x2d, 0x41,
+ 0x67, 0x65, 0x6e, 0x74, 0x3a, 0x20, 0x4d, 0x6f, 0x7a, 0x69, 0x6c, 0x6c,
+ 0x61, 0x2f, 0x35, 0x2e, 0x30, 0x20, 0x28, 0x58, 0x31, 0x31, 0x3b, 0x20,
+ 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x78, 0x38, 0x36, 0x5f, 0x36, 0x34,
+ 0x29, 0x20, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x57, 0x65, 0x62, 0x4b, 0x69,
+ 0x74, 0x2f, 0x35, 0x33, 0x35, 0x2e, 0x32, 0x20, 0x28, 0x4b, 0x48, 0x54,
+ 0x4d, 0x4c, 0x2c, 0x20, 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x47, 0x65, 0x63,
+ 0x6b, 0x6f, 0x29, 0x20, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x2f, 0x31,
+ 0x35, 0x2e, 0x30, 0x2e, 0x38, 0x37, 0x34, 0x2e, 0x31, 0x32, 0x31, 0x20,
+ 0x53, 0x61, 0x66, 0x61, 0x72, 0x69, 0x2f, 0x35, 0x33, 0x35, 0x2e, 0x32,
+ 0x0d, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x3a, 0x20, 0x74, 0x65,
+ 0x78, 0x74, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c,
+ 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, 0x68, 0x74, 0x6d,
+ 0x6c, 0x2b, 0x78, 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, 0x6d, 0x6c, 0x3b, 0x71, 0x3d,
+ 0x30, 0x2e, 0x39, 0x2c, 0x2a, 0x2f, 0x2a, 0x3b, 0x71, 0x3d, 0x30, 0x2e,
+ 0x38, 0x0d, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x45, 0x6e,
+ 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x3a, 0x20, 0x67, 0x7a, 0x69, 0x70,
+ 0x2c, 0x64, 0x65, 0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64, 0x63,
+ 0x68, 0x0d, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x4c, 0x61,
+ 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x3a, 0x20, 0x65, 0x6e, 0x2d, 0x55,
+ 0x53, 0x2c, 0x65, 0x6e, 0x3b, 0x71, 0x3d, 0x30, 0x2e, 0x38, 0x0d, 0x0a,
+ 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x43, 0x68, 0x61, 0x72, 0x73,
+ 0x65, 0x74, 0x3a, 0x20, 0x49, 0x53, 0x4f, 0x2e, 0x39, 0x55, 0x35, 0x39,
+ 0x2d, 0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x3b, 0x71, 0x3d, 0x30,
+ 0x2e, 0x37, 0x2c, 0x2a, 0x3b, 0x71, 0x3d, 0x30, 0x2e, 0x33, 0x0d, 0x0a,
+ 0x0d, 0x0a,
+}
+
+func main() {
+ fmt.Println(bytediff.BashOutput.String(bytediff.Diff(sliceA, sliceB)))
+}
diff --git a/vendor/github.com/google/gopacket/examples/httpassembly/main.go b/vendor/github.com/google/gopacket/examples/httpassembly/main.go
new file mode 100644
index 0000000..02af21e
--- /dev/null
+++ b/vendor/github.com/google/gopacket/examples/httpassembly/main.go
@@ -0,0 +1,127 @@
+// 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.
+
+// This binary provides sample code for using the gopacket TCP assembler and TCP
+// stream reader. It reads packets off the wire and reconstructs HTTP requests
+// it sees, logging them.
+package main
+
+import (
+ "bufio"
+ "flag"
+ "io"
+ "log"
+ "net/http"
+ "time"
+
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/examples/util"
+ "github.com/google/gopacket/layers"
+ "github.com/google/gopacket/pcap"
+ "github.com/google/gopacket/tcpassembly"
+ "github.com/google/gopacket/tcpassembly/tcpreader"
+)
+
+var iface = flag.String("i", "eth0", "Interface to get packets from")
+var fname = flag.String("r", "", "Filename to read from, overrides -i")
+var snaplen = flag.Int("s", 1600, "SnapLen for pcap packet capture")
+var filter = flag.String("f", "tcp and dst port 80", "BPF filter for pcap")
+var logAllPackets = flag.Bool("v", false, "Logs every packet in great detail")
+
+// Build a simple HTTP request parser using tcpassembly.StreamFactory and tcpassembly.Stream interfaces
+
+// httpStreamFactory implements tcpassembly.StreamFactory
+type httpStreamFactory struct{}
+
+// httpStream will handle the actual decoding of http requests.
+type httpStream struct {
+ net, transport gopacket.Flow
+ r tcpreader.ReaderStream
+}
+
+func (h *httpStreamFactory) New(net, transport gopacket.Flow) tcpassembly.Stream {
+ hstream := &httpStream{
+ net: net,
+ transport: transport,
+ r: tcpreader.NewReaderStream(),
+ }
+ go hstream.run() // Important... we must guarantee that data from the reader stream is read.
+
+ // ReaderStream implements tcpassembly.Stream, so we can return a pointer to it.
+ return &hstream.r
+}
+
+func (h *httpStream) run() {
+ buf := bufio.NewReader(&h.r)
+ for {
+ req, err := http.ReadRequest(buf)
+ if err == io.EOF {
+ // We must read until we see an EOF... very important!
+ return
+ } else if err != nil {
+ log.Println("Error reading stream", h.net, h.transport, ":", err)
+ } else {
+ bodyBytes := tcpreader.DiscardBytesToEOF(req.Body)
+ req.Body.Close()
+ log.Println("Received request from stream", h.net, h.transport, ":", req, "with", bodyBytes, "bytes in request body")
+ }
+ }
+}
+
+func main() {
+ defer util.Run()()
+ var handle *pcap.Handle
+ var err error
+
+ // Set up pcap packet capture
+ if *fname != "" {
+ log.Printf("Reading from pcap dump %q", *fname)
+ handle, err = pcap.OpenOffline(*fname)
+ } else {
+ log.Printf("Starting capture on interface %q", *iface)
+ handle, err = pcap.OpenLive(*iface, int32(*snaplen), true, pcap.BlockForever)
+ }
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ if err := handle.SetBPFFilter(*filter); err != nil {
+ log.Fatal(err)
+ }
+
+ // Set up assembly
+ streamFactory := &httpStreamFactory{}
+ streamPool := tcpassembly.NewStreamPool(streamFactory)
+ assembler := tcpassembly.NewAssembler(streamPool)
+
+ log.Println("reading in packets")
+ // Read in packets, pass to assembler.
+ packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
+ packets := packetSource.Packets()
+ ticker := time.Tick(time.Minute)
+ for {
+ select {
+ case packet := <-packets:
+ // A nil packet indicates the end of a pcap file.
+ if packet == nil {
+ return
+ }
+ if *logAllPackets {
+ log.Println(packet)
+ }
+ if packet.NetworkLayer() == nil || packet.TransportLayer() == nil || packet.TransportLayer().LayerType() != layers.LayerTypeTCP {
+ log.Println("Unusable packet")
+ continue
+ }
+ tcp := packet.TransportLayer().(*layers.TCP)
+ assembler.AssembleWithTimestamp(packet.NetworkLayer().NetworkFlow(), tcp, packet.Metadata().Timestamp)
+
+ case <-ticker:
+ // Every minute, flush connections that haven't seen activity in the past 2 minutes.
+ assembler.FlushOlderThan(time.Now().Add(time.Minute * -2))
+ }
+ }
+}
diff --git a/vendor/github.com/google/gopacket/examples/pcapdump/main.go b/vendor/github.com/google/gopacket/examples/pcapdump/main.go
new file mode 100644
index 0000000..373dee2
--- /dev/null
+++ b/vendor/github.com/google/gopacket/examples/pcapdump/main.go
@@ -0,0 +1,73 @@
+// 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 (
+ "flag"
+ "fmt"
+ "github.com/google/gopacket/dumpcommand"
+ "github.com/google/gopacket/examples/util"
+ "github.com/google/gopacket/pcap"
+ "log"
+ "os"
+ "strings"
+ "time"
+)
+
+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")
+
+func main() {
+ defer util.Run()()
+ var handle *pcap.Handle
+ var err error
+ 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.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.Fatal("PCAP Activate error:", err)
+ }
+ defer handle.Close()
+ }
+ if len(flag.Args()) > 0 {
+ bpffilter := strings.Join(flag.Args(), " ")
+ fmt.Fprintf(os.Stderr, "Using BPF filter %q\n", bpffilter)
+ if err = handle.SetBPFFilter(bpffilter); err != nil {
+ log.Fatal("BPF filter error:", err)
+ }
+ }
+ dumpcommand.Run(handle)
+}
diff --git a/vendor/github.com/google/gopacket/examples/pcaplay/main.go b/vendor/github.com/google/gopacket/examples/pcaplay/main.go
new file mode 100644
index 0000000..d36d860
--- /dev/null
+++ b/vendor/github.com/google/gopacket/examples/pcaplay/main.go
@@ -0,0 +1,163 @@
+// 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 pcaplay binary load an offline capture (pcap file) and replay
+// it on the select interface, with an emphasis on packet timing
+package main
+
+import (
+ "flag"
+ "fmt"
+ "io"
+ "log"
+ "os"
+ "strings"
+ "time"
+
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/examples/util"
+ "github.com/google/gopacket/pcap"
+)
+
+var iface = flag.String("i", "eth0", "Interface to write packets to")
+var fname = flag.String("r", "", "Filename to read from")
+var fast = flag.Bool("f", false, "Send each packets as fast as possible")
+
+var lastTS time.Time
+var lastSend time.Time
+
+var start time.Time
+var bytesSent int
+
+func writePacketDelayed(handle *pcap.Handle, buf []byte, ci gopacket.CaptureInfo) {
+ if ci.CaptureLength != ci.Length {
+ // do not write truncated packets
+ return
+ }
+
+ intervalInCapture := ci.Timestamp.Sub(lastTS)
+ elapsedTime := time.Since(lastSend)
+
+ if (intervalInCapture > elapsedTime) && !lastSend.IsZero() {
+ time.Sleep(intervalInCapture - elapsedTime)
+ }
+
+ lastSend = time.Now()
+ writePacket(handle, buf)
+ lastTS = ci.Timestamp
+}
+
+func writePacket(handle *pcap.Handle, buf []byte) error {
+ if err := handle.WritePacketData(buf); err != nil {
+ log.Printf("Failed to send packet: %s\n", err)
+ return err
+ }
+ return nil
+}
+
+func pcapInfo(filename string) (start time.Time, end time.Time, packets int, size int) {
+ handleRead, err := pcap.OpenOffline(*fname)
+ if err != nil {
+ log.Fatal("PCAP OpenOffline error (handle to read packet):", err)
+ }
+
+ var previousTs time.Time
+ var deltaTotal time.Duration
+
+ for {
+ data, ci, err := handleRead.ReadPacketData()
+ if err != nil && err != io.EOF {
+ log.Fatal(err)
+ } else if err == io.EOF {
+ break
+ } else {
+
+ if start.IsZero() {
+ start = ci.Timestamp
+ }
+ end = ci.Timestamp
+ packets++
+ size += len(data)
+
+ if previousTs.IsZero() {
+ previousTs = ci.Timestamp
+ } else {
+ deltaTotal += ci.Timestamp.Sub(previousTs)
+ previousTs = ci.Timestamp
+ }
+ }
+ }
+ sec := int(deltaTotal.Seconds())
+ if sec == 0 {
+ sec = 1
+ }
+ fmt.Printf("Avg packet rate %d/s\n", packets/sec)
+ return start, end, packets, size
+}
+
+func main() {
+ defer util.Run()()
+
+ // Sanity checks
+ if *fname == "" {
+ log.Fatal("Need a input file")
+ }
+
+ // Open PCAP file + handle potential BPF Filter
+ handleRead, err := pcap.OpenOffline(*fname)
+ if err != nil {
+ log.Fatal("PCAP OpenOffline error (handle to read packet):", err)
+ }
+ defer handleRead.Close()
+ if len(flag.Args()) > 0 {
+ bpffilter := strings.Join(flag.Args(), " ")
+ fmt.Fprintf(os.Stderr, "Using BPF filter %q\n", bpffilter)
+ if err = handleRead.SetBPFFilter(bpffilter); err != nil {
+ log.Fatal("BPF filter error:", err)
+ }
+ }
+ // Open up a second pcap handle for packet writes.
+ handleWrite, err := pcap.OpenLive(*iface, 65536, true, pcap.BlockForever)
+ if err != nil {
+ log.Fatal("PCAP OpenLive error (handle to write packet):", err)
+ }
+ defer handleWrite.Close()
+
+ start = time.Now()
+ pkt := 0
+ tsStart, tsEnd, packets, size := pcapInfo(*fname)
+
+ // Loop over packets and write them
+ for {
+ data, ci, err := handleRead.ReadPacketData()
+ switch {
+ case err == io.EOF:
+ fmt.Printf("\nFinished in %s", time.Since(start))
+ return
+ case err != nil:
+ log.Printf("Failed to read packet %d: %s\n", pkt, err)
+ default:
+ if *fast {
+ writePacket(handleWrite, data)
+ } else {
+ writePacketDelayed(handleWrite, data, ci)
+ }
+
+ bytesSent += len(data)
+ duration := time.Since(start)
+ pkt++
+
+ if duration > time.Second {
+ rate := bytesSent / int(duration.Seconds())
+ remainingTime := tsEnd.Sub(tsStart) - duration
+ fmt.Printf("\rrate %d kB/sec - sent %d/%d kB - %d/%d packets - remaining time %s",
+ rate/1000, bytesSent/1000, size/1000,
+ pkt, packets, remainingTime)
+ }
+ }
+ }
+
+}
diff --git a/vendor/github.com/google/gopacket/examples/pfdump/main.go b/vendor/github.com/google/gopacket/examples/pfdump/main.go
new file mode 100644
index 0000000..4b3ace6
--- /dev/null
+++ b/vendor/github.com/google/gopacket/examples/pfdump/main.go
@@ -0,0 +1,52 @@
+// 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 pfdump binary implements a tcpdump-like command line tool with gopacket
+// using pfring as a backend data collection mechanism.
+package main
+
+import (
+ "flag"
+ "fmt"
+ "github.com/google/gopacket/dumpcommand"
+ "github.com/google/gopacket/examples/util"
+ "github.com/google/gopacket/pfring"
+ "log"
+ "os"
+ "strings"
+)
+
+var iface = flag.String("i", "eth0", "Interface to read packets from")
+var snaplen = flag.Int("s", 65536, "Snap length (number of bytes max to read per packet")
+var cluster = flag.Int("cluster", -1, "If >= 0, sets the pfring cluster to this value")
+var clustertype = flag.Int("clustertype", int(pfring.ClusterPerFlow), "Cluster type")
+
+func main() {
+ defer util.Run()()
+ var ring *pfring.Ring
+ var err error
+ if ring, err = pfring.NewRing(*iface, uint32(*snaplen), pfring.FlagPromisc); err != nil {
+ log.Fatalln("pfring ring creation error:", err)
+ }
+ if len(flag.Args()) > 0 {
+ bpffilter := strings.Join(flag.Args(), " ")
+ fmt.Fprintf(os.Stderr, "Using BPF filter %q\n", bpffilter)
+ if err = ring.SetBPFFilter(bpffilter); err != nil {
+ log.Fatalln("BPF filter error:", err)
+ }
+ }
+ if *cluster >= 0 {
+ if err = ring.SetCluster(*cluster, pfring.ClusterType(*clustertype)); err != nil {
+ log.Fatalln("pfring SetCluster error:", err)
+ }
+ }
+ if err = ring.SetSocketMode(pfring.ReadOnly); err != nil {
+ log.Fatalln("pfring SetSocketMode error:", err)
+ } else if err = ring.Enable(); err != nil {
+ log.Fatalln("pfring Enable error:", err)
+ }
+ dumpcommand.Run(ring)
+}
diff --git a/vendor/github.com/google/gopacket/examples/reassemblydump/compare.sh b/vendor/github.com/google/gopacket/examples/reassemblydump/compare.sh
new file mode 100755
index 0000000..671d29f
--- /dev/null
+++ b/vendor/github.com/google/gopacket/examples/reassemblydump/compare.sh
@@ -0,0 +1,103 @@
+#!/bin/bash
+
+# Limitations: if the number extracted files in too big, finding identical
+# files might fail due to '*' in cmdline
+# This would require to split sha256sum symlinks in xx/yyyyy
+
+usage()
+{
+ echo "Usage: $0 <file.pcap> <output-dir>"
+ echo "Compares tcpreassembly against tcpflow"
+ echo ""
+ echo "$@"
+ exit 1
+}
+
+debug() {
+ return # comment me for debug
+ echo "$@"
+}
+
+die()
+{
+ (
+ echo "$@"
+ echo
+ ) >&2
+ exit 1
+}
+
+rename()
+{
+ local path="$1"
+ local filter="$2"
+ find "$path" -type f -name "$filter" -print0 |
+ while IFS= read -r -d $'\0' f; do
+ local sha256="$(sha256sum "$f" | cut -d ' ' -f 1)"
+ local target="$(dirname $f)/../sha256/$sha256"
+ debug "$target → $f"
+ mkdir -p "$(dirname "$target")" || return 1
+ if [ ! -f "$target" ]; then
+ ln -sr "$f" "$target" || return 1
+ fi
+ done
+ return $?
+}
+
+main()
+{
+ local src="$1"
+ local out="$2"
+
+ # TODO: make options
+ local extra=""
+ extra="$extra -debug"
+ extra="$extra -cpuprofile "$out/gopacket/cpu.prof""
+ extra="$extra -memprofile "$out/gopacket/mem.prof""
+
+ [ ! -f "$src" ] && usage "Missing pcap"
+ [ ! -d "$out" ] && ( mkdir "$out" || die "Failed to create $out" )
+
+ mkdir -p "$out/gopacket/all" || die "Failed to create $out/gopacket/all"
+ mkdir -p "$out/tcpflow/all" || die "Faield to create $out/tcpflow/all"
+
+ echo " * Running go reassembly"
+ time ./reassemblydump -r "$src" $debug -output "$out/gopacket/all" $extra -writeincomplete -ignorefsmerr -nooptcheck -allowmissinginit port 80 &> "$out/gopacket.txt" || die "Failed to run reassmbly. Check $out/gopacket.txt"
+ echo " * Running tcpflow"
+ time tcpflow -e http -r "$src" -o "$out/tcpflow/all" port 80 &> "$out/tcpflow.txt" || die "Failed to run tcpflow. Check $out/tcpflow.txt"
+
+ echo " * Creating sha256sum symlinks for gopacket"
+ rename "$out/gopacket/all" '*' || die "Failed to rename in $out/gopacket"
+ echo " * Creating sha256sum symlinks for tcpflow"
+ rename "$out/tcpflow/all" '*HTTPBODY*' || die "Failed to rename in $out/tcpflow"
+
+ # Remove identical files
+ echo " * Finding identical files"
+ local nb=0
+ mkdir -p "$out/gopacket/sha256-equal"
+ mkdir -p "$out/tcpflow/sha256-equal"
+ for f in "$out/gopacket/sha256/"*; do
+ local f="$(basename "$f")"
+ [ -f "$out/tcpflow/sha256/$f" ] && {
+ debug " $f"
+ mv "$out/gopacket/sha256/$f" "$out/gopacket/sha256-equal"
+ mv "$out/tcpflow/sha256/$f" "$out/tcpflow/sha256-equal"
+ nb=$((nb+1))
+ }
+ done
+ echo " → found $nb files"
+
+ echo " * Diffing {gopacket,tcpflow}/sha256"
+ local rc=0
+ for p in "gopacket" "tcpflow"; do
+ local nb=$(ls -1 "$out/$p/sha256/" | wc -l)
+ if [ $nb -ne 0 ]; then
+ rc=$((rc+1))
+ echo " → $nb files in $out/$p/sha256"
+ fi
+ done
+ return $rc
+}
+
+main "$@"
+exit $?
diff --git a/vendor/github.com/google/gopacket/examples/reassemblydump/main.go b/vendor/github.com/google/gopacket/examples/reassemblydump/main.go
new file mode 100644
index 0000000..9fc3791
--- /dev/null
+++ b/vendor/github.com/google/gopacket/examples/reassemblydump/main.go
@@ -0,0 +1,650 @@
+// 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])
+ }
+}
diff --git a/vendor/github.com/google/gopacket/examples/statsassembly/main.go b/vendor/github.com/google/gopacket/examples/statsassembly/main.go
new file mode 100644
index 0000000..36da011
--- /dev/null
+++ b/vendor/github.com/google/gopacket/examples/statsassembly/main.go
@@ -0,0 +1,211 @@
+// 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.
+
+// This binary provides sample code for using the gopacket TCP assembler raw,
+// without the help of the tcpreader library. It watches TCP streams and
+// reports statistics on completed streams.
+//
+// It also uses gopacket.DecodingLayerParser instead of the normal
+// gopacket.PacketSource, to highlight the methods, pros, and cons of this
+// approach.
+package main
+
+import (
+ "flag"
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/examples/util"
+ "github.com/google/gopacket/layers"
+ "github.com/google/gopacket/pcap"
+ "github.com/google/gopacket/tcpassembly"
+ "log"
+ "time"
+)
+
+var iface = flag.String("i", "eth0", "Interface to get packets from")
+var snaplen = flag.Int("s", 65536, "SnapLen for pcap packet capture")
+var filter = flag.String("f", "tcp", "BPF filter for pcap")
+var logAllPackets = flag.Bool("v", false, "Log whenever we see a packet")
+var bufferedPerConnection = flag.Int("connection_max_buffer", 0, `
+Max packets to buffer for a single connection before skipping over a gap in data
+and continuing to stream the connection after the buffer. If zero or less, this
+is infinite.`)
+var bufferedTotal = flag.Int("total_max_buffer", 0, `
+Max packets to buffer total before skipping over gaps in connections and
+continuing to stream connection data. If zero or less, this is infinite`)
+var flushAfter = flag.String("flush_after", "2m", `
+Connections which have buffered packets (they've gotten packets out of order and
+are waiting for old packets to fill the gaps) are flushed after they're this old
+(their oldest gap is skipped). Any string parsed by time.ParseDuration is
+acceptable here`)
+var packetCount = flag.Int("c", -1, `
+Quit after processing this many packets, flushing all currently buffered
+connections. If negative, this is infinite`)
+
+// simpleStreamFactory implements tcpassembly.StreamFactory
+type statsStreamFactory struct{}
+
+// statsStream will handle the actual decoding of stats requests.
+type statsStream struct {
+ net, transport gopacket.Flow
+ bytes, packets, outOfOrder, skipped int64
+ start, end time.Time
+ sawStart, sawEnd bool
+}
+
+// New creates a new stream. It's called whenever the assembler sees a stream
+// it isn't currently following.
+func (factory *statsStreamFactory) New(net, transport gopacket.Flow) tcpassembly.Stream {
+ log.Printf("new stream %v:%v started", net, transport)
+ s := &statsStream{
+ net: net,
+ transport: transport,
+ start: time.Now(),
+ }
+ s.end = s.start
+ // ReaderStream implements tcpassembly.Stream, so we can return a pointer to it.
+ return s
+}
+
+// Reassembled is called whenever new packet data is available for reading.
+// Reassembly objects contain stream data IN ORDER.
+func (s *statsStream) Reassembled(reassemblies []tcpassembly.Reassembly) {
+ for _, reassembly := range reassemblies {
+ if reassembly.Seen.Before(s.end) {
+ s.outOfOrder++
+ } else {
+ s.end = reassembly.Seen
+ }
+ s.bytes += int64(len(reassembly.Bytes))
+ s.packets += 1
+ if reassembly.Skip > 0 {
+ s.skipped += int64(reassembly.Skip)
+ }
+ s.sawStart = s.sawStart || reassembly.Start
+ s.sawEnd = s.sawEnd || reassembly.End
+ }
+}
+
+// ReassemblyComplete is called when the TCP assembler believes a stream has
+// finished.
+func (s *statsStream) ReassemblyComplete() {
+ diffSecs := float64(s.end.Sub(s.start)) / float64(time.Second)
+ log.Printf("Reassembly of stream %v:%v complete - start:%v end:%v bytes:%v packets:%v ooo:%v bps:%v pps:%v skipped:%v",
+ s.net, s.transport, s.start, s.end, s.bytes, s.packets, s.outOfOrder,
+ float64(s.bytes)/diffSecs, float64(s.packets)/diffSecs, s.skipped)
+}
+
+func main() {
+ defer util.Run()()
+
+ flushDuration, err := time.ParseDuration(*flushAfter)
+ if err != nil {
+ log.Fatal("invalid flush duration: ", *flushAfter)
+ }
+
+ log.Printf("starting capture on interface %q", *iface)
+ // Set up pcap packet capture
+ handle, err := pcap.OpenLive(*iface, int32(*snaplen), true, flushDuration/2)
+ if err != nil {
+ log.Fatal("error opening pcap handle: ", err)
+ }
+ if err := handle.SetBPFFilter(*filter); err != nil {
+ log.Fatal("error setting BPF filter: ", err)
+ }
+
+ // Set up assembly
+ streamFactory := &statsStreamFactory{}
+ streamPool := tcpassembly.NewStreamPool(streamFactory)
+ assembler := tcpassembly.NewAssembler(streamPool)
+ assembler.MaxBufferedPagesPerConnection = *bufferedPerConnection
+ assembler.MaxBufferedPagesTotal = *bufferedTotal
+
+ log.Println("reading in packets")
+
+ // We use a DecodingLayerParser here instead of a simpler PacketSource.
+ // This approach should be measurably faster, but is also more rigid.
+ // PacketSource will handle any known type of packet safely and easily,
+ // but DecodingLayerParser will only handle those packet types we
+ // specifically pass in. This trade-off can be quite useful, though, in
+ // high-throughput situations.
+ var eth layers.Ethernet
+ var dot1q layers.Dot1Q
+ var ip4 layers.IPv4
+ var ip6 layers.IPv6
+ var ip6extensions layers.IPv6ExtensionSkipper
+ var tcp layers.TCP
+ var payload gopacket.Payload
+ parser := gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet,
+ &eth, &dot1q, &ip4, &ip6, &ip6extensions, &tcp, &payload)
+ decoded := make([]gopacket.LayerType, 0, 4)
+
+ nextFlush := time.Now().Add(flushDuration / 2)
+
+ var byteCount int64
+ start := time.Now()
+
+loop:
+ for ; *packetCount != 0; *packetCount-- {
+ // Check to see if we should flush the streams we have
+ // that haven't seen any new data in a while. Note we set a
+ // timeout on our PCAP handle, so this should happen even if we
+ // never see packet data.
+ if time.Now().After(nextFlush) {
+ stats, _ := handle.Stats()
+ log.Printf("flushing all streams that haven't seen packets in the last 2 minutes, pcap stats: %+v", stats)
+ assembler.FlushOlderThan(time.Now().Add(flushDuration))
+ nextFlush = time.Now().Add(flushDuration / 2)
+ }
+
+ // To speed things up, we're also using the ZeroCopy method for
+ // reading packet data. This method is faster than the normal
+ // ReadPacketData, but the returned bytes in 'data' are
+ // invalidated by any subsequent ZeroCopyReadPacketData call.
+ // Note that tcpassembly is entirely compatible with this packet
+ // reading method. This is another trade-off which might be
+ // appropriate for high-throughput sniffing: it avoids a packet
+ // copy, but its cost is much more careful handling of the
+ // resulting byte slice.
+ data, ci, err := handle.ZeroCopyReadPacketData()
+
+ if err != nil {
+ log.Printf("error getting packet: %v", err)
+ continue
+ }
+ err = parser.DecodeLayers(data, &decoded)
+ if err != nil {
+ log.Printf("error decoding packet: %v", err)
+ continue
+ }
+ if *logAllPackets {
+ log.Printf("decoded the following layers: %v", decoded)
+ }
+ byteCount += int64(len(data))
+ // Find either the IPv4 or IPv6 address to use as our network
+ // layer.
+ foundNetLayer := false
+ var netFlow gopacket.Flow
+ for _, typ := range decoded {
+ switch typ {
+ case layers.LayerTypeIPv4:
+ netFlow = ip4.NetworkFlow()
+ foundNetLayer = true
+ case layers.LayerTypeIPv6:
+ netFlow = ip6.NetworkFlow()
+ foundNetLayer = true
+ case layers.LayerTypeTCP:
+ if foundNetLayer {
+ assembler.AssembleWithTimestamp(netFlow, &tcp, ci.Timestamp)
+ } else {
+ log.Println("could not find IPv4 or IPv6 layer, inoring")
+ }
+ continue loop
+ }
+ }
+ log.Println("could not find TCP layer")
+ }
+ assembler.FlushAll()
+ log.Printf("processed %d bytes in %v", byteCount, time.Since(start))
+}
diff --git a/vendor/github.com/google/gopacket/examples/synscan/main.go b/vendor/github.com/google/gopacket/examples/synscan/main.go
new file mode 100644
index 0000000..7a2345f
--- /dev/null
+++ b/vendor/github.com/google/gopacket/examples/synscan/main.go
@@ -0,0 +1,259 @@
+// 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.
+
+// synscan implements a TCP syn scanner on top of pcap.
+// It's more complicated than arpscan, since it has to handle sending packets
+// outside the local network, requiring some routing and ARP work.
+//
+// Since this is just an example program, it aims for simplicity over
+// performance. It doesn't handle sending packets very quickly, it scans IPs
+// serially instead of in parallel, and uses gopacket.Packet instead of
+// gopacket.DecodingLayerParser for packet processing. We also make use of very
+// simple timeout logic with time.Since.
+//
+// Making it blazingly fast is left as an exercise to the reader.
+package main
+
+import (
+ "errors"
+ "flag"
+ "log"
+ "net"
+ "time"
+
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/examples/util"
+ "github.com/google/gopacket/layers"
+ "github.com/google/gopacket/pcap"
+ "github.com/google/gopacket/routing"
+)
+
+// scanner handles scanning a single IP address.
+type scanner struct {
+ // iface is the interface to send packets on.
+ iface *net.Interface
+ // destination, gateway (if applicable), and source IP addresses to use.
+ dst, gw, src net.IP
+
+ handle *pcap.Handle
+
+ // opts and buf allow us to easily serialize packets in the send()
+ // method.
+ opts gopacket.SerializeOptions
+ buf gopacket.SerializeBuffer
+}
+
+// newScanner creates a new scanner for a given destination IP address, using
+// router to determine how to route packets to that IP.
+func newScanner(ip net.IP, router routing.Router) (*scanner, error) {
+ s := &scanner{
+ dst: ip,
+ opts: gopacket.SerializeOptions{
+ FixLengths: true,
+ ComputeChecksums: true,
+ },
+ buf: gopacket.NewSerializeBuffer(),
+ }
+ // Figure out the route to the IP.
+ iface, gw, src, err := router.Route(ip)
+ if err != nil {
+ return nil, err
+ }
+ log.Printf("scanning ip %v with interface %v, gateway %v, src %v", ip, iface.Name, gw, src)
+ s.gw, s.src, s.iface = gw, src, iface
+
+ // Open the handle for reading/writing.
+ // Note we could very easily add some BPF filtering here to greatly
+ // decrease the number of packets we have to look at when getting back
+ // scan results.
+ handle, err := pcap.OpenLive(iface.Name, 65536, true, pcap.BlockForever)
+ if err != nil {
+ return nil, err
+ }
+ s.handle = handle
+ return s, nil
+}
+
+// close cleans up the handle.
+func (s *scanner) close() {
+ s.handle.Close()
+}
+
+// getHwAddr is a hacky but effective way to get the destination hardware
+// address for our packets. It does an ARP request for our gateway (if there is
+// one) or destination IP (if no gateway is necessary), then waits for an ARP
+// reply. This is pretty slow right now, since it blocks on the ARP
+// request/reply.
+func (s *scanner) getHwAddr() (net.HardwareAddr, error) {
+ start := time.Now()
+ arpDst := s.dst
+ if s.gw != nil {
+ arpDst = s.gw
+ }
+ // Prepare the layers to send for an ARP request.
+ eth := layers.Ethernet{
+ SrcMAC: s.iface.HardwareAddr,
+ DstMAC: net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ EthernetType: layers.EthernetTypeARP,
+ }
+ arp := layers.ARP{
+ AddrType: layers.LinkTypeEthernet,
+ Protocol: layers.EthernetTypeIPv4,
+ HwAddressSize: 6,
+ ProtAddressSize: 4,
+ Operation: layers.ARPRequest,
+ SourceHwAddress: []byte(s.iface.HardwareAddr),
+ SourceProtAddress: []byte(s.src),
+ DstHwAddress: []byte{0, 0, 0, 0, 0, 0},
+ DstProtAddress: []byte(arpDst),
+ }
+ // Send a single ARP request packet (we never retry a send, since this
+ // is just an example ;)
+ if err := s.send(&eth, &arp); err != nil {
+ return nil, err
+ }
+ // Wait 3 seconds for an ARP reply.
+ for {
+ if time.Since(start) > time.Second*3 {
+ return nil, errors.New("timeout getting ARP reply")
+ }
+ data, _, err := s.handle.ReadPacketData()
+ if err == pcap.NextErrorTimeoutExpired {
+ continue
+ } else if err != nil {
+ return nil, err
+ }
+ packet := gopacket.NewPacket(data, layers.LayerTypeEthernet, gopacket.NoCopy)
+ if arpLayer := packet.Layer(layers.LayerTypeARP); arpLayer != nil {
+ arp := arpLayer.(*layers.ARP)
+ if net.IP(arp.SourceProtAddress).Equal(net.IP(arpDst)) {
+ return net.HardwareAddr(arp.SourceHwAddress), nil
+ }
+ }
+ }
+}
+
+// scan scans the dst IP address of this scanner.
+func (s *scanner) scan() error {
+ // First off, get the MAC address we should be sending packets to.
+ hwaddr, err := s.getHwAddr()
+ if err != nil {
+ return err
+ }
+ // Construct all the network layers we need.
+ eth := layers.Ethernet{
+ SrcMAC: s.iface.HardwareAddr,
+ DstMAC: hwaddr,
+ EthernetType: layers.EthernetTypeIPv4,
+ }
+ ip4 := layers.IPv4{
+ SrcIP: s.src,
+ DstIP: s.dst,
+ Version: 4,
+ TTL: 64,
+ Protocol: layers.IPProtocolTCP,
+ }
+ tcp := layers.TCP{
+ SrcPort: 54321,
+ DstPort: 0, // will be incremented during the scan
+ SYN: true,
+ }
+ tcp.SetNetworkLayerForChecksum(&ip4)
+
+ // Create the flow we expect returning packets to have, so we can check
+ // against it and discard useless packets.
+ ipFlow := gopacket.NewFlow(layers.EndpointIPv4, s.dst, s.src)
+ start := time.Now()
+ for {
+ // Send one packet per loop iteration until we've sent packets
+ // to all of ports [1, 65535].
+ if tcp.DstPort < 65535 {
+ start = time.Now()
+ tcp.DstPort++
+ if err := s.send(&eth, &ip4, &tcp); err != nil {
+ log.Printf("error sending to port %v: %v", tcp.DstPort, err)
+ }
+ }
+ // Time out 5 seconds after the last packet we sent.
+ if time.Since(start) > time.Second*5 {
+ log.Printf("timed out for %v, assuming we've seen all we can", s.dst)
+ return nil
+ }
+
+ // Read in the next packet.
+ data, _, err := s.handle.ReadPacketData()
+ if err == pcap.NextErrorTimeoutExpired {
+ continue
+ } else if err != nil {
+ log.Printf("error reading packet: %v", err)
+ continue
+ }
+
+ // Parse the packet. We'd use DecodingLayerParser here if we
+ // wanted to be really fast.
+ packet := gopacket.NewPacket(data, layers.LayerTypeEthernet, gopacket.NoCopy)
+
+ // Find the packets we care about, and print out logging
+ // information about them. All others are ignored.
+ if net := packet.NetworkLayer(); net == nil {
+ // log.Printf("packet has no network layer")
+ } else if net.NetworkFlow() != ipFlow {
+ // log.Printf("packet does not match our ip src/dst")
+ } else if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer == nil {
+ // log.Printf("packet has not tcp layer")
+ } else if tcp, ok := tcpLayer.(*layers.TCP); !ok {
+ // We panic here because this is guaranteed to never
+ // happen.
+ panic("tcp layer is not tcp layer :-/")
+ } else if tcp.DstPort != 54321 {
+ // log.Printf("dst port %v does not match", tcp.DstPort)
+ } else if tcp.RST {
+ log.Printf(" port %v closed", tcp.SrcPort)
+ } else if tcp.SYN && tcp.ACK {
+ log.Printf(" port %v open", tcp.SrcPort)
+ } else {
+ // log.Printf("ignoring useless packet")
+ }
+ }
+}
+
+// send sends the given layers as a single packet on the network.
+func (s *scanner) send(l ...gopacket.SerializableLayer) error {
+ if err := gopacket.SerializeLayers(s.buf, s.opts, l...); err != nil {
+ return err
+ }
+ return s.handle.WritePacketData(s.buf.Bytes())
+}
+
+func main() {
+ defer util.Run()()
+ router, err := routing.New()
+ if err != nil {
+ log.Fatal("routing error:", err)
+ }
+ for _, arg := range flag.Args() {
+ var ip net.IP
+ if ip = net.ParseIP(arg); ip == nil {
+ log.Printf("non-ip target: %q", arg)
+ continue
+ } else if ip = ip.To4(); ip == nil {
+ log.Printf("non-ipv4 target: %q", arg)
+ continue
+ }
+ // Note: newScanner creates and closes a pcap Handle once for
+ // every scan target. We could do much better, were this not an
+ // example ;)
+ s, err := newScanner(ip, router)
+ if err != nil {
+ log.Printf("unable to create scanner for %v: %v", ip, err)
+ continue
+ }
+ if err := s.scan(); err != nil {
+ log.Printf("unable to scan %v: %v", ip, err)
+ }
+ s.close()
+ }
+}
diff --git a/vendor/github.com/google/gopacket/examples/util/util.go b/vendor/github.com/google/gopacket/examples/util/util.go
new file mode 100644
index 0000000..0f698fb
--- /dev/null
+++ b/vendor/github.com/google/gopacket/examples/util/util.go
@@ -0,0 +1,40 @@
+// 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.
+
+// Package util provides shared utilities for all gopacket examples.
+package util
+
+import (
+ "flag"
+ "log"
+ "os"
+ "runtime/pprof"
+)
+
+var cpuprofile = flag.String("cpuprofile", "", "Where to write CPU profile")
+
+// Run starts up stuff at the beginning of a main function, and returns a
+// function to defer until the function completes. It should be used like this:
+//
+// func main() {
+// defer util.Run()()
+// ... stuff ...
+// }
+func Run() func() {
+ flag.Parse()
+ if *cpuprofile != "" {
+ f, err := os.Create(*cpuprofile)
+ if err != nil {
+ log.Fatalf("could not open cpu profile file %q", *cpuprofile)
+ }
+ pprof.StartCPUProfile(f)
+ return func() {
+ pprof.StopCPUProfile()
+ f.Close()
+ }
+ }
+ return func() {}
+}