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// +build linux 8 9package afpacket 10 11import ( 12 "errors" 13 "fmt" 14 "time" 15 16 "golang.org/x/sys/unix" 17) 18 19// OptTPacketVersion is the version of TPacket to use. 20// It can be passed into NewTPacket. 21type OptTPacketVersion int 22 23// String returns a string representation of the version, generally of the form V#. 24func (t OptTPacketVersion) String() string { 25 switch t { 26 case TPacketVersion1: 27 return "V1" 28 case TPacketVersion2: 29 return "V2" 30 case TPacketVersion3: 31 return "V3" 32 case TPacketVersionHighestAvailable: 33 return "HighestAvailable" 34 } 35 return "InvalidVersion" 36} 37 38// OptSocketType is the socket type used to open the TPacket socket. 39type OptSocketType int 40 41func (t OptSocketType) String() string { 42 switch t { 43 case SocketRaw: 44 return "SOCK_RAW" 45 case SocketDgram: 46 return "SOCK_DGRAM" 47 } 48 return "UnknownSocketType" 49} 50 51// TPacket version numbers for use with NewHandle. 52const ( 53 // TPacketVersionHighestAvailable tells NewHandle to use the highest available version of tpacket the kernel has available. 54 // This is the default, should a version number not be given in NewHandle's options. 55 TPacketVersionHighestAvailable = OptTPacketVersion(-1) 56 TPacketVersion1 = OptTPacketVersion(unix.TPACKET_V1) 57 TPacketVersion2 = OptTPacketVersion(unix.TPACKET_V2) 58 TPacketVersion3 = OptTPacketVersion(unix.TPACKET_V3) 59 tpacketVersionMax = TPacketVersion3 60 tpacketVersionMin = -1 61 // SocketRaw is the default socket type. It returns packet data 62 // including the link layer (ethernet headers, etc). 63 SocketRaw = OptSocketType(unix.SOCK_RAW) 64 // SocketDgram strips off the link layer when reading packets, and adds 65 // the link layer back automatically on packet writes (coming soon...) 66 SocketDgram = OptSocketType(unix.SOCK_DGRAM) 67) 68 69// OptInterface is the specific interface to bind to. 70// It can be passed into NewTPacket. 71type OptInterface string 72 73// OptFrameSize is TPacket's tp_frame_size 74// It can be passed into NewTPacket. 75type OptFrameSize int 76 77// OptBlockSize is TPacket's tp_block_size 78// It can be passed into NewTPacket. 79type OptBlockSize int 80 81// OptNumBlocks is TPacket's tp_block_nr 82// It can be passed into NewTPacket. 83type OptNumBlocks int 84 85// OptBlockTimeout is TPacket v3's tp_retire_blk_tov. Note that it has only millisecond granularity, so must be >= 1 ms. 86// It can be passed into NewTPacket. 87type OptBlockTimeout time.Duration 88 89// OptPollTimeout is the number of milliseconds that poll() should block waiting for a file 90// descriptor to become ready. Specifying a negative value in time‐out means an infinite timeout. 91type OptPollTimeout time.Duration 92 93// OptAddVLANHeader modifies the packet data that comes back from the 94// kernel by adding in the VLAN header that the NIC stripped. AF_PACKET by 95// default uses VLAN offloading, in which the NIC strips the VLAN header off of 96// the packet before handing it to the kernel. This means that, even if a 97// packet has an 802.1q header on the wire, it'll show up without one by the 98// time it goes through AF_PACKET. If this option is true, the VLAN header is 99// added back in before the packet is returned. Note that this potentially has 100// a large performance hit, especially in otherwise zero-copy operation. 101// 102// Note that if you do not need to have a "real" VLAN layer, it may be 103// preferable to use the VLAN ID provided by the AncillaryVLAN struct 104// in CaptureInfo.AncillaryData, which is populated out-of-band and has 105// negligible performance impact. Such ancillary data will automatically 106// be provided if available. 107type OptAddVLANHeader bool 108 109// Default constants used by options. 110const ( 111 DefaultFrameSize = 4096 // Default value for OptFrameSize. 112 DefaultBlockSize = DefaultFrameSize * 128 // Default value for OptBlockSize. 113 DefaultNumBlocks = 128 // Default value for OptNumBlocks. 114 DefaultBlockTimeout = 64 * time.Millisecond // Default value for OptBlockTimeout. 115 DefaultPollTimeout = -1 * time.Millisecond // Default value for OptPollTimeout. This blocks forever. 116) 117 118type options struct { 119 frameSize int 120 framesPerBlock int 121 blockSize int 122 numBlocks int 123 addVLANHeader bool 124 blockTimeout time.Duration 125 pollTimeout time.Duration 126 version OptTPacketVersion 127 socktype OptSocketType 128 iface string 129} 130 131var defaultOpts = options{ 132 frameSize: DefaultFrameSize, 133 blockSize: DefaultBlockSize, 134 numBlocks: DefaultNumBlocks, 135 blockTimeout: DefaultBlockTimeout, 136 pollTimeout: DefaultPollTimeout, 137 version: TPacketVersionHighestAvailable, 138 socktype: SocketRaw, 139} 140 141func parseOptions(opts ...interface{}) (ret options, err error) { 142 ret = defaultOpts 143 for _, opt := range opts { 144 switch v := opt.(type) { 145 case OptFrameSize: 146 ret.frameSize = int(v) 147 case OptBlockSize: 148 ret.blockSize = int(v) 149 case OptNumBlocks: 150 ret.numBlocks = int(v) 151 case OptBlockTimeout: 152 ret.blockTimeout = time.Duration(v) 153 case OptPollTimeout: 154 ret.pollTimeout = time.Duration(v) 155 case OptTPacketVersion: 156 ret.version = v 157 case OptInterface: 158 ret.iface = string(v) 159 case OptSocketType: 160 ret.socktype = v 161 case OptAddVLANHeader: 162 ret.addVLANHeader = bool(v) 163 default: 164 err = errors.New("unknown type in options") 165 return 166 } 167 } 168 if err = ret.check(); err != nil { 169 return 170 } 171 ret.framesPerBlock = ret.blockSize / ret.frameSize 172 return 173} 174func (o options) check() error { 175 switch { 176 case o.blockSize%pageSize != 0: 177 return fmt.Errorf("block size %d must be divisible by page size %d", o.blockSize, pageSize) 178 case o.blockSize%o.frameSize != 0: 179 return fmt.Errorf("block size %d must be divisible by frame size %d", o.blockSize, o.frameSize) 180 case o.numBlocks < 1: 181 return fmt.Errorf("num blocks %d must be >= 1", o.numBlocks) 182 case o.blockTimeout < time.Millisecond: 183 return fmt.Errorf("block timeout %v must be > %v", o.blockTimeout, time.Millisecond) 184 case o.version < tpacketVersionMin || o.version > tpacketVersionMax: 185 return fmt.Errorf("tpacket version %v is invalid", o.version) 186 } 187 return nil 188} 189