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// The pcaplay binary load an offline capture (pcap file) and replay 8// it on the select interface, with an emphasis on packet timing 9package main 10 11import ( 12 "flag" 13 "fmt" 14 "io" 15 "log" 16 "os" 17 "strings" 18 "time" 19 20 "github.com/google/gopacket" 21 "github.com/google/gopacket/examples/util" 22 "github.com/google/gopacket/pcap" 23) 24 25var iface = flag.String("i", "eth0", "Interface to write packets to") 26var fname = flag.String("r", "", "Filename to read from") 27var fast = flag.Bool("f", false, "Send each packets as fast as possible") 28 29var lastTS time.Time 30var lastSend time.Time 31 32var start time.Time 33var bytesSent int 34 35func writePacketDelayed(handle *pcap.Handle, buf []byte, ci gopacket.CaptureInfo) { 36 if ci.CaptureLength != ci.Length { 37 // do not write truncated packets 38 return 39 } 40 41 intervalInCapture := ci.Timestamp.Sub(lastTS) 42 elapsedTime := time.Since(lastSend) 43 44 if (intervalInCapture > elapsedTime) && !lastSend.IsZero() { 45 time.Sleep(intervalInCapture - elapsedTime) 46 } 47 48 lastSend = time.Now() 49 writePacket(handle, buf) 50 lastTS = ci.Timestamp 51} 52 53func writePacket(handle *pcap.Handle, buf []byte) error { 54 if err := handle.WritePacketData(buf); err != nil { 55 log.Printf("Failed to send packet: %s\n", err) 56 return err 57 } 58 return nil 59} 60 61func pcapInfo(filename string) (start time.Time, end time.Time, packets int, size int) { 62 handleRead, err := pcap.OpenOffline(*fname) 63 if err != nil { 64 log.Fatal("PCAP OpenOffline error (handle to read packet):", err) 65 } 66 67 var previousTs time.Time 68 var deltaTotal time.Duration 69 70 for { 71 data, ci, err := handleRead.ReadPacketData() 72 if err != nil && err != io.EOF { 73 log.Fatal(err) 74 } else if err == io.EOF { 75 break 76 } else { 77 78 if start.IsZero() { 79 start = ci.Timestamp 80 } 81 end = ci.Timestamp 82 packets++ 83 size += len(data) 84 85 if previousTs.IsZero() { 86 previousTs = ci.Timestamp 87 } else { 88 deltaTotal += ci.Timestamp.Sub(previousTs) 89 previousTs = ci.Timestamp 90 } 91 } 92 } 93 sec := int(deltaTotal.Seconds()) 94 if sec == 0 { 95 sec = 1 96 } 97 fmt.Printf("Avg packet rate %d/s\n", packets/sec) 98 return start, end, packets, size 99} 100 101func main() { 102 defer util.Run()() 103 104 // Sanity checks 105 if *fname == "" { 106 log.Fatal("Need a input file") 107 } 108 109 // Open PCAP file + handle potential BPF Filter 110 handleRead, err := pcap.OpenOffline(*fname) 111 if err != nil { 112 log.Fatal("PCAP OpenOffline error (handle to read packet):", err) 113 } 114 defer handleRead.Close() 115 if len(flag.Args()) > 0 { 116 bpffilter := strings.Join(flag.Args(), " ") 117 fmt.Fprintf(os.Stderr, "Using BPF filter %q\n", bpffilter) 118 if err = handleRead.SetBPFFilter(bpffilter); err != nil { 119 log.Fatal("BPF filter error:", err) 120 } 121 } 122 // Open up a second pcap handle for packet writes. 123 handleWrite, err := pcap.OpenLive(*iface, 65536, true, pcap.BlockForever) 124 if err != nil { 125 log.Fatal("PCAP OpenLive error (handle to write packet):", err) 126 } 127 defer handleWrite.Close() 128 129 start = time.Now() 130 pkt := 0 131 tsStart, tsEnd, packets, size := pcapInfo(*fname) 132 133 // Loop over packets and write them 134 for { 135 data, ci, err := handleRead.ReadPacketData() 136 switch { 137 case err == io.EOF: 138 fmt.Printf("\nFinished in %s", time.Since(start)) 139 return 140 case err != nil: 141 log.Printf("Failed to read packet %d: %s\n", pkt, err) 142 default: 143 if *fast { 144 writePacket(handleWrite, data) 145 } else { 146 writePacketDelayed(handleWrite, data, ci) 147 } 148 149 bytesSent += len(data) 150 duration := time.Since(start) 151 pkt++ 152 153 if duration > time.Second { 154 rate := bytesSent / int(duration.Seconds()) 155 remainingTime := tsEnd.Sub(tsStart) - duration 156 fmt.Printf("\rrate %d kB/sec - sent %d/%d kB - %d/%d packets - remaining time %s", 157 rate/1000, bytesSent/1000, size/1000, 158 pkt, packets, remainingTime) 159 } 160 } 161 } 162 163} 164