-
Notifications
You must be signed in to change notification settings - Fork 4.9k
/
Copy pathafpacket_linux.go
164 lines (140 loc) · 4.71 KB
/
afpacket_linux.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//go:build linux
// +build linux
package sniffer
import (
"fmt"
"syscall"
"time"
"unsafe"
"github.com/google/gopacket"
"github.com/google/gopacket/afpacket"
"github.com/google/gopacket/layers"
"github.com/google/gopacket/pcap"
"golang.org/x/net/bpf"
"github.com/elastic/beats/v7/libbeat/logp"
)
type afpacketHandle struct {
TPacket *afpacket.TPacket
frameSize int
promiscPreviousState bool
promiscPreviousStateDetected bool
device string
}
func newAfpacketHandle(device string, snaplen int, block_size int, num_blocks int,
timeout time.Duration, autoPromiscMode bool) (*afpacketHandle, error,
) {
var err error
var promiscEnabled bool
if autoPromiscMode {
promiscEnabled, err = isPromiscEnabled(device)
if err != nil {
logp.Err("Failed to get promiscuous mode for device '%s': %v", device, err)
}
if !promiscEnabled {
if setPromiscErr := setPromiscMode(device, true); setPromiscErr != nil {
logp.Warn("Failed to set promiscuous mode for device '%s'. Packetbeat may be unable to see any network traffic. Please follow packetbeat FAQ to learn about mitigation: Error: %v", device, err)
}
}
}
h := &afpacketHandle{
promiscPreviousState: promiscEnabled,
frameSize: snaplen,
device: device,
promiscPreviousStateDetected: autoPromiscMode && err == nil,
}
if device == "any" {
h.TPacket, err = afpacket.NewTPacket(
afpacket.OptFrameSize(snaplen),
afpacket.OptBlockSize(block_size),
afpacket.OptNumBlocks(num_blocks),
afpacket.OptPollTimeout(timeout))
} else {
h.TPacket, err = afpacket.NewTPacket(
afpacket.OptInterface(device),
afpacket.OptFrameSize(snaplen),
afpacket.OptBlockSize(block_size),
afpacket.OptNumBlocks(num_blocks),
afpacket.OptPollTimeout(timeout))
}
return h, err
}
func (h *afpacketHandle) ReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error) {
return h.TPacket.ReadPacketData()
}
func (h *afpacketHandle) SetBPFFilter(expr string) error {
prog, err := pcap.CompileBPFFilter(layers.LinkTypeEthernet, h.frameSize, expr)
if err != nil {
return err
}
p := make([]bpf.RawInstruction, len(prog))
for i, ins := range prog {
p[i] = bpf.RawInstruction{
Op: ins.Code,
Jt: ins.Jt,
Jf: ins.Jf,
K: ins.K,
}
}
return h.TPacket.SetBPF(p)
}
func (h *afpacketHandle) LinkType() layers.LinkType {
return layers.LinkTypeEthernet
}
func (h *afpacketHandle) Close() {
h.TPacket.Close()
// previous state detected only if auto mode was on
if h.promiscPreviousStateDetected {
if err := setPromiscMode(h.device, h.promiscPreviousState); err != nil {
logp.Warn("Failed to reset promiscuous mode for device '%s'. Your device might be in promiscuous mode.: %v", h.device, err)
}
}
}
func isPromiscEnabled(device string) (bool, error) {
if device == "any" {
return false, nil
}
s, e := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, 0)
if e != nil {
return false, e
}
defer syscall.Close(s)
var ifreq struct {
name [syscall.IFNAMSIZ]byte
flags uint16
}
copy(ifreq.name[:], []byte(device))
_, _, ep := syscall.Syscall(syscall.SYS_IOCTL, uintptr(s), syscall.SIOCGIFFLAGS, uintptr(unsafe.Pointer(&ifreq)))
if ep != 0 {
return false, fmt.Errorf("ioctl command SIOCGIFFLAGS failed to get device flags for %v: return code %d", device, ep)
}
return ifreq.flags&uint16(syscall.IFF_PROMISC) != 0, nil
}
// setPromiscMode enables promisc mode if configured.
// this makes maintenance for user simpler without any additional manual steps
// issue [700](https://github.com/elastic/beats/issues/700)
func setPromiscMode(device string, enabled bool) error {
if device == "any" {
logp.Warn("Cannot set promiscuous mode to device 'any'")
return nil
}
// SetLsfPromisc is marked as deprecated but used to improve readability (bpf)
// and avoid Cgo (pcap)
// TODO: replace with x/net/bpf or pcap
return syscall.SetLsfPromisc(device, enabled)
}