github.com/gopacket/gopacket@v1.1.0/pcap/pcap_test.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  package pcap
     8  
     9  import (
    10  	"fmt"
    11  	"io"
    12  	"io/ioutil"
    13  	"log"
    14  	"os"
    15  	"testing"
    16  
    17  	"github.com/gopacket/gopacket"
    18  	"github.com/gopacket/gopacket/layers"
    19  )
    20  
    21  func TestPcapNonexistentFile(t *testing.T) {
    22  	handle, err := OpenOffline("/path/to/nonexistent/file")
    23  	if err == nil {
    24  		t.Error("No error returned for nonexistent file open")
    25  	} else {
    26  		t.Logf("Error returned for nonexistent file: %v", err)
    27  	}
    28  	if handle != nil {
    29  		t.Error("Non-nil handle returned for nonexistent file open")
    30  	}
    31  }
    32  
    33  func TestPcapFileRead(t *testing.T) {
    34  	invalidData := []byte{
    35  		0xAB, 0xAD, 0x1D, 0xEA,
    36  	}
    37  
    38  	invalidPcap, err := ioutil.TempFile("", "invalid.pcap")
    39  	if err != nil {
    40  		t.Fatal(err)
    41  	}
    42  	invalidPcap.Close() // if the file is still open later, the invalid test fails with permission denied on windows
    43  	defer os.Remove(invalidPcap.Name())
    44  
    45  	err = ioutil.WriteFile(invalidPcap.Name(), invalidData, 0644)
    46  	if err != nil {
    47  		t.Fatal(err)
    48  	}
    49  
    50  	for _, file := range []struct {
    51  		filename       string
    52  		num            int
    53  		expectedLayers []gopacket.LayerType
    54  		err            string
    55  	}{
    56  		{filename: "test_loopback.pcap",
    57  			num: 24,
    58  			expectedLayers: []gopacket.LayerType{
    59  				layers.LayerTypeLoopback,
    60  				layers.LayerTypeIPv6,
    61  				layers.LayerTypeTCP,
    62  			},
    63  		},
    64  		{filename: "test_ethernet.pcap",
    65  			num: 10,
    66  			expectedLayers: []gopacket.LayerType{
    67  				layers.LayerTypeEthernet,
    68  				layers.LayerTypeIPv4,
    69  				layers.LayerTypeTCP,
    70  			},
    71  		},
    72  		{filename: "test_dns.pcap",
    73  			num: 10,
    74  			expectedLayers: []gopacket.LayerType{
    75  				layers.LayerTypeEthernet,
    76  				layers.LayerTypeIPv4,
    77  				layers.LayerTypeUDP,
    78  				layers.LayerTypeDNS,
    79  			},
    80  		},
    81  		{filename: invalidPcap.Name(),
    82  			num: 0,
    83  			err: "unknown file format",
    84  		},
    85  	} {
    86  		t.Logf("\n\n\n\nProcessing file %s\n\n\n\n", file.filename)
    87  
    88  		packets := []gopacket.Packet{}
    89  		if handle, err := OpenOffline(file.filename); err != nil {
    90  			if file.err != "" {
    91  				if err.Error() != file.err {
    92  					t.Errorf("expected message %q; got %q", file.err, err.Error())
    93  				}
    94  			} else {
    95  				t.Fatal(err)
    96  			}
    97  		} else {
    98  			if file.err != "" {
    99  				t.Fatalf("Expected error, got none")
   100  			}
   101  			packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
   102  			for packet := range packetSource.Packets() {
   103  				packets = append(packets, packet)
   104  			}
   105  		}
   106  		if len(packets) != file.num {
   107  			t.Fatal("Incorrect number of packets, want", file.num, "got", len(packets))
   108  		}
   109  		for i, p := range packets {
   110  			t.Log(p.Dump())
   111  			for _, layertype := range file.expectedLayers {
   112  				if p.Layer(layertype) == nil {
   113  					t.Fatal("Packet", i, "has no layer type\n%s", layertype, p.Dump())
   114  				}
   115  			}
   116  		}
   117  	}
   118  }
   119  
   120  func TestBPF(t *testing.T) {
   121  	handle, err := OpenOffline("test_ethernet.pcap")
   122  	if err != nil {
   123  		t.Fatal(err)
   124  	}
   125  
   126  	for _, expected := range []struct {
   127  		expr   string
   128  		Error  bool
   129  		Result bool
   130  	}{
   131  		{"foobar", true, false},
   132  		{"tcp[tcpflags] & (tcp-syn|tcp-ack) == (tcp-syn|tcp-ack)", false, true},
   133  		{"tcp[tcpflags] & (tcp-syn|tcp-ack) == tcp-ack", false, true},
   134  		{"udp", false, false},
   135  		{string([]byte("udp")), false, false}, // test for #664
   136  	} {
   137  		data, ci, err := handle.ReadPacketData()
   138  		if err != nil {
   139  			t.Fatal(err)
   140  		}
   141  		t.Log("Testing filter", expected.expr)
   142  		if bpf, err := handle.NewBPF(expected.expr); err != nil {
   143  			if !expected.Error {
   144  				t.Error(err, "while compiling filter was unexpected")
   145  			}
   146  		} else if expected.Error {
   147  			t.Error("expected error but didn't see one")
   148  		} else if matches := bpf.Matches(ci, data); matches != expected.Result {
   149  			t.Error("Filter result was", matches, "but should be", expected.Result)
   150  		}
   151  	}
   152  }
   153  
   154  func TestBPFInstruction(t *testing.T) {
   155  	handle, err := OpenOffline("test_ethernet.pcap")
   156  	if err != nil {
   157  		t.Fatal(err)
   158  	}
   159  
   160  	cntr := 0
   161  	oversizedBpfInstructionBuffer := [MaxBpfInstructions + 1]BPFInstruction{}
   162  
   163  	for _, expected := range []struct {
   164  		Filter         string
   165  		BpfInstruction []BPFInstruction
   166  		Error          bool
   167  		Result         bool
   168  	}{
   169  		// {"foobar", true, false},
   170  		{"foobar", []BPFInstruction{}, true, false},
   171  
   172  		// tcpdump -dd 'tcp[tcpflags] & (tcp-syn|tcp-ack) == (tcp-syn|tcp-ack)'
   173  		{"tcp[tcpflags] & (tcp-syn|tcp-ack) == (tcp-syn|tcp-ack)",
   174  			[]BPFInstruction{
   175  				{0x28, 0, 0, 0x0000000c},
   176  				{0x15, 0, 9, 0x00000800},
   177  				{0x30, 0, 0, 0x00000017},
   178  				{0x15, 0, 7, 0x00000006},
   179  				{0x28, 0, 0, 0x00000014},
   180  				{0x45, 5, 0, 0x00001fff},
   181  				{0xb1, 0, 0, 0x0000000e},
   182  				{0x50, 0, 0, 0x0000001b},
   183  				{0x54, 0, 0, 0x00000012},
   184  				{0x15, 0, 1, 0x00000012},
   185  				{0x6, 0, 0, 0x0000ffff},
   186  				{0x6, 0, 0, 0x00000000},
   187  			}, false, true},
   188  
   189  		// tcpdump -dd 'tcp[tcpflags] & (tcp-syn|tcp-ack) == tcp-ack'
   190  		{"tcp[tcpflags] & (tcp-syn|tcp-ack) == tcp-ack",
   191  			[]BPFInstruction{
   192  				{0x28, 0, 0, 0x0000000c},
   193  				{0x15, 0, 9, 0x00000800},
   194  				{0x30, 0, 0, 0x00000017},
   195  				{0x15, 0, 7, 0x00000006},
   196  				{0x28, 0, 0, 0x00000014},
   197  				{0x45, 5, 0, 0x00001fff},
   198  				{0xb1, 0, 0, 0x0000000e},
   199  				{0x50, 0, 0, 0x0000001b},
   200  				{0x54, 0, 0, 0x00000012},
   201  				{0x15, 0, 1, 0x00000010},
   202  				{0x6, 0, 0, 0x0000ffff},
   203  				{0x6, 0, 0, 0x00000000},
   204  			}, false, true},
   205  
   206  		// tcpdump -dd 'udp'
   207  		{"udp",
   208  			[]BPFInstruction{
   209  				{0x28, 0, 0, 0x0000000c},
   210  				{0x15, 0, 5, 0x000086dd},
   211  				{0x30, 0, 0, 0x00000014},
   212  				{0x15, 6, 0, 0x00000011},
   213  				{0x15, 0, 6, 0x0000002c},
   214  				{0x30, 0, 0, 0x00000036},
   215  				{0x15, 3, 4, 0x00000011},
   216  				{0x15, 0, 3, 0x00000800},
   217  				{0x30, 0, 0, 0x00000017},
   218  				{0x15, 0, 1, 0x00000011},
   219  				{0x6, 0, 0, 0x0000ffff},
   220  				{0x6, 0, 0, 0x00000000},
   221  			}, false, false},
   222  
   223  		{"", oversizedBpfInstructionBuffer[:], true, false},
   224  	} {
   225  		cntr++
   226  		data, ci, err := handle.ReadPacketData()
   227  		if err != nil {
   228  			t.Fatal(err)
   229  		}
   230  
   231  		t.Log("Testing BpfInstruction filter", cntr)
   232  		if bpf, err := handle.NewBPFInstructionFilter(expected.BpfInstruction); err != nil {
   233  			if !expected.Error {
   234  				t.Error(err, "while compiling filter was unexpected")
   235  			}
   236  		} else if expected.Error {
   237  			t.Error("expected error but didn't see one")
   238  		} else if matches := bpf.Matches(ci, data); matches != expected.Result {
   239  			t.Error("Filter result was", matches, "but should be", expected.Result)
   240  		}
   241  
   242  		if expected.Filter != "" {
   243  			t.Log("Testing dead bpf filter", cntr)
   244  			if bpf, err := CompileBPFFilter(layers.LinkTypeEthernet, 65535, expected.Filter); err != nil {
   245  				if !expected.Error {
   246  					t.Error(err, "while compiling filter was unexpected")
   247  				}
   248  			} else if expected.Error {
   249  				t.Error("expected error but didn't see one")
   250  			} else {
   251  				if len(bpf) != len(expected.BpfInstruction) {
   252  					t.Errorf("expected %d instructions, got %d", len(expected.BpfInstruction), len(bpf))
   253  				}
   254  				for i := 0; i < len(bpf); i++ {
   255  					if bpf[i] != expected.BpfInstruction[i] {
   256  						t.Errorf("expected instruction %d = %d, got %d", i, expected.BpfInstruction[i], bpf[i])
   257  					}
   258  				}
   259  			}
   260  		}
   261  	}
   262  }
   263  
   264  func ExampleBPF() {
   265  	handle, err := OpenOffline("test_ethernet.pcap")
   266  	if err != nil {
   267  		log.Fatal(err)
   268  	}
   269  	synack, err := handle.NewBPF("tcp[tcpflags] & (tcp-syn|tcp-ack) == (tcp-syn|tcp-ack)")
   270  	if err != nil {
   271  		log.Fatal(err)
   272  	}
   273  	syn, err := handle.NewBPF("tcp[tcpflags] & (tcp-syn|tcp-ack) == tcp-syn")
   274  	if err != nil {
   275  		log.Fatal(err)
   276  	}
   277  	for {
   278  		data, ci, err := handle.ReadPacketData()
   279  		switch {
   280  		case err == io.EOF:
   281  			return
   282  		case err != nil:
   283  			log.Fatal(err)
   284  		case synack.Matches(ci, data):
   285  			fmt.Println("SYN/ACK packet")
   286  		case syn.Matches(ci, data):
   287  			fmt.Println("SYN packet")
   288  		default:
   289  			fmt.Println("SYN flag not set")
   290  		}
   291  	}
   292  	// Output:
   293  	// SYN packet
   294  	// SYN/ACK packet
   295  	// SYN flag not set
   296  	// SYN flag not set
   297  	// SYN flag not set
   298  	// SYN flag not set
   299  	// SYN flag not set
   300  	// SYN flag not set
   301  	// SYN flag not set
   302  	// SYN flag not set
   303  }