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 }