mirror of
https://github.com/weaveworks/scope.git
synced 2026-05-14 21:27:32 +00:00
We'd like to benefit from the memory reduction from: https://github.com/google/gopacket/pull/377 I just ran: $ gvt update github.com/google/gopacket Fixes: https://github.com/weaveworks/scope/issues/2905
164 lines
3.8 KiB
Go
164 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.
|
|
|
|
// 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)
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|