forked from j-keck/arping
-
Notifications
You must be signed in to change notification settings - Fork 0
/
arping_bsd.go
109 lines (91 loc) · 2.55 KB
/
arping_bsd.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
// +build darwin freebsd openbsd
package arping
import (
"errors"
"fmt"
"net"
"os"
"runtime"
"syscall"
"time"
)
type BsdSocket struct {
bpf *os.File
bpfFd int
buflen int
}
var bpfArpFilter = []syscall.BpfInsn{
// make sure this is an arp packet
*syscall.BpfStmt(syscall.BPF_LD+syscall.BPF_H+syscall.BPF_ABS, 12),
*syscall.BpfJump(syscall.BPF_JMP+syscall.BPF_JEQ+syscall.BPF_K, 0x0806, 0, 1),
// if we passed all the tests, ask for the whole packet.
*syscall.BpfStmt(syscall.BPF_RET+syscall.BPF_K, -1),
// otherwise, drop it.
*syscall.BpfStmt(syscall.BPF_RET+syscall.BPF_K, 0),
}
func initialize(iface net.Interface) (s *BsdSocket, err error) {
s = &BsdSocket{}
verboseLog.Println("search available /dev/bpfX")
for i := 0; i <= 10; i++ {
bpfPath := fmt.Sprintf("/dev/bpf%d", i)
s.bpf, err = os.OpenFile(bpfPath, os.O_RDWR, 0666)
if err != nil {
verboseLog.Printf(" open failed: %s - %s\n", bpfPath, err.Error())
} else {
verboseLog.Printf(" open success: %s\n", bpfPath)
break
}
}
s.bpfFd = int(s.bpf.Fd())
if s.bpfFd == -1 {
return s, errors.New("unable to open /dev/bpfX")
}
if err := syscall.SetBpfInterface(s.bpfFd, iface.Name); err != nil {
return s, err
}
if err := syscall.SetBpfImmediate(s.bpfFd, 1); err != nil {
return s, err
}
s.buflen, err = syscall.BpfBuflen(s.bpfFd)
if err != nil {
return s, err
}
if err := syscall.SetBpf(s.bpfFd, bpfArpFilter); err != nil {
return s, err
}
if err := syscall.FlushBpf(s.bpfFd); err != nil {
return s, err
}
return s, nil
}
func (s *BsdSocket) send(request arpDatagram) (time.Time, error) {
_, err := syscall.Write(s.bpfFd, request.MarshalWithEthernetHeader())
return time.Now(), err
}
func (s *BsdSocket) receive() (arpDatagram, time.Time, error) {
buffer := make([]byte, s.buflen)
n, err := syscall.Read(s.bpfFd, buffer)
if err != nil {
return arpDatagram{}, time.Now(), err
}
//
// FreeBSD uses a different bpf header (bh_tstamp differ in it's size)
// https://www.freebsd.org/cgi/man.cgi?bpf(4)#BPF_HEADER
//
var bpfHdrLength int
if runtime.GOOS == "freebsd" {
bpfHdrLength = 26
} else {
bpfHdrLength = 18
}
// skip bpf header + 14 bytes ethernet header
var hdrLength = bpfHdrLength + 14
if n <= hdrLength || len(buffer) <= hdrLength {
// amount of bytes read by socket is less than an ethernet header. clearly not what we look for
return arpDatagram{}, time.Now(), fmt.Errorf("buffer with invalid length")
}
return parseArpDatagram(buffer[hdrLength:n]), time.Now(), nil
}
func (s *BsdSocket) deinitialize() error {
return s.bpf.Close()
}