1// Copyright 2012 Google, Inc. All rights reserved. 2// 3// Use of this source code is governed by a BSD-style license 4// that can be found in the LICENSE file in the root of the source 5// tree. 6 7// This binary provides sample code for using the gopacket TCP assembler and TCP 8// stream reader. It reads packets off the wire and reconstructs HTTP requests 9// it sees, logging them. 10package main 11 12import ( 13 "bufio" 14 "flag" 15 "io" 16 "log" 17 "net/http" 18 "time" 19 20 "github.com/google/gopacket" 21 "github.com/google/gopacket/examples/util" 22 "github.com/google/gopacket/layers" 23 "github.com/google/gopacket/pcap" 24 "github.com/google/gopacket/tcpassembly" 25 "github.com/google/gopacket/tcpassembly/tcpreader" 26) 27 28var iface = flag.String("i", "eth0", "Interface to get packets from") 29var fname = flag.String("r", "", "Filename to read from, overrides -i") 30var snaplen = flag.Int("s", 1600, "SnapLen for pcap packet capture") 31var filter = flag.String("f", "tcp and dst port 80", "BPF filter for pcap") 32var logAllPackets = flag.Bool("v", false, "Logs every packet in great detail") 33 34// Build a simple HTTP request parser using tcpassembly.StreamFactory and tcpassembly.Stream interfaces 35 36// httpStreamFactory implements tcpassembly.StreamFactory 37type httpStreamFactory struct{} 38 39// httpStream will handle the actual decoding of http requests. 40type httpStream struct { 41 net, transport gopacket.Flow 42 r tcpreader.ReaderStream 43} 44 45func (h *httpStreamFactory) New(net, transport gopacket.Flow) tcpassembly.Stream { 46 hstream := &httpStream{ 47 net: net, 48 transport: transport, 49 r: tcpreader.NewReaderStream(), 50 } 51 go hstream.run() // Important... we must guarantee that data from the reader stream is read. 52 53 // ReaderStream implements tcpassembly.Stream, so we can return a pointer to it. 54 return &hstream.r 55} 56 57func (h *httpStream) run() { 58 buf := bufio.NewReader(&h.r) 59 for { 60 req, err := http.ReadRequest(buf) 61 if err == io.EOF { 62 // We must read until we see an EOF... very important! 63 return 64 } else if err != nil { 65 log.Println("Error reading stream", h.net, h.transport, ":", err) 66 } else { 67 bodyBytes := tcpreader.DiscardBytesToEOF(req.Body) 68 req.Body.Close() 69 log.Println("Received request from stream", h.net, h.transport, ":", req, "with", bodyBytes, "bytes in request body") 70 } 71 } 72} 73 74func main() { 75 defer util.Run()() 76 var handle *pcap.Handle 77 var err error 78 79 // Set up pcap packet capture 80 if *fname != "" { 81 log.Printf("Reading from pcap dump %q", *fname) 82 handle, err = pcap.OpenOffline(*fname) 83 } else { 84 log.Printf("Starting capture on interface %q", *iface) 85 handle, err = pcap.OpenLive(*iface, int32(*snaplen), true, pcap.BlockForever) 86 } 87 if err != nil { 88 log.Fatal(err) 89 } 90 91 if err := handle.SetBPFFilter(*filter); err != nil { 92 log.Fatal(err) 93 } 94 95 // Set up assembly 96 streamFactory := &httpStreamFactory{} 97 streamPool := tcpassembly.NewStreamPool(streamFactory) 98 assembler := tcpassembly.NewAssembler(streamPool) 99 100 log.Println("reading in packets") 101 // Read in packets, pass to assembler. 102 packetSource := gopacket.NewPacketSource(handle, handle.LinkType()) 103 packets := packetSource.Packets() 104 ticker := time.Tick(time.Minute) 105 for { 106 select { 107 case packet := <-packets: 108 // A nil packet indicates the end of a pcap file. 109 if packet == nil { 110 return 111 } 112 if *logAllPackets { 113 log.Println(packet) 114 } 115 if packet.NetworkLayer() == nil || packet.TransportLayer() == nil || packet.TransportLayer().LayerType() != layers.LayerTypeTCP { 116 log.Println("Unusable packet") 117 continue 118 } 119 tcp := packet.TransportLayer().(*layers.TCP) 120 assembler.AssembleWithTimestamp(packet.NetworkLayer().NetworkFlow(), tcp, packet.Metadata().Timestamp) 121 122 case <-ticker: 123 // Every minute, flush connections that haven't seen activity in the past 2 minutes. 124 assembler.FlushOlderThan(time.Now().Add(time.Minute * -2)) 125 } 126 } 127} 128