mirror of
https://github.com/weaveworks/scope.git
synced 2026-03-27 13:57:09 +00:00
128 lines
3.8 KiB
Go
128 lines
3.8 KiB
Go
// Copyright 2012 Google, Inc. All rights reserved.
|
|
//
|
|
// Use of this source code is governed by a BSD-style license
|
|
// that can be found in the LICENSE file in the root of the source
|
|
// tree.
|
|
|
|
// 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))
|
|
}
|
|
}
|
|
}
|