File: arping_bsd.go

package info (click to toggle)
golang-github-j-keck-arping 0.0~git20160618.2cf9dc6-1
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 92 kB
  • sloc: makefile: 3
file content (101 lines) | stat: -rw-r--r-- 2,237 bytes parent folder | download | duplicates (3)
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
// +build darwin freebsd openbsd

package arping

import (
	"errors"
	"fmt"
	"net"
	"os"
	"runtime"
	"syscall"
	"time"
)

var bpf *os.File
var bpfFd int
var 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) (err error) {
	verboseLog.Println("search available /dev/bpfX")
	for i := 0; i <= 10; i++ {
		bpfPath := fmt.Sprintf("/dev/bpf%d", i)
		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
		}
	}
	bpfFd = int(bpf.Fd())
	if bpfFd == -1 {
		return errors.New("unable to open /dev/bpfX")
	}

	if err := syscall.SetBpfInterface(bpfFd, iface.Name); err != nil {
		return err
	}

	if err := syscall.SetBpfImmediate(bpfFd, 1); err != nil {
		return err
	}

	buflen, err = syscall.BpfBuflen(bpfFd)
	if err != nil {
		return err
	}

	if err := syscall.SetBpf(bpfFd, bpfArpFilter); err != nil {
		return err
	}

	if err := syscall.FlushBpf(bpfFd); err != nil {
		return err
	}

	return nil
}

func send(request arpDatagram) (time.Time, error) {
	_, err := syscall.Write(bpfFd, request.MarshalWithEthernetHeader())
	return time.Now(), err
}

func receive() (arpDatagram, time.Time, error) {
	buffer := make([]byte, buflen)
	n, err := syscall.Read(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

	return parseArpDatagram(buffer[hdrLength:n]), time.Now(), nil
}

func deinitialize() error {
	return bpf.Close()
}