github.com/gopacket/gopacket@v1.1.0/examples/afpacket/afpacket.go (about)

     1  // Copyright 2018 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  // afpacket provides a simple example of using afpacket with zero-copy to read
     8  // packet data.
     9  package main
    10  
    11  import (
    12  	"flag"
    13  	"fmt"
    14  	"log"
    15  	"os"
    16  	"runtime/pprof"
    17  	"time"
    18  
    19  	"github.com/gopacket/gopacket"
    20  	"github.com/gopacket/gopacket/afpacket"
    21  	"github.com/gopacket/gopacket/layers"
    22  	"github.com/gopacket/gopacket/pcap"
    23  	"golang.org/x/net/bpf"
    24  
    25  	_ "github.com/gopacket/gopacket/layers"
    26  )
    27  
    28  var (
    29  	iface      = flag.String("i", "any", "Interface to read from")
    30  	cpuprofile = flag.String("cpuprofile", "", "If non-empty, write CPU profile here")
    31  	snaplen    = flag.Int("s", 0, "Snaplen, if <= 0, use 65535")
    32  	bufferSize = flag.Int("b", 8, "Interface buffersize (MB)")
    33  	filter     = flag.String("f", "port not 22", "BPF filter")
    34  	count      = flag.Int64("c", -1, "If >= 0, # of packets to capture before returning")
    35  	verbose    = flag.Int64("log_every", 1, "Write a log every X packets")
    36  	addVLAN    = flag.Bool("add_vlan", false, "If true, add VLAN header")
    37  )
    38  
    39  type afpacketHandle struct {
    40  	TPacket *afpacket.TPacket
    41  }
    42  
    43  func newAfpacketHandle(device string, snaplen int, block_size int, num_blocks int,
    44  	useVLAN bool, timeout time.Duration) (*afpacketHandle, error) {
    45  
    46  	h := &afpacketHandle{}
    47  	var err error
    48  
    49  	if device == "any" {
    50  		h.TPacket, err = afpacket.NewTPacket(
    51  			afpacket.OptFrameSize(snaplen),
    52  			afpacket.OptBlockSize(block_size),
    53  			afpacket.OptNumBlocks(num_blocks),
    54  			afpacket.OptAddVLANHeader(useVLAN),
    55  			afpacket.OptPollTimeout(timeout),
    56  			afpacket.SocketRaw,
    57  			afpacket.TPacketVersion3)
    58  	} else {
    59  		h.TPacket, err = afpacket.NewTPacket(
    60  			afpacket.OptInterface(device),
    61  			afpacket.OptFrameSize(snaplen),
    62  			afpacket.OptBlockSize(block_size),
    63  			afpacket.OptNumBlocks(num_blocks),
    64  			afpacket.OptAddVLANHeader(useVLAN),
    65  			afpacket.OptPollTimeout(timeout),
    66  			afpacket.SocketRaw,
    67  			afpacket.TPacketVersion3)
    68  	}
    69  	return h, err
    70  }
    71  
    72  // ZeroCopyReadPacketData satisfies ZeroCopyPacketDataSource interface
    73  func (h *afpacketHandle) ZeroCopyReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error) {
    74  	return h.TPacket.ZeroCopyReadPacketData()
    75  }
    76  
    77  // SetBPFFilter translates a BPF filter string into BPF RawInstruction and applies them.
    78  func (h *afpacketHandle) SetBPFFilter(filter string, snaplen int) (err error) {
    79  	pcapBPF, err := pcap.CompileBPFFilter(layers.LinkTypeEthernet, snaplen, filter)
    80  	if err != nil {
    81  		return err
    82  	}
    83  	bpfIns := []bpf.RawInstruction{}
    84  	for _, ins := range pcapBPF {
    85  		bpfIns2 := bpf.RawInstruction{
    86  			Op: ins.Code,
    87  			Jt: ins.Jt,
    88  			Jf: ins.Jf,
    89  			K:  ins.K,
    90  		}
    91  		bpfIns = append(bpfIns, bpfIns2)
    92  	}
    93  	if h.TPacket.SetBPF(bpfIns); err != nil {
    94  		return err
    95  	}
    96  	return h.TPacket.SetBPF(bpfIns)
    97  }
    98  
    99  // LinkType returns ethernet link type.
   100  func (h *afpacketHandle) LinkType() layers.LinkType {
   101  	return layers.LinkTypeEthernet
   102  }
   103  
   104  // Close will close afpacket source.
   105  func (h *afpacketHandle) Close() {
   106  	h.TPacket.Close()
   107  }
   108  
   109  // SocketStats prints received, dropped, queue-freeze packet stats.
   110  func (h *afpacketHandle) SocketStats() (as afpacket.SocketStats, asv afpacket.SocketStatsV3, err error) {
   111  	return h.TPacket.SocketStats()
   112  }
   113  
   114  // afpacketComputeSize computes the block_size and the num_blocks in such a way that the
   115  // allocated mmap buffer is close to but smaller than target_size_mb.
   116  // The restriction is that the block_size must be divisible by both the
   117  // frame size and page size.
   118  func afpacketComputeSize(targetSizeMb int, snaplen int, pageSize int) (
   119  	frameSize int, blockSize int, numBlocks int, err error) {
   120  
   121  	if snaplen < pageSize {
   122  		frameSize = pageSize / (pageSize / snaplen)
   123  	} else {
   124  		frameSize = (snaplen/pageSize + 1) * pageSize
   125  	}
   126  
   127  	// 128 is the default from the gopacket library so just use that
   128  	blockSize = frameSize * 128
   129  	numBlocks = (targetSizeMb * 1024 * 1024) / blockSize
   130  
   131  	if numBlocks == 0 {
   132  		return 0, 0, 0, fmt.Errorf("Interface buffersize is too small")
   133  	}
   134  
   135  	return frameSize, blockSize, numBlocks, nil
   136  }
   137  
   138  func main() {
   139  	flag.Parse()
   140  	if *cpuprofile != "" {
   141  		log.Printf("Writing CPU profile to %q", *cpuprofile)
   142  		f, err := os.Create(*cpuprofile)
   143  		if err != nil {
   144  			log.Fatal(err)
   145  		}
   146  		if err := pprof.StartCPUProfile(f); err != nil {
   147  			log.Fatal(err)
   148  		}
   149  		defer pprof.StopCPUProfile()
   150  	}
   151  	log.Printf("Starting on interface %q", *iface)
   152  	if *snaplen <= 0 {
   153  		*snaplen = 65535
   154  	}
   155  	szFrame, szBlock, numBlocks, err := afpacketComputeSize(*bufferSize, *snaplen, os.Getpagesize())
   156  	if err != nil {
   157  		log.Fatal(err)
   158  	}
   159  	afpacketHandle, err := newAfpacketHandle(*iface, szFrame, szBlock, numBlocks, *addVLAN, pcap.BlockForever)
   160  	if err != nil {
   161  		log.Fatal(err)
   162  	}
   163  	err = afpacketHandle.SetBPFFilter(*filter, *snaplen)
   164  	if err != nil {
   165  		log.Fatal(err)
   166  	}
   167  	source := gopacket.ZeroCopyPacketDataSource(afpacketHandle)
   168  	defer afpacketHandle.Close()
   169  
   170  	bytes := uint64(0)
   171  	packets := uint64(0)
   172  	for ; *count != 0; *count-- {
   173  		data, _, err := source.ZeroCopyReadPacketData()
   174  		if err != nil {
   175  			log.Fatal(err)
   176  		}
   177  		bytes += uint64(len(data))
   178  		packets++
   179  		if *count%*verbose == 0 {
   180  			_, afpacketStats, err := afpacketHandle.SocketStats()
   181  			if err != nil {
   182  				log.Println(err)
   183  			}
   184  			log.Printf("Read in %d bytes in %d packets", bytes, packets)
   185  			log.Printf("Stats {received dropped queue-freeze}: %d", afpacketStats)
   186  		}
   187  	}
   188  }