github.com/gopacket/gopacket@v1.1.0/pcapgo/capture_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  //go:build linux
     7  
     8  package pcapgo_test
     9  
    10  import (
    11  	"bytes"
    12  	"context"
    13  	"fmt"
    14  	"log"
    15  	"os"
    16  	"sync/atomic"
    17  	"testing"
    18  	"time"
    19  
    20  	"github.com/vishvananda/netlink"
    21  
    22  	"github.com/gopacket/gopacket"
    23  	"github.com/gopacket/gopacket/layers"
    24  	"github.com/gopacket/gopacket/pcapgo"
    25  )
    26  
    27  const (
    28  	timeout = 100 * time.Millisecond
    29  )
    30  
    31  func Example_captureEthernet() {
    32  	f, err := os.Create("/tmp/lo.pcap")
    33  	if err != nil {
    34  		log.Fatal(err)
    35  	}
    36  	defer f.Close()
    37  	pcapw := pcapgo.NewWriter(f)
    38  	if err := pcapw.WriteFileHeader(1600, layers.LinkTypeEthernet); err != nil {
    39  		log.Fatalf("WriteFileHeader: %v", err)
    40  	}
    41  
    42  	handle, err := pcapgo.NewEthernetHandle("lo")
    43  	if err != nil {
    44  		log.Fatalf("OpenEthernet: %v", err)
    45  	}
    46  
    47  	pkgsrc := gopacket.NewPacketSource(handle, layers.LayerTypeEthernet)
    48  	ctx, cancel := context.WithCancel(context.Background())
    49  	defer cancel()
    50  	for packet := range pkgsrc.PacketsCtx(ctx) {
    51  		if err := pcapw.WritePacket(packet.Metadata().CaptureInfo, packet.Data()); err != nil {
    52  			log.Fatalf("pcap.WritePacket(): %v", err)
    53  		}
    54  	}
    55  }
    56  
    57  func TestEthernetHandle_Close_WithTimeout(t *testing.T) {
    58  	var (
    59  		handle *pcapgo.EthernetHandle
    60  		err    error
    61  		done   = make(chan struct{})
    62  	)
    63  
    64  	handle, err = pcapgo.NewEthernetHandle(setupDummyInterface(t))
    65  	if err != nil {
    66  		t.Fatalf("OpenEthernet: %v", err)
    67  	}
    68  
    69  	ctx, cancel := context.WithTimeout(context.Background(), timeout)
    70  	defer cancel()
    71  
    72  	pkgsrc := gopacket.NewPacketSource(handle, layers.LayerTypeEthernet)
    73  
    74  	go consumePacketSource(ctx, t, pkgsrc, done)
    75  
    76  	select {
    77  	case _, more := <-done:
    78  		if more {
    79  			t.Fatalf("done channel is polluted?!")
    80  		} else {
    81  			t.Log("PacketSource got closed")
    82  		}
    83  	case <-time.After(2 * timeout):
    84  	}
    85  }
    86  
    87  func TestEthernetHandle_Close_WithCancel(t *testing.T) {
    88  	var (
    89  		handle *pcapgo.EthernetHandle
    90  		err    error
    91  		done   = make(chan struct{})
    92  	)
    93  
    94  	handle, err = pcapgo.NewEthernetHandle(setupDummyInterface(t))
    95  	if err != nil {
    96  		t.Fatalf("OpenEthernet: %v", err)
    97  	}
    98  
    99  	ctx, cancel := context.WithCancel(context.Background())
   100  
   101  	pkgsrc := gopacket.NewPacketSource(handle, layers.LayerTypeEthernet)
   102  	go consumePacketSource(ctx, t, pkgsrc, done)
   103  
   104  	go func() {
   105  		<-time.After(timeout)
   106  		cancel()
   107  	}()
   108  
   109  	select {
   110  	case _, more := <-done:
   111  		if more {
   112  			t.Fatalf("done channel is polluted?!")
   113  		} else {
   114  			t.Log("PacketSource got closed")
   115  		}
   116  	case <-time.After(2 * timeout):
   117  	}
   118  }
   119  
   120  func consumePacketSource(ctx context.Context, tb testing.TB, pkgsrc *gopacket.PacketSource, done chan<- struct{}) {
   121  	tb.Helper()
   122  	var writer = pcapgo.NewWriter(new(bytes.Buffer))
   123  	defer close(done)
   124  	for packet := range pkgsrc.PacketsCtx(ctx) {
   125  		if err := writer.WritePacket(packet.Metadata().CaptureInfo, packet.Data()); err != nil {
   126  			tb.Errorf("pcap.WritePacket(): %v", err)
   127  		}
   128  	}
   129  }
   130  
   131  var (
   132  	dummyInterfaceIdx int32 = -1
   133  )
   134  
   135  // setupDummyInterface configures a dummy interface and returns the generated interface name.
   136  // It assigns an address from the 127.10.0.0/24 network.
   137  // It does not check if there are already more than 254 interfaces.
   138  // If there are the call to netlink.ParseAddr will fail because 127.10.0.256/24 isn't a valid IP address,
   139  // but it should be fine for testing purposes.
   140  func setupDummyInterface(tb testing.TB) (ifName string) {
   141  	tb.Helper()
   142  	la := netlink.NewLinkAttrs()
   143  	idx := atomic.AddInt32(&dummyInterfaceIdx, 1)
   144  	la.Name = fmt.Sprintf("dummy%02d", idx)
   145  	dummyInterface := &netlink.Dummy{LinkAttrs: la}
   146  	if err := netlink.LinkAdd(dummyInterface); err != nil {
   147  		tb.Fatalf("netlink.LinkAdd() error = %v", err)
   148  	}
   149  
   150  	var (
   151  		link netlink.Link
   152  		addr *netlink.Addr
   153  		err  error
   154  	)
   155  
   156  	link, err = netlink.LinkByName(la.Name)
   157  	if err != nil {
   158  		tb.Fatalf("netlink.LinkByName() error = %v", err)
   159  	}
   160  
   161  	tb.Cleanup(func() {
   162  		if err := netlink.LinkDel(link); err != nil {
   163  			tb.Fatalf("netlink.LinkDel() error = %v", err)
   164  		}
   165  	})
   166  
   167  	addr, err = netlink.ParseAddr(fmt.Sprintf("127.10.0.%d/24", idx+1))
   168  	if err != nil {
   169  		tb.Fatalf("netlink.ParseAddr() = %v", err)
   170  	}
   171  
   172  	if err := netlink.AddrAdd(link, addr); err != nil {
   173  		tb.Fatalf("netlink.AddrAdd() error = %v", err)
   174  	}
   175  
   176  	if err := netlink.LinkSetUp(link); err != nil {
   177  		tb.Fatalf("netlink.LinkSetUp() error = %v", err)
   178  	}
   179  
   180  	return la.Name
   181  }