3e1da0b9107191cf093a36579a72ac4f75ba1551
[govpp.git] / vendor / github.com / google / gopacket / bsdbpf / bsd_bpf_sniffer.go
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 darwin dragonfly freebsd netbsd openbsd
8
9 package bsdbpf
10
11 import (
12         "errors"
13         "fmt"
14         "syscall"
15         "time"
16         "unsafe"
17
18         "github.com/google/gopacket"
19         "golang.org/x/sys/unix"
20 )
21
22 const wordSize = int(unsafe.Sizeof(uintptr(0)))
23
24 func bpfWordAlign(x int) int {
25         return (((x) + (wordSize - 1)) &^ (wordSize - 1))
26 }
27
28 // Options is used to configure various properties of the BPF sniffer.
29 // Default values are used when a nil Options pointer is passed to NewBPFSniffer.
30 type Options struct {
31         // BPFDeviceName is name of the bpf device to use for sniffing
32         // the network device. The default value of BPFDeviceName is empty string
33         // which causes the first available BPF device file /dev/bpfX to be used.
34         BPFDeviceName string
35         // ReadBufLen specifies the size of the buffer used to read packets
36         // off the wire such that multiple packets are buffered with each read syscall.
37         // Note that an individual packet larger than the buffer size is necessarily truncated.
38         // A larger buffer should increase performance because fewer read syscalls would be made.
39         // If zero is used, the system's default buffer length will be used which depending on the
40         // system may default to 4096 bytes which is not big enough to accomodate some link layers
41         // such as WLAN (802.11).
42         // ReadBufLen defaults to 32767... however typical BSD manual pages for BPF indicate that
43         // if the requested buffer size cannot be accommodated, the closest allowable size will be
44         // set and returned... hence our GetReadBufLen method.
45         ReadBufLen int
46         // Timeout is the length of time to wait before timing out on a read request.
47         // Timeout defaults to nil which means no timeout is used.
48         Timeout *syscall.Timeval
49         // Promisc is set to true for promiscuous mode ethernet sniffing.
50         // Promisc defaults to true.
51         Promisc bool
52         // Immediate is set to true to make our read requests return as soon as a packet becomes available.
53         // Otherwise, a read will block until either the kernel buffer becomes full or a timeout occurs.
54         // The default is true.
55         Immediate bool
56         // PreserveLinkAddr is set to false if the link level source address should be filled in automatically
57         // by the interface output routine. Set to true if the link level source address will be written,
58         // as provided, to the wire.
59         // The default is true.
60         PreserveLinkAddr bool
61 }
62
63 var defaultOptions = Options{
64         BPFDeviceName:    "",
65         ReadBufLen:       32767,
66         Timeout:          nil,
67         Promisc:          true,
68         Immediate:        true,
69         PreserveLinkAddr: true,
70 }
71
72 // BPFSniffer is a struct used to track state of a BSD BPF ethernet sniffer
73 // such that gopacket's PacketDataSource interface is implemented.
74 type BPFSniffer struct {
75         options           *Options
76         sniffDeviceName   string
77         fd                int
78         readBuffer        []byte
79         lastReadLen       int
80         readBytesConsumed int
81 }
82
83 // NewBPFSniffer is used to create BSD-only BPF ethernet sniffer
84 // iface is the network interface device name that you wish to sniff
85 // options can set to nil in order to utilize default values for everything.
86 // Each field of Options also have a default setting if left unspecified by
87 // the user's custome Options struct.
88 func NewBPFSniffer(iface string, options *Options) (*BPFSniffer, error) {
89         var err error
90         enable := 1
91         sniffer := BPFSniffer{
92                 sniffDeviceName: iface,
93         }
94         if options == nil {
95                 sniffer.options = &defaultOptions
96         } else {
97                 sniffer.options = options
98         }
99
100         if sniffer.options.BPFDeviceName == "" {
101                 sniffer.pickBpfDevice()
102         }
103
104         // setup our read buffer
105         if sniffer.options.ReadBufLen == 0 {
106                 sniffer.options.ReadBufLen, err = syscall.BpfBuflen(sniffer.fd)
107                 if err != nil {
108                         return nil, err
109                 }
110         } else {
111                 sniffer.options.ReadBufLen, err = syscall.SetBpfBuflen(sniffer.fd, sniffer.options.ReadBufLen)
112                 if err != nil {
113                         return nil, err
114                 }
115         }
116         sniffer.readBuffer = make([]byte, sniffer.options.ReadBufLen)
117
118         err = syscall.SetBpfInterface(sniffer.fd, sniffer.sniffDeviceName)
119         if err != nil {
120                 return nil, err
121         }
122
123         if sniffer.options.Immediate {
124                 // turn immediate mode on. This makes the snffer non-blocking.
125                 err = syscall.SetBpfImmediate(sniffer.fd, enable)
126                 if err != nil {
127                         return nil, err
128                 }
129         }
130
131         // the above call to syscall.SetBpfImmediate needs to be made
132         // before setting a timer otherwise the reads will block for the
133         // entire timer duration even if there are packets to return.
134         if sniffer.options.Timeout != nil {
135                 err = syscall.SetBpfTimeout(sniffer.fd, sniffer.options.Timeout)
136                 if err != nil {
137                         return nil, err
138                 }
139         }
140
141         if sniffer.options.PreserveLinkAddr {
142                 // preserves the link level source address...
143                 // higher level protocol analyzers will not need this
144                 err = syscall.SetBpfHeadercmpl(sniffer.fd, enable)
145                 if err != nil {
146                         return nil, err
147                 }
148         }
149
150         if sniffer.options.Promisc {
151                 // forces the interface into promiscuous mode
152                 err = syscall.SetBpfPromisc(sniffer.fd, enable)
153                 if err != nil {
154                         return nil, err
155                 }
156         }
157
158         return &sniffer, nil
159 }
160
161 // Close is used to close the file-descriptor of the BPF device file.
162 func (b *BPFSniffer) Close() error {
163         return syscall.Close(b.fd)
164 }
165
166 func (b *BPFSniffer) pickBpfDevice() {
167         var err error
168         b.options.BPFDeviceName = ""
169         for i := 0; i < 99; i++ {
170                 b.options.BPFDeviceName = fmt.Sprintf("/dev/bpf%d", i)
171                 b.fd, err = syscall.Open(b.options.BPFDeviceName, syscall.O_RDWR, 0)
172                 if err == nil {
173                         return
174                 }
175         }
176         panic("failed to acquire a BPF device for read-write access")
177 }
178
179 func (b *BPFSniffer) ReadPacketData() ([]byte, gopacket.CaptureInfo, error) {
180         var err error
181         if b.readBytesConsumed >= b.lastReadLen {
182                 b.readBytesConsumed = 0
183                 b.readBuffer = make([]byte, b.options.ReadBufLen)
184                 b.lastReadLen, err = syscall.Read(b.fd, b.readBuffer)
185                 if err != nil {
186                         b.lastReadLen = 0
187                         return nil, gopacket.CaptureInfo{}, err
188                 }
189         }
190         hdr := (*unix.BpfHdr)(unsafe.Pointer(&b.readBuffer[b.readBytesConsumed]))
191         frameStart := b.readBytesConsumed + int(hdr.Hdrlen)
192         b.readBytesConsumed += bpfWordAlign(int(hdr.Hdrlen) + int(hdr.Caplen))
193
194         if frameStart+int(hdr.Caplen) > len(b.readBuffer) {
195                 captureInfo := gopacket.CaptureInfo{
196                         Timestamp:     time.Unix(int64(hdr.Tstamp.Sec), int64(hdr.Tstamp.Usec)*1000),
197                         CaptureLength: 0,
198                         Length:        0,
199                 }
200                 return nil, captureInfo, errors.New("BPF captured frame received with corrupted BpfHdr struct.")
201         }
202
203         rawFrame := b.readBuffer[frameStart : frameStart+int(hdr.Caplen)]
204         captureInfo := gopacket.CaptureInfo{
205                 Timestamp:     time.Unix(int64(hdr.Tstamp.Sec), int64(hdr.Tstamp.Usec)*1000),
206                 CaptureLength: len(rawFrame),
207                 Length:        len(rawFrame),
208         }
209         return rawFrame, captureInfo, nil
210 }
211
212 // GetReadBufLen returns the BPF read buffer length
213 func (b *BPFSniffer) GetReadBufLen() int {
214         return b.options.ReadBufLen
215 }