From e98ac9938f142759e6f24c28e15284ee21a93c88 Mon Sep 17 00:00:00 2001 From: evilsocket Date: Sun, 3 Mar 2019 12:26:18 +0100 Subject: [PATCH] fix: fixed linux build issue due to mdlayher/raw (fixes #468) --- Gopkg.lock | 4 +- vendor/github.com/mdlayher/raw/raw.go | 30 +++ vendor/github.com/mdlayher/raw/raw_linux.go | 191 ++++++++---------- vendor/github.com/mdlayher/raw/timeout_bsd.go | 26 --- vendor/github.com/mdlayher/raw/timeval.go | 2 +- vendor/github.com/mdlayher/raw/timeval32.go | 1 - 6 files changed, 119 insertions(+), 135 deletions(-) delete mode 100644 vendor/github.com/mdlayher/raw/timeout_bsd.go diff --git a/Gopkg.lock b/Gopkg.lock index c293448f..38900389 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -222,11 +222,11 @@ [[projects]] branch = "master" - digest = "1:7b8a07084a265123ced4a06172044402febe04f9f7d39d7269901d556cbd769e" + digest = "1:34fe44dd2bbe5723068e0a7a266847965a88297d383fe611e0358e556d82de09" name = "github.com/mdlayher/raw" packages = ["."] pruneopts = "UT" - revision = "81144197cf93eb7382abd04a6a15154cfce2e65c" + revision = "480b93709cce56651807d3fdeb260a5a7c4e2d5f" [[projects]] branch = "master" diff --git a/vendor/github.com/mdlayher/raw/raw.go b/vendor/github.com/mdlayher/raw/raw.go index f30de37f..ea91020b 100644 --- a/vendor/github.com/mdlayher/raw/raw.go +++ b/vendor/github.com/mdlayher/raw/raw.go @@ -10,6 +10,12 @@ import ( "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 ( // ErrNotImplemented is returned when certain functionality is not yet // implemented for the host operating system. @@ -143,6 +149,17 @@ type Config struct { // Has no effect on other operating systems. 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 // socket statistics are reset on each call to retrieve them via getsockopt, // 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. 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 } diff --git a/vendor/github.com/mdlayher/raw/raw_linux.go b/vendor/github.com/mdlayher/raw/raw_linux.go index 4279ac54..d11b9ce4 100644 --- a/vendor/github.com/mdlayher/raw/raw_linux.go +++ b/vendor/github.com/mdlayher/raw/raw_linux.go @@ -5,8 +5,8 @@ package raw import ( "net" "os" + "sync" "sync/atomic" - "syscall" "time" "unsafe" @@ -26,11 +26,18 @@ type packetConn struct { s socket pbe uint16 + // Should timeouts be set at all? + noTimeouts bool + // Should stats be accumulated instead of reset on each call? noCumulativeStats bool // Internal storage for cumulative 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 @@ -38,13 +45,12 @@ type packetConn struct { type socket interface { Bind(unix.Sockaddr) error Close() error + FD() int GetSockopt(level, name int, v unsafe.Pointer, l uintptr) error Recvfrom([]byte, int) (int, unix.Sockaddr, error) Sendto([]byte, int, unix.Sockaddr) error SetSockopt(level, name int, v unsafe.Pointer, l uint32) error - SetDeadline(time.Time) error - SetReadDeadline(time.Time) error - SetWriteDeadline(time.Time) error + SetTimeout(time.Duration) error } // 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. pbe := htons(proto) - filename := "eth-packet-socket" // Enabling overriding the socket type via config. typ := unix.SOCK_RAW if cfg.LinuxSockDGRAM { - filename = "packet-socket" typ = unix.SOCK_DGRAM } @@ -74,28 +78,13 @@ func listenPacket(ifi *net.Interface, proto uint16, cfg Config) (*packetConn, er 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. - pc, err := newPacketConn(ifi, &sysSocket{f: f, rc: sc}, pbe) + pc, err := newPacketConn(ifi, &sysSocket{fd: sock}, pbe) if err != nil { return nil, err } + pc.noTimeouts = cfg.NoTimeouts pc.noCumulativeStats = cfg.NoCumulativeStats 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. func (p *packetConn) ReadFrom(b []byte) (int, net.Addr, error) { - // Attempt to receive on socket - n, addr, err := p.s.Recvfrom(b, 0) - if err != nil { - return n, nil, err + p.timeoutMu.Lock() + deadline := p.rtimeout + p.timeoutMu.Unlock() + + 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. @@ -193,17 +223,20 @@ func (p *packetConn) LocalAddr() net.Addr { // SetDeadline implements the net.PacketConn.SetDeadline method. func (p *packetConn) SetDeadline(t time.Time) error { - return p.s.SetDeadline(t) + return p.SetReadDeadline(t) } // SetReadDeadline implements the net.PacketConn.SetReadDeadline method. 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. 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. @@ -222,6 +255,7 @@ func (p *packetConn) SetBPF(filter []bpf.RawInstruction) error { if err != nil { return os.NewSyscallError("setsockopt", err) } + return nil } @@ -276,91 +310,38 @@ func (p *packetConn) handleStats(s unix.TpacketStats) *Stats { // sysSocket is the default socket implementation. It makes use of // Linux-specific system calls to handle raw socket functionality. type sysSocket struct { - f *os.File - 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() + fd int } +// 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 { - var err error - cerr := s.rc.Control(func(fd uintptr) { - _, _, errno := unix.Syscall6(unix.SYS_GETSOCKOPT, fd, uintptr(level), uintptr(name), uintptr(v), uintptr(unsafe.Pointer(&l)), 0) - if errno != 0 { - err = os.NewSyscallError("getsockopt", errno) - } - }) - if err != nil { - return err + _, _, err := unix.Syscall6(unix.SYS_GETSOCKOPT, uintptr(s.fd), uintptr(level), uintptr(name), uintptr(v), uintptr(unsafe.Pointer(&l)), 0) + if err != 0 { + return unix.Errno(err) } - return cerr + return nil } - -func (s *sysSocket) Recvfrom(p []byte, flags int) (n int, addr unix.Sockaddr, err error) { - 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) Recvfrom(p []byte, flags int) (int, unix.Sockaddr, error) { + return unix.Recvfrom(s.fd, p, flags) } - func (s *sysSocket) Sendto(p []byte, flags int, to unix.Sockaddr) error { - var err error - 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 + return unix.Sendto(s.fd, p, flags, to) } - func (s *sysSocket) SetSockopt(level, name int, v unsafe.Pointer, l uint32) error { - var err error - cerr := s.rc.Control(func(fd uintptr) { - _, _, errno := unix.Syscall6(unix.SYS_SETSOCKOPT, fd, uintptr(level), uintptr(name), uintptr(v), uintptr(l), 0) - if errno != 0 { - err = os.NewSyscallError("setsockopt", errno) - } - }) + _, _, err := unix.Syscall6(unix.SYS_SETSOCKOPT, uintptr(s.fd), uintptr(level), uintptr(name), uintptr(v), uintptr(l), 0) + if err != 0 { + return unix.Errno(err) + } + return nil +} +func (s *sysSocket) SetTimeout(timeout time.Duration) error { + tv, err := newTimeval(timeout) if err != nil { return err } - return cerr + return unix.SetsockoptTimeval(s.fd, unix.SOL_SOCKET, unix.SO_RCVTIMEO, tv) } diff --git a/vendor/github.com/mdlayher/raw/timeout_bsd.go b/vendor/github.com/mdlayher/raw/timeout_bsd.go deleted file mode 100644 index 1957c1a9..00000000 --- a/vendor/github.com/mdlayher/raw/timeout_bsd.go +++ /dev/null @@ -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 } diff --git a/vendor/github.com/mdlayher/raw/timeval.go b/vendor/github.com/mdlayher/raw/timeval.go index c79e3e4a..54027f1a 100644 --- a/vendor/github.com/mdlayher/raw/timeval.go +++ b/vendor/github.com/mdlayher/raw/timeval.go @@ -1,4 +1,4 @@ -// +build !darwin,!arm,!windows,!mipsle,!mips,!386,!linux +// +build !darwin,!arm,!windows,!mipsle,!mips,!386 package raw diff --git a/vendor/github.com/mdlayher/raw/timeval32.go b/vendor/github.com/mdlayher/raw/timeval32.go index 8c2acddc..0c47c619 100644 --- a/vendor/github.com/mdlayher/raw/timeval32.go +++ b/vendor/github.com/mdlayher/raw/timeval32.go @@ -1,5 +1,4 @@ // +build arm mipsle mips 386 -// +build !linux package raw