fix: fixed linux build issue due to mdlayher/raw (fixes #468)

This commit is contained in:
evilsocket 2019-03-03 12:26:18 +01:00
parent 5cc9db802c
commit e98ac9938f
No known key found for this signature in database
GPG key ID: 1564D7F30393A456
6 changed files with 119 additions and 135 deletions

4
Gopkg.lock generated
View file

@ -222,11 +222,11 @@
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:7b8a07084a265123ced4a06172044402febe04f9f7d39d7269901d556cbd769e" digest = "1:34fe44dd2bbe5723068e0a7a266847965a88297d383fe611e0358e556d82de09"
name = "github.com/mdlayher/raw" name = "github.com/mdlayher/raw"
packages = ["."] packages = ["."]
pruneopts = "UT" pruneopts = "UT"
revision = "81144197cf93eb7382abd04a6a15154cfce2e65c" revision = "480b93709cce56651807d3fdeb260a5a7c4e2d5f"
[[projects]] [[projects]]
branch = "master" branch = "master"

View file

@ -10,6 +10,12 @@ import (
"golang.org/x/net/bpf" "golang.org/x/net/bpf"
) )
const (
// Maximum read timeout per syscall.
// It is required because read/recvfrom won't be interrupted on closing of the file descriptor.
readTimeout = 200 * time.Millisecond
)
var ( var (
// ErrNotImplemented is returned when certain functionality is not yet // ErrNotImplemented is returned when certain functionality is not yet
// implemented for the host operating system. // implemented for the host operating system.
@ -143,6 +149,17 @@ type Config struct {
// Has no effect on other operating systems. // Has no effect on other operating systems.
LinuxSockDGRAM bool LinuxSockDGRAM bool
// Experimental: Linux only (for now, but can be ported to BSD):
// disables repeated socket reads due to internal timeouts, at the expense
// of losing the ability to cancel a ReadFrom operation by calling the Close
// method of the net.PacketConn.
//
// Not recommended for programs which may need to open and close multiple
// sockets during program runs. This may save some CPU time by avoiding a
// busy loop for programs which do not need timeouts, or programs which keep
// a single socket open for the entire duration of the program.
NoTimeouts bool
// Linux only: do not accumulate packet socket statistic counters. Packet // Linux only: do not accumulate packet socket statistic counters. Packet
// socket statistics are reset on each call to retrieve them via getsockopt, // socket statistics are reset on each call to retrieve them via getsockopt,
// but this package's default behavior is to continue accumulating the // but this package's default behavior is to continue accumulating the
@ -150,3 +167,16 @@ type Config struct {
// resetting statistics on each call to Stats, set this value to true. // resetting statistics on each call to Stats, set this value to true.
NoCumulativeStats bool NoCumulativeStats bool
} }
// Copyright (c) 2012 The Go Authors. All rights reserved.
// Source code in this file is based on src/net/interface_linux.go,
// from the Go standard library. The Go license can be found here:
// https://golang.org/LICENSE.
// Taken from:
// https://github.com/golang/go/blob/master/src/net/net.go#L417-L421.
type timeoutError struct{}
func (e *timeoutError) Error() string { return "i/o timeout" }
func (e *timeoutError) Timeout() bool { return true }
func (e *timeoutError) Temporary() bool { return true }

View file

@ -5,8 +5,8 @@ package raw
import ( import (
"net" "net"
"os" "os"
"sync"
"sync/atomic" "sync/atomic"
"syscall"
"time" "time"
"unsafe" "unsafe"
@ -26,11 +26,18 @@ type packetConn struct {
s socket s socket
pbe uint16 pbe uint16
// Should timeouts be set at all?
noTimeouts bool
// Should stats be accumulated instead of reset on each call? // Should stats be accumulated instead of reset on each call?
noCumulativeStats bool noCumulativeStats bool
// Internal storage for cumulative stats. // Internal storage for cumulative stats.
stats Stats stats Stats
// Timeouts set via Set{Read,}Deadline, guarded by mutex.
timeoutMu sync.RWMutex
rtimeout time.Time
} }
// socket is an interface which enables swapping out socket syscalls for // socket is an interface which enables swapping out socket syscalls for
@ -38,13 +45,12 @@ type packetConn struct {
type socket interface { type socket interface {
Bind(unix.Sockaddr) error Bind(unix.Sockaddr) error
Close() error Close() error
FD() int
GetSockopt(level, name int, v unsafe.Pointer, l uintptr) error GetSockopt(level, name int, v unsafe.Pointer, l uintptr) error
Recvfrom([]byte, int) (int, unix.Sockaddr, error) Recvfrom([]byte, int) (int, unix.Sockaddr, error)
Sendto([]byte, int, unix.Sockaddr) error Sendto([]byte, int, unix.Sockaddr) error
SetSockopt(level, name int, v unsafe.Pointer, l uint32) error SetSockopt(level, name int, v unsafe.Pointer, l uint32) error
SetDeadline(time.Time) error SetTimeout(time.Duration) error
SetReadDeadline(time.Time) error
SetWriteDeadline(time.Time) error
} }
// htons converts a short (uint16) from host-to-network byte order. // htons converts a short (uint16) from host-to-network byte order.
@ -60,11 +66,9 @@ func listenPacket(ifi *net.Interface, proto uint16, cfg Config) (*packetConn, er
// Convert proto to big endian. // Convert proto to big endian.
pbe := htons(proto) pbe := htons(proto)
filename := "eth-packet-socket"
// Enabling overriding the socket type via config. // Enabling overriding the socket type via config.
typ := unix.SOCK_RAW typ := unix.SOCK_RAW
if cfg.LinuxSockDGRAM { if cfg.LinuxSockDGRAM {
filename = "packet-socket"
typ = unix.SOCK_DGRAM typ = unix.SOCK_DGRAM
} }
@ -74,28 +78,13 @@ func listenPacket(ifi *net.Interface, proto uint16, cfg Config) (*packetConn, er
return nil, err return nil, err
} }
if err := unix.SetNonblock(sock, true); err != nil {
return nil, err
}
// When using Go 1.12+, the SetNonblock call we just did puts the file
// descriptor into non-blocking mode. In that case, os.NewFile
// registers the file descriptor with the runtime poller, which is then
// used for all subsequent operations.
//
// See also: https://golang.org/pkg/os/#NewFile
f := os.NewFile(uintptr(sock), filename)
sc, err := f.SyscallConn()
if err != nil {
return nil, err
}
// Wrap raw socket in socket interface. // Wrap raw socket in socket interface.
pc, err := newPacketConn(ifi, &sysSocket{f: f, rc: sc}, pbe) pc, err := newPacketConn(ifi, &sysSocket{fd: sock}, pbe)
if err != nil { if err != nil {
return nil, err return nil, err
} }
pc.noTimeouts = cfg.NoTimeouts
pc.noCumulativeStats = cfg.NoCumulativeStats pc.noCumulativeStats = cfg.NoCumulativeStats
return pc, nil return pc, nil
} }
@ -126,10 +115,51 @@ func newPacketConn(ifi *net.Interface, s socket, pbe uint16) (*packetConn, error
// ReadFrom implements the net.PacketConn.ReadFrom method. // ReadFrom implements the net.PacketConn.ReadFrom method.
func (p *packetConn) ReadFrom(b []byte) (int, net.Addr, error) { func (p *packetConn) ReadFrom(b []byte) (int, net.Addr, error) {
// Attempt to receive on socket p.timeoutMu.Lock()
n, addr, err := p.s.Recvfrom(b, 0) deadline := p.rtimeout
if err != nil { p.timeoutMu.Unlock()
return n, nil, err
var (
// Information returned by unix.Recvfrom.
n int
addr unix.Sockaddr
err error
// Timeout for a single loop iteration.
timeout = readTimeout
)
for {
if !deadline.IsZero() {
timeout = time.Until(deadline)
if timeout > readTimeout {
timeout = readTimeout
}
}
// Set a timeout for this iteration if configured to do so.
if !p.noTimeouts {
if err := p.s.SetTimeout(timeout); err != nil {
return 0, nil, err
}
}
// Attempt to receive on socket
// The recvfrom sycall will NOT be interrupted by closing of the socket
n, addr, err = p.s.Recvfrom(b, 0)
switch err {
case nil:
// Got data, break this loop shortly.
case unix.EAGAIN:
// Hit a timeout, keep looping.
continue
default:
// Return on any other error.
return n, nil, err
}
// Got data, exit the loop.
break
} }
// Retrieve hardware address and other information from addr. // Retrieve hardware address and other information from addr.
@ -193,17 +223,20 @@ func (p *packetConn) LocalAddr() net.Addr {
// SetDeadline implements the net.PacketConn.SetDeadline method. // SetDeadline implements the net.PacketConn.SetDeadline method.
func (p *packetConn) SetDeadline(t time.Time) error { func (p *packetConn) SetDeadline(t time.Time) error {
return p.s.SetDeadline(t) return p.SetReadDeadline(t)
} }
// SetReadDeadline implements the net.PacketConn.SetReadDeadline method. // SetReadDeadline implements the net.PacketConn.SetReadDeadline method.
func (p *packetConn) SetReadDeadline(t time.Time) error { func (p *packetConn) SetReadDeadline(t time.Time) error {
return p.s.SetReadDeadline(t) p.timeoutMu.Lock()
p.rtimeout = t
p.timeoutMu.Unlock()
return nil
} }
// SetWriteDeadline implements the net.PacketConn.SetWriteDeadline method. // SetWriteDeadline implements the net.PacketConn.SetWriteDeadline method.
func (p *packetConn) SetWriteDeadline(t time.Time) error { func (p *packetConn) SetWriteDeadline(t time.Time) error {
return p.s.SetWriteDeadline(t) return nil
} }
// SetBPF attaches an assembled BPF program to a raw net.PacketConn. // SetBPF attaches an assembled BPF program to a raw net.PacketConn.
@ -222,6 +255,7 @@ func (p *packetConn) SetBPF(filter []bpf.RawInstruction) error {
if err != nil { if err != nil {
return os.NewSyscallError("setsockopt", err) return os.NewSyscallError("setsockopt", err)
} }
return nil return nil
} }
@ -276,91 +310,38 @@ func (p *packetConn) handleStats(s unix.TpacketStats) *Stats {
// sysSocket is the default socket implementation. It makes use of // sysSocket is the default socket implementation. It makes use of
// Linux-specific system calls to handle raw socket functionality. // Linux-specific system calls to handle raw socket functionality.
type sysSocket struct { type sysSocket struct {
f *os.File fd int
rc syscall.RawConn
}
func (s *sysSocket) SetDeadline(t time.Time) error {
return s.f.SetDeadline(t)
}
func (s *sysSocket) SetReadDeadline(t time.Time) error {
return s.f.SetReadDeadline(t)
}
func (s *sysSocket) SetWriteDeadline(t time.Time) error {
return s.f.SetWriteDeadline(t)
}
func (s *sysSocket) Bind(sa unix.Sockaddr) error {
var err error
cerr := s.rc.Control(func(fd uintptr) {
err = unix.Bind(int(fd), sa)
})
if err != nil {
return err
}
return cerr
}
func (s *sysSocket) Close() error {
return s.f.Close()
} }
// Method implementations simply invoke the syscall of the same name, but pass
// the file descriptor stored in the sysSocket as the socket to use.
func (s *sysSocket) Bind(sa unix.Sockaddr) error { return unix.Bind(s.fd, sa) }
func (s *sysSocket) Close() error { return unix.Close(s.fd) }
func (s *sysSocket) FD() int { return s.fd }
func (s *sysSocket) GetSockopt(level, name int, v unsafe.Pointer, l uintptr) error { func (s *sysSocket) GetSockopt(level, name int, v unsafe.Pointer, l uintptr) error {
var err error _, _, err := unix.Syscall6(unix.SYS_GETSOCKOPT, uintptr(s.fd), uintptr(level), uintptr(name), uintptr(v), uintptr(unsafe.Pointer(&l)), 0)
cerr := s.rc.Control(func(fd uintptr) { if err != 0 {
_, _, errno := unix.Syscall6(unix.SYS_GETSOCKOPT, fd, uintptr(level), uintptr(name), uintptr(v), uintptr(unsafe.Pointer(&l)), 0) return unix.Errno(err)
if errno != 0 {
err = os.NewSyscallError("getsockopt", errno)
}
})
if err != nil {
return err
} }
return cerr return nil
} }
func (s *sysSocket) Recvfrom(p []byte, flags int) (int, unix.Sockaddr, error) {
func (s *sysSocket) Recvfrom(p []byte, flags int) (n int, addr unix.Sockaddr, err error) { return unix.Recvfrom(s.fd, p, flags)
cerr := s.rc.Read(func(fd uintptr) bool {
n, addr, err = unix.Recvfrom(int(fd), p, flags)
// When the socket is in non-blocking mode, we might see EAGAIN
// and end up here. In that case, return false to let the
// poller wait for readiness. See the source code for
// internal/poll.FD.RawRead for more details.
//
// If the socket is in blocking mode, EAGAIN should never occur.
return err != unix.EAGAIN
})
if err != nil {
return n, addr, err
}
return n, addr, cerr
} }
func (s *sysSocket) Sendto(p []byte, flags int, to unix.Sockaddr) error { func (s *sysSocket) Sendto(p []byte, flags int, to unix.Sockaddr) error {
var err error return unix.Sendto(s.fd, p, flags, to)
cerr := s.rc.Write(func(fd uintptr) bool {
err = unix.Sendto(int(fd), p, flags, to)
// See comment in Recvfrom.
return err != unix.EAGAIN
})
if err != nil {
return err
}
return cerr
} }
func (s *sysSocket) SetSockopt(level, name int, v unsafe.Pointer, l uint32) error { func (s *sysSocket) SetSockopt(level, name int, v unsafe.Pointer, l uint32) error {
var err error _, _, err := unix.Syscall6(unix.SYS_SETSOCKOPT, uintptr(s.fd), uintptr(level), uintptr(name), uintptr(v), uintptr(l), 0)
cerr := s.rc.Control(func(fd uintptr) { if err != 0 {
_, _, errno := unix.Syscall6(unix.SYS_SETSOCKOPT, fd, uintptr(level), uintptr(name), uintptr(v), uintptr(l), 0) return unix.Errno(err)
if errno != 0 { }
err = os.NewSyscallError("setsockopt", errno) return nil
} }
}) func (s *sysSocket) SetTimeout(timeout time.Duration) error {
tv, err := newTimeval(timeout)
if err != nil { if err != nil {
return err return err
} }
return cerr return unix.SetsockoptTimeval(s.fd, unix.SOL_SOCKET, unix.SO_RCVTIMEO, tv)
} }

View file

@ -1,26 +0,0 @@
// +build darwin dragonfly freebsd netbsd openbsd
package raw
import (
"time"
)
const (
// Maximum read timeout per syscall.
// It is required because read/recvfrom won't be interrupted on closing of the file descriptor.
readTimeout = 200 * time.Millisecond
)
// Copyright (c) 2012 The Go Authors. All rights reserved.
// Source code in this file is based on src/net/interface_linux.go,
// from the Go standard library. The Go license can be found here:
// https://golang.org/LICENSE.
// Taken from:
// https://github.com/golang/go/blob/master/src/net/net.go#L417-L421.
type timeoutError struct{}
func (e *timeoutError) Error() string { return "i/o timeout" }
func (e *timeoutError) Timeout() bool { return true }
func (e *timeoutError) Temporary() bool { return true }

View file

@ -1,4 +1,4 @@
// +build !darwin,!arm,!windows,!mipsle,!mips,!386,!linux // +build !darwin,!arm,!windows,!mipsle,!mips,!386
package raw package raw

View file

@ -1,5 +1,4 @@
// +build arm mipsle mips 386 // +build arm mipsle mips 386
// +build !linux
package raw package raw