mirror of
https://github.com/weaveworks/scope.git
synced 2026-07-18 04:49:55 +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
309 lines
7.7 KiB
Go
309 lines
7.7 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.
|
|
|
|
package pcap
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"testing"
|
|
|
|
"github.com/google/gopacket"
|
|
"github.com/google/gopacket/layers"
|
|
)
|
|
|
|
func TestPcapNonexistentFile(t *testing.T) {
|
|
handle, err := OpenOffline("/path/to/nonexistent/file")
|
|
if err == nil {
|
|
t.Error("No error returned for nonexistent file open")
|
|
} else {
|
|
t.Logf("Error returned for nonexistent file: %v", err)
|
|
}
|
|
if handle != nil {
|
|
t.Error("Non-nil handle returned for nonexistent file open")
|
|
}
|
|
}
|
|
|
|
func TestPcapFileRead(t *testing.T) {
|
|
invalidData := []byte{
|
|
0xAB, 0xAD, 0x1D, 0xEA,
|
|
}
|
|
|
|
invalidPcap, err := ioutil.TempFile("", "invalid.pcap")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.Remove(invalidPcap.Name())
|
|
|
|
err = ioutil.WriteFile(invalidPcap.Name(), invalidData, 0644)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer invalidPcap.Close()
|
|
|
|
for _, file := range []struct {
|
|
filename string
|
|
num int
|
|
expectedLayers []gopacket.LayerType
|
|
err string
|
|
}{
|
|
{filename: "test_loopback.pcap",
|
|
num: 24,
|
|
expectedLayers: []gopacket.LayerType{
|
|
layers.LayerTypeLoopback,
|
|
layers.LayerTypeIPv6,
|
|
layers.LayerTypeTCP,
|
|
},
|
|
},
|
|
{filename: "test_ethernet.pcap",
|
|
num: 16,
|
|
expectedLayers: []gopacket.LayerType{
|
|
layers.LayerTypeEthernet,
|
|
layers.LayerTypeIPv4,
|
|
layers.LayerTypeTCP,
|
|
},
|
|
},
|
|
{filename: "test_dns.pcap",
|
|
num: 10,
|
|
expectedLayers: []gopacket.LayerType{
|
|
layers.LayerTypeEthernet,
|
|
layers.LayerTypeIPv4,
|
|
layers.LayerTypeUDP,
|
|
layers.LayerTypeDNS,
|
|
},
|
|
},
|
|
{filename: invalidPcap.Name(),
|
|
num: 0,
|
|
err: "unknown file format",
|
|
},
|
|
} {
|
|
t.Logf("\n\n\n\nProcessing file %s\n\n\n\n", file.filename)
|
|
|
|
packets := []gopacket.Packet{}
|
|
if handle, err := OpenOffline(file.filename); err != nil {
|
|
if file.err != "" {
|
|
if err.Error() != file.err {
|
|
t.Errorf("expected message %q; got %q", file.err, err.Error())
|
|
}
|
|
} else {
|
|
t.Fatal(err)
|
|
}
|
|
} else {
|
|
if file.err != "" {
|
|
t.Fatalf("Expected error, got none")
|
|
}
|
|
packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
|
|
for packet := range packetSource.Packets() {
|
|
packets = append(packets, packet)
|
|
}
|
|
}
|
|
if len(packets) != file.num {
|
|
t.Fatal("Incorrect number of packets, want", file.num, "got", len(packets))
|
|
}
|
|
for i, p := range packets {
|
|
t.Log(p.Dump())
|
|
for _, layertype := range file.expectedLayers {
|
|
if p.Layer(layertype) == nil {
|
|
t.Fatal("Packet", i, "has no layer type\n%s", layertype, p.Dump())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestBPF(t *testing.T) {
|
|
handle, err := OpenOffline("test_ethernet.pcap")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
for _, expected := range []struct {
|
|
expr string
|
|
Error bool
|
|
Result bool
|
|
}{
|
|
{"foobar", true, false},
|
|
{"tcp[tcpflags] & (tcp-syn|tcp-ack) == (tcp-syn|tcp-ack)", false, true},
|
|
{"tcp[tcpflags] & (tcp-syn|tcp-ack) == tcp-ack", false, true},
|
|
{"udp", false, false},
|
|
} {
|
|
data, ci, err := handle.ReadPacketData()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Log("Testing filter", expected.expr)
|
|
if bpf, err := handle.NewBPF(expected.expr); err != nil {
|
|
if !expected.Error {
|
|
t.Error(err, "while compiling filter was unexpected")
|
|
}
|
|
} else if expected.Error {
|
|
t.Error("expected error but didn't see one")
|
|
} else if matches := bpf.Matches(ci, data); matches != expected.Result {
|
|
t.Error("Filter result was", matches, "but should be", expected.Result)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestBPFInstruction(t *testing.T) {
|
|
handle, err := OpenOffline("test_ethernet.pcap")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
cntr := 0
|
|
oversizedBpfInstructionBuffer := [MaxBpfInstructions + 1]BPFInstruction{}
|
|
|
|
for _, expected := range []struct {
|
|
Filter string
|
|
BpfInstruction []BPFInstruction
|
|
Error bool
|
|
Result bool
|
|
}{
|
|
// {"foobar", true, false},
|
|
{"foobar", []BPFInstruction{}, true, false},
|
|
|
|
// tcpdump -dd 'tcp[tcpflags] & (tcp-syn|tcp-ack) == (tcp-syn|tcp-ack)'
|
|
{"tcp[tcpflags] & (tcp-syn|tcp-ack) == (tcp-syn|tcp-ack)",
|
|
[]BPFInstruction{
|
|
{0x28, 0, 0, 0x0000000c},
|
|
{0x15, 0, 9, 0x00000800},
|
|
{0x30, 0, 0, 0x00000017},
|
|
{0x15, 0, 7, 0x00000006},
|
|
{0x28, 0, 0, 0x00000014},
|
|
{0x45, 5, 0, 0x00001fff},
|
|
{0xb1, 0, 0, 0x0000000e},
|
|
{0x50, 0, 0, 0x0000001b},
|
|
{0x54, 0, 0, 0x00000012},
|
|
{0x15, 0, 1, 0x00000012},
|
|
{0x6, 0, 0, 0x0000ffff},
|
|
{0x6, 0, 0, 0x00000000},
|
|
}, false, true},
|
|
|
|
// tcpdump -dd 'tcp[tcpflags] & (tcp-syn|tcp-ack) == tcp-ack'
|
|
{"tcp[tcpflags] & (tcp-syn|tcp-ack) == tcp-ack",
|
|
[]BPFInstruction{
|
|
{0x28, 0, 0, 0x0000000c},
|
|
{0x15, 0, 9, 0x00000800},
|
|
{0x30, 0, 0, 0x00000017},
|
|
{0x15, 0, 7, 0x00000006},
|
|
{0x28, 0, 0, 0x00000014},
|
|
{0x45, 5, 0, 0x00001fff},
|
|
{0xb1, 0, 0, 0x0000000e},
|
|
{0x50, 0, 0, 0x0000001b},
|
|
{0x54, 0, 0, 0x00000012},
|
|
{0x15, 0, 1, 0x00000010},
|
|
{0x6, 0, 0, 0x0000ffff},
|
|
{0x6, 0, 0, 0x00000000},
|
|
}, false, true},
|
|
|
|
// tcpdump -dd 'udp'
|
|
{"udp",
|
|
[]BPFInstruction{
|
|
{0x28, 0, 0, 0x0000000c},
|
|
{0x15, 0, 5, 0x000086dd},
|
|
{0x30, 0, 0, 0x00000014},
|
|
{0x15, 6, 0, 0x00000011},
|
|
{0x15, 0, 6, 0x0000002c},
|
|
{0x30, 0, 0, 0x00000036},
|
|
{0x15, 3, 4, 0x00000011},
|
|
{0x15, 0, 3, 0x00000800},
|
|
{0x30, 0, 0, 0x00000017},
|
|
{0x15, 0, 1, 0x00000011},
|
|
{0x6, 0, 0, 0x0000ffff},
|
|
{0x6, 0, 0, 0x00000000},
|
|
}, false, false},
|
|
|
|
{"", oversizedBpfInstructionBuffer[:], true, false},
|
|
} {
|
|
cntr++
|
|
data, ci, err := handle.ReadPacketData()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
t.Log("Testing BpfInstruction filter", cntr)
|
|
if bpf, err := handle.NewBPFInstructionFilter(expected.BpfInstruction); err != nil {
|
|
if !expected.Error {
|
|
t.Error(err, "while compiling filter was unexpected")
|
|
}
|
|
} else if expected.Error {
|
|
t.Error("expected error but didn't see one")
|
|
} else if matches := bpf.Matches(ci, data); matches != expected.Result {
|
|
t.Error("Filter result was", matches, "but should be", expected.Result)
|
|
}
|
|
|
|
if expected.Filter != "" {
|
|
t.Log("Testing dead bpf filter", cntr)
|
|
if bpf, err := CompileBPFFilter(layers.LinkTypeEthernet, 65535, expected.Filter); err != nil {
|
|
if !expected.Error {
|
|
t.Error(err, "while compiling filter was unexpected")
|
|
}
|
|
} else if expected.Error {
|
|
t.Error("expected error but didn't see one")
|
|
} else {
|
|
if len(bpf) != len(expected.BpfInstruction) {
|
|
t.Errorf("expected %d instructions, got %d", len(expected.BpfInstruction), len(bpf))
|
|
}
|
|
for i := 0; i < len(bpf); i++ {
|
|
if bpf[i] != expected.BpfInstruction[i] {
|
|
t.Errorf("expected instruction %d = %d, got %d", i, expected.BpfInstruction[i], bpf[i])
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func ExampleBPF() {
|
|
handle, err := OpenOffline("test_ethernet.pcap")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
synack, err := handle.NewBPF("tcp[tcpflags] & (tcp-syn|tcp-ack) == (tcp-syn|tcp-ack)")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
syn, err := handle.NewBPF("tcp[tcpflags] & (tcp-syn|tcp-ack) == tcp-syn")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
for {
|
|
data, ci, err := handle.ReadPacketData()
|
|
switch {
|
|
case err == io.EOF:
|
|
return
|
|
case err != nil:
|
|
log.Fatal(err)
|
|
case synack.Matches(ci, data):
|
|
fmt.Println("SYN/ACK packet")
|
|
case syn.Matches(ci, data):
|
|
fmt.Println("SYN packet")
|
|
default:
|
|
fmt.Println("SYN flag not set")
|
|
}
|
|
}
|
|
// Output:
|
|
// SYN packet
|
|
// SYN/ACK packet
|
|
// SYN flag not set
|
|
// SYN flag not set
|
|
// SYN flag not set
|
|
// SYN flag not set
|
|
// SYN flag not set
|
|
// SYN flag not set
|
|
// SYN flag not set
|
|
// SYN flag not set
|
|
// SYN flag not set
|
|
// SYN flag not set
|
|
// SYN flag not set
|
|
// SYN flag not set
|
|
// SYN flag not set
|
|
// SYN flag not set
|
|
}
|