raw_bsd.go 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. // +build darwin dragonfly freebsd netbsd openbsd
  2. package raw
  3. import (
  4. "errors"
  5. "fmt"
  6. "net"
  7. "os"
  8. "runtime"
  9. "sync"
  10. "syscall"
  11. "time"
  12. "unsafe"
  13. "golang.org/x/net/bpf"
  14. "golang.org/x/sys/unix"
  15. )
  16. const (
  17. // osFreeBSD is the GOOS name for FreeBSD.
  18. osFreeBSD = "freebsd"
  19. )
  20. // bpfLen returns the length of the BPF header prepended to each incoming ethernet
  21. // frame. FreeBSD uses a slightly modified header from other BSD variants.
  22. func bpfLen() int {
  23. // Majority of BSD family systems use the bpf_hdr struct, but FreeBSD
  24. // has replaced this with bpf_xhdr, which is longer.
  25. const (
  26. bpfHeaderLen = 18
  27. bpfXHeaderLen = 26
  28. )
  29. if runtime.GOOS == osFreeBSD {
  30. return bpfXHeaderLen
  31. }
  32. return bpfHeaderLen
  33. }
  34. var (
  35. // Must implement net.PacketConn at compile-time.
  36. _ net.PacketConn = &packetConn{}
  37. )
  38. // packetConn is the Linux-specific implementation of net.PacketConn for this
  39. // package.
  40. type packetConn struct {
  41. proto uint16
  42. ifi *net.Interface
  43. f *os.File
  44. fd int
  45. buflen int
  46. // Timeouts set via Set{Read,}Deadline, guarded by mutex
  47. timeoutMu sync.RWMutex
  48. rtimeout time.Time
  49. }
  50. // listenPacket creates a net.PacketConn which can be used to send and receive
  51. // data at the device driver level.
  52. func listenPacket(ifi *net.Interface, proto uint16, cfg Config) (*packetConn, error) {
  53. // TODO(mdlayher): consider porting NoTimeouts option to BSD if it pans out.
  54. var f *os.File
  55. var err error
  56. // Try to find an available BPF device
  57. for i := 0; i <= 10; i++ {
  58. bpfPath := fmt.Sprintf("/dev/bpf%d", i)
  59. f, err = os.OpenFile(bpfPath, os.O_RDWR, 0666)
  60. if err == nil {
  61. // Found a usable device
  62. break
  63. }
  64. // Device is busy, try the next one
  65. if perr, ok := err.(*os.PathError); ok {
  66. if perr.Err.(syscall.Errno) == syscall.EBUSY {
  67. continue
  68. }
  69. }
  70. return nil, err
  71. }
  72. if f == nil {
  73. return nil, errors.New("unable to open BPF device")
  74. }
  75. fd := int(f.Fd())
  76. if fd == -1 {
  77. return nil, errors.New("unable to open BPF device")
  78. }
  79. // Configure BPF device to send and receive data
  80. buflen, err := configureBPF(fd, ifi, proto, cfg.BPFDirection)
  81. if err != nil {
  82. return nil, err
  83. }
  84. return &packetConn{
  85. proto: proto,
  86. ifi: ifi,
  87. f: f,
  88. fd: fd,
  89. buflen: buflen,
  90. }, nil
  91. }
  92. const (
  93. // Maximum read timeout per syscall.
  94. // It is required because read/recvfrom won't be interrupted on closing of the file descriptor.
  95. readTimeout = 200 * time.Millisecond
  96. )
  97. // ReadFrom implements the net.PacketConn.ReadFrom method.
  98. func (p *packetConn) ReadFrom(b []byte) (int, net.Addr, error) {
  99. p.timeoutMu.Lock()
  100. deadline := p.rtimeout
  101. p.timeoutMu.Unlock()
  102. buf := make([]byte, p.buflen)
  103. var n int
  104. for {
  105. var timeout time.Duration
  106. if deadline.IsZero() {
  107. timeout = readTimeout
  108. } else {
  109. timeout = time.Until(deadline)
  110. if timeout > readTimeout {
  111. timeout = readTimeout
  112. }
  113. }
  114. tv := unix.NsecToTimeval(timeout.Nanoseconds())
  115. if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(p.fd), syscall.BIOCSRTIMEOUT, uintptr(unsafe.Pointer(&tv))); err != 0 {
  116. return 0, nil, syscall.Errno(err)
  117. }
  118. // Attempt to receive on socket
  119. // The read sycall will NOT be interrupted by closing of the socket
  120. var err error
  121. n, err = syscall.Read(p.fd, buf)
  122. if err != nil {
  123. return n, nil, err
  124. }
  125. if n > 0 {
  126. break
  127. }
  128. }
  129. // TODO(mdlayher): consider parsing BPF header if it proves useful.
  130. // BPF header length depends on the platform this code is running on
  131. bpfl := bpfLen()
  132. // Retrieve source MAC address of ethernet header
  133. mac := make(net.HardwareAddr, 6)
  134. copy(mac, buf[bpfl+6:bpfl+12])
  135. // Skip past BPF header to retrieve ethernet frame
  136. out := copy(b, buf[bpfl:bpfl+n])
  137. return out, &Addr{
  138. HardwareAddr: mac,
  139. }, nil
  140. }
  141. // WriteTo implements the net.PacketConn.WriteTo method.
  142. func (p *packetConn) WriteTo(b []byte, _ net.Addr) (int, error) {
  143. return syscall.Write(p.fd, b)
  144. }
  145. // Close closes the connection.
  146. func (p *packetConn) Close() error {
  147. return p.f.Close()
  148. }
  149. // LocalAddr returns the local network address.
  150. func (p *packetConn) LocalAddr() net.Addr {
  151. return &Addr{
  152. HardwareAddr: p.ifi.HardwareAddr,
  153. }
  154. }
  155. // SetDeadline implements the net.PacketConn.SetDeadline method.
  156. func (p *packetConn) SetDeadline(t time.Time) error {
  157. return p.SetReadDeadline(t)
  158. }
  159. // SetReadDeadline implements the net.PacketConn.SetReadDeadline method.
  160. func (p *packetConn) SetReadDeadline(t time.Time) error {
  161. p.timeoutMu.Lock()
  162. p.rtimeout = t
  163. p.timeoutMu.Unlock()
  164. return nil
  165. }
  166. // SetWriteDeadline implements the net.PacketConn.SetWriteDeadline method.
  167. func (p *packetConn) SetWriteDeadline(t time.Time) error {
  168. return ErrNotImplemented
  169. }
  170. // SetBPF attaches an assembled BPF program to a raw net.PacketConn.
  171. func (p *packetConn) SetBPF(filter []bpf.RawInstruction) error {
  172. // Base filter filters traffic based on EtherType
  173. base, err := bpf.Assemble(baseFilter(p.proto))
  174. if err != nil {
  175. return err
  176. }
  177. // Append user filter to base filter, translate to raw format,
  178. // and apply to BPF device
  179. return syscall.SetBpf(p.fd, assembleBpfInsn(append(base, filter...)))
  180. }
  181. // SetPromiscuous enables or disables promiscuous mode on the interface, allowing it
  182. // to receive traffic that is not addressed to the interface.
  183. func (p *packetConn) SetPromiscuous(b bool) error {
  184. m := 1
  185. if !b {
  186. m = 0
  187. }
  188. return syscall.SetBpfPromisc(p.fd, m)
  189. }
  190. // Stats retrieves statistics from the Conn.
  191. func (p *packetConn) Stats() (*Stats, error) {
  192. return nil, ErrNotImplemented
  193. }
  194. // configureBPF configures a BPF device with the specified file descriptor to
  195. // use the specified network and interface and protocol.
  196. func configureBPF(fd int, ifi *net.Interface, proto uint16, direction int) (int, error) {
  197. // Use specified interface with BPF device
  198. if err := syscall.SetBpfInterface(fd, ifi.Name); err != nil {
  199. return 0, err
  200. }
  201. // Inform BPF to send us its data immediately
  202. if err := syscall.SetBpfImmediate(fd, 1); err != nil {
  203. return 0, err
  204. }
  205. // Check buffer size of BPF device
  206. buflen, err := syscall.BpfBuflen(fd)
  207. if err != nil {
  208. return 0, err
  209. }
  210. // Do not automatically complete source address in ethernet headers
  211. if err := syscall.SetBpfHeadercmpl(fd, 1); err != nil {
  212. return 0, err
  213. }
  214. // Specify incoming only or bidirectional traffic using BPF device
  215. if err := setBPFDirection(fd, direction); err != nil {
  216. return 0, err
  217. }
  218. // Build and apply base BPF filter which checks for correct EtherType
  219. // on incoming packets
  220. prog, err := bpf.Assemble(baseInterfaceFilter(proto, ifi.MTU))
  221. if err != nil {
  222. return 0, err
  223. }
  224. if err := syscall.SetBpf(fd, assembleBpfInsn(prog)); err != nil {
  225. return 0, err
  226. }
  227. // Flush any packets currently in the BPF device's buffer
  228. if err := syscall.FlushBpf(fd); err != nil {
  229. return 0, err
  230. }
  231. return buflen, nil
  232. }
  233. // assembleBpfInsn assembles a slice of bpf.RawInstructions to the format required by
  234. // package syscall.
  235. func assembleBpfInsn(filter []bpf.RawInstruction) []syscall.BpfInsn {
  236. // Copy each bpf.RawInstruction into syscall.BpfInsn. If needed,
  237. // the structures have the same memory layout and could probably be
  238. // unsafely cast to each other for speed.
  239. insns := make([]syscall.BpfInsn, 0, len(filter))
  240. for _, ins := range filter {
  241. insns = append(insns, syscall.BpfInsn{
  242. Code: ins.Op,
  243. Jt: ins.Jt,
  244. Jf: ins.Jf,
  245. K: ins.K,
  246. })
  247. }
  248. return insns
  249. }
  250. // baseInterfaceFilter creates a base BPF filter which filters traffic based
  251. // on its EtherType and returns up to "mtu" bytes of data for processing.
  252. func baseInterfaceFilter(proto uint16, mtu int) []bpf.Instruction {
  253. return append(
  254. // Filter traffic based on EtherType
  255. baseFilter(proto),
  256. // Accept the packet bytes up to the interface's MTU
  257. bpf.RetConstant{
  258. Val: uint32(mtu),
  259. },
  260. )
  261. }
  262. // baseFilter creates a base BPF filter which filters traffic based on its
  263. // EtherType. baseFilter can be prepended to other filters to handle common
  264. // filtering tasks.
  265. func baseFilter(proto uint16) []bpf.Instruction {
  266. // Offset | Length | Comment
  267. // -------------------------
  268. // 00 | 06 | Ethernet destination MAC address
  269. // 06 | 06 | Ethernet source MAC address
  270. // 12 | 02 | Ethernet EtherType
  271. const (
  272. etherTypeOffset = 12
  273. etherTypeLength = 2
  274. )
  275. return []bpf.Instruction{
  276. // Load EtherType value from Ethernet header
  277. bpf.LoadAbsolute{
  278. Off: etherTypeOffset,
  279. Size: etherTypeLength,
  280. },
  281. // If EtherType is equal to the protocol we are using, jump to instructions
  282. // added outside of this function.
  283. bpf.JumpIf{
  284. Cond: bpf.JumpEqual,
  285. Val: uint32(proto),
  286. SkipTrue: 1,
  287. },
  288. // EtherType does not match our protocol
  289. bpf.RetConstant{
  290. Val: 0,
  291. },
  292. }
  293. }