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