github.com/gopacket/gopacket@v1.1.0/pcap/gopacket_benchmark/benchmark.go (about) 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 benchmark reads in file <tempdir>/gopacket_benchmark.pcap and measures 8 // the time it takes to decode all packets from that file. If the file doesn't 9 // exist, it's pulled down from a publicly available location. However, you can 10 // feel free to substitute your own file at that location, in which case the 11 // benchmark will run on your own data. 12 // 13 // It's also useful for figuring out which packets may be causing errors. Pass 14 // in the --printErrors flag, and it'll print out error layers for each packet 15 // that has them. This includes any packets that it's just unable to decode, 16 // which is a great way to find new protocols to decode, and get test packets to 17 // write tests for them. 18 package main 19 20 import ( 21 "compress/gzip" 22 "encoding/hex" 23 "flag" 24 "fmt" 25 "io" 26 "io/ioutil" 27 "net/http" 28 "os" 29 "runtime" 30 "runtime/pprof" 31 "time" 32 33 "github.com/gopacket/gopacket" 34 "github.com/gopacket/gopacket/layers" 35 "github.com/gopacket/gopacket/pcap" 36 "github.com/gopacket/gopacket/tcpassembly" 37 ) 38 39 var decodeLazy *bool = flag.Bool("lazy", false, "If true, use lazy decoding") 40 var decodeNoCopy *bool = flag.Bool("nocopy", true, "If true, avoid an extra copy when decoding packets") 41 var printErrors *bool = flag.Bool("printErrors", false, "If true, check for and print error layers.") 42 var printLayers *bool = flag.Bool("printLayers", false, "If true, print out the layers of each packet") 43 var repeat *int = flag.Int("repeat", 5, "Read over the file N times") 44 var cpuProfile *string = flag.String("cpuprofile", "", "If set, write CPU profile to filename") 45 var url *string = flag.String("url", "http://www.ll.mit.edu/mission/communications/cyber/CSTcorpora/ideval/data/1999/training/week1/tuesday/inside.tcpdump.gz", "URL to gzip'd pcap file") 46 47 type BufferPacketSource struct { 48 index int 49 data [][]byte 50 ci []gopacket.CaptureInfo 51 } 52 53 func NewBufferPacketSource(p gopacket.PacketDataSource) *BufferPacketSource { 54 start := time.Now() 55 b := &BufferPacketSource{} 56 for { 57 data, ci, err := p.ReadPacketData() 58 if err == io.EOF { 59 break 60 } 61 b.data = append(b.data, data) 62 b.ci = append(b.ci, ci) 63 } 64 duration := time.Since(start) 65 fmt.Printf("Reading packet data into memory: %d packets in %v, %v per packet\n", len(b.data), duration, duration/time.Duration(len(b.data))) 66 return b 67 } 68 69 func (b *BufferPacketSource) ReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error) { 70 if b.index >= len(b.data) { 71 err = io.EOF 72 return 73 } 74 data = b.data[b.index] 75 ci = b.ci[b.index] 76 b.index++ 77 return 78 } 79 80 func (b *BufferPacketSource) Reset() { 81 runtime.GC() 82 b.index = 0 83 } 84 85 func main() { 86 flag.Parse() 87 filename := os.TempDir() + string(os.PathSeparator) + "gopacket_benchmark.pcap" 88 if _, err := os.Stat(filename); err != nil { 89 // This URL points to a publicly available packet data set from a DARPA 90 // intrusion detection evaluation. See 91 // http://www.ll.mit.edu/mission/communications/cyber/CSTcorpora/ideval/data/1999/training/week1/index.html 92 // for more details. 93 fmt.Println("Local pcap file", filename, "doesn't exist, reading from", *url) 94 if resp, err := http.Get(*url); err != nil { 95 panic(err) 96 } else if out, err := os.Create(filename); err != nil { 97 panic(err) 98 } else if gz, err := gzip.NewReader(resp.Body); err != nil { 99 panic(err) 100 } else if n, err := io.Copy(out, gz); err != nil { 101 panic(err) 102 } else if err := gz.Close(); err != nil { 103 panic(err) 104 } else if err := out.Close(); err != nil { 105 panic(err) 106 } else { 107 fmt.Println("Successfully read", n, "bytes from url, unzipped to local storage") 108 } 109 } 110 fmt.Println("Reading file once through to hopefully cache most of it") 111 if f, err := os.Open(filename); err != nil { 112 panic(err) 113 } else if n, err := io.Copy(ioutil.Discard, f); err != nil { 114 panic(err) 115 } else if err := f.Close(); err != nil { 116 panic(err) 117 } else { 118 fmt.Println("Read in file", filename, ", total of", n, "bytes") 119 } 120 if *cpuProfile != "" { 121 if cpu, err := os.Create(*cpuProfile); err != nil { 122 panic(err) 123 } else if err := pprof.StartCPUProfile(cpu); err != nil { 124 panic(err) 125 } else { 126 defer func() { 127 pprof.StopCPUProfile() 128 cpu.Close() 129 }() 130 } 131 } 132 var packetDataSource *BufferPacketSource 133 var packetSource *gopacket.PacketSource 134 fmt.Printf("Opening file %q for read\n", filename) 135 if h, err := pcap.OpenOffline(filename); err != nil { 136 panic(err) 137 } else { 138 fmt.Println("Reading all packets into memory with BufferPacketSource.") 139 start := time.Now() 140 packetDataSource = NewBufferPacketSource(h) 141 duration := time.Since(start) 142 fmt.Printf("Time to read packet data into memory from file: %v\n", duration) 143 packetSource = gopacket.NewPacketSource(packetDataSource, h.LinkType()) 144 packetSource.DecodeOptions.Lazy = *decodeLazy 145 packetSource.DecodeOptions.NoCopy = *decodeNoCopy 146 } 147 fmt.Println() 148 for i := 0; i < *repeat; i++ { 149 packetDataSource.Reset() 150 fmt.Printf("Benchmarking decode %d/%d\n", i+1, *repeat) 151 benchmarkPacketDecode(packetSource) 152 } 153 fmt.Println() 154 for i := 0; i < *repeat; i++ { 155 packetDataSource.Reset() 156 fmt.Printf("Benchmarking decoding layer parser %d/%d\n", i+1, *repeat) 157 benchmarkLayerDecode(packetDataSource, false) 158 } 159 fmt.Println() 160 for i := 0; i < *repeat; i++ { 161 packetDataSource.Reset() 162 fmt.Printf("Benchmarking decoding layer parser with assembly %d/%d\n", i+1, *repeat) 163 benchmarkLayerDecode(packetDataSource, true) 164 } 165 } 166 167 func benchmarkPacketDecode(packetSource *gopacket.PacketSource) { 168 count, errors := 0, 0 169 start := time.Now() 170 for packet, err := packetSource.NextPacket(); err != io.EOF; packet, err = packetSource.NextPacket() { 171 if err != nil { 172 fmt.Println("Error reading in packet:", err) 173 continue 174 } 175 count++ 176 var hasError bool 177 if *printErrors && packet.ErrorLayer() != nil { 178 fmt.Println("\n\n\nError decoding packet:", packet.ErrorLayer().Error()) 179 fmt.Println(hex.Dump(packet.Data())) 180 fmt.Printf("%#v\n", packet.Data()) 181 errors++ 182 hasError = true 183 } 184 if *printLayers || hasError { 185 fmt.Printf("\n=== PACKET %d ===\n", count) 186 for _, l := range packet.Layers() { 187 fmt.Printf("--- LAYER %v ---\n%#v\n\n", l.LayerType(), l) 188 } 189 fmt.Println() 190 } 191 } 192 duration := time.Since(start) 193 fmt.Printf("\tRead in %v packets in %v, %v per packet\n", count, duration, duration/time.Duration(count)) 194 if *printErrors { 195 fmt.Printf("%v errors, successfully decoded %.02f%%\n", errors, float64(count-errors)*100.0/float64(count)) 196 } 197 } 198 199 type streamFactory struct { 200 } 201 202 func (s *streamFactory) New(netFlow, tcpFlow gopacket.Flow) tcpassembly.Stream { 203 return s 204 } 205 func (s *streamFactory) Reassembled([]tcpassembly.Reassembly) { 206 } 207 func (s *streamFactory) ReassemblyComplete() { 208 } 209 210 func benchmarkLayerDecode(source *BufferPacketSource, assemble bool) { 211 var tcp layers.TCP 212 var ip layers.IPv4 213 var eth layers.Ethernet 214 var udp layers.UDP 215 var icmp layers.ICMPv4 216 var payload gopacket.Payload 217 parser := gopacket.NewDecodingLayerParser( 218 layers.LayerTypeEthernet, 219 ð, &ip, &icmp, &tcp, &udp, &payload) 220 pool := tcpassembly.NewStreamPool(&streamFactory{}) 221 assembler := tcpassembly.NewAssembler(pool) 222 var decoded []gopacket.LayerType 223 start := time.Now() 224 packets, decodedlayers, assembled := 0, 0, 0 225 for { 226 packets++ 227 data, ci, err := source.ReadPacketData() 228 if err == io.EOF { 229 break 230 } else if err != nil { 231 fmt.Println("Error reading packet: ", err) 232 continue 233 } 234 err = parser.DecodeLayers(data, &decoded) 235 for _, typ := range decoded { 236 decodedlayers++ 237 if typ == layers.LayerTypeTCP && assemble { 238 assembled++ 239 assembler.AssembleWithTimestamp(ip.NetworkFlow(), &tcp, ci.Timestamp) 240 } 241 } 242 } 243 if assemble { 244 assembler.FlushAll() 245 } 246 duration := time.Since(start) 247 fmt.Printf("\tRead in %d packets in %v, decoded %v layers, assembled %v packets: %v per packet\n", packets, duration, decodedlayers, assembled, duration/time.Duration(packets)) 248 }