From 3fe638a7bf3c911518875f2077b92a426fcb65df Mon Sep 17 00:00:00 2001 From: Song Gao Date: Sun, 31 Mar 2019 15:42:28 -0700 Subject: [PATCH] use blocking mode pre-go1.11 --- .travis.yml | 3 ++- if.go | 6 ++--- if_linux.go | 4 ++-- ipv4_go1.11_test.go | 36 ++++++++++++++++++++++++++++ ipv4_test.go | 29 ----------------------- syscalls_darwin.go | 11 ++++----- syscalls_darwin_go1.11.go | 9 +++++++ syscalls_darwin_legacy.go | 10 ++++++++ syscalls_linux.go | 50 +++++++++------------------------------ syscalls_linux_go1.11.go | 27 +++++++++++++++++++++ syscalls_linux_legacy.go | 26 ++++++++++++++++++++ syscalls_other.go | 8 ++----- syscalls_windows.go | 8 ------- 13 files changed, 132 insertions(+), 95 deletions(-) create mode 100644 ipv4_go1.11_test.go create mode 100644 syscalls_darwin_go1.11.go create mode 100644 syscalls_darwin_legacy.go create mode 100644 syscalls_linux_go1.11.go create mode 100644 syscalls_linux_legacy.go diff --git a/.travis.yml b/.travis.yml index eaf2ee0..45eabe8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,7 @@ language: go go: - - "1.11.4" + - "1.12.1" + - "1.10.8" go_import_path: github.com/songgao/water install: go get -u golang.org/x/lint/golint script: make ci diff --git a/if.go b/if.go index d008f78..4023a1a 100644 --- a/if.go +++ b/if.go @@ -57,10 +57,8 @@ func New(config Config) (ifce *Interface, err error) { config.PlatformSpecificParams = defaultPlatformSpecificParams() } switch config.DeviceType { - case TUN: - return newTUN(config) - case TAP: - return newTAP(config) + case TUN, TAP: + return openDev(config) default: return nil, errors.New("unknown device type") } diff --git a/if_linux.go b/if_linux.go index 4c541ce..227e8a8 100644 --- a/if_linux.go +++ b/if_linux.go @@ -14,7 +14,7 @@ func NewTAP(ifName string) (ifce *Interface, err error) { fmt.Println("Deprecated: NewTAP(..) may be removed in the future. Please use New() instead.") config := Config{DeviceType: TAP} config.Name = ifName - return newTAP(config) + return openDev(config) } // NewTUN creates a new TUN interface whose name is ifName. If ifName is empty, a @@ -26,5 +26,5 @@ func NewTUN(ifName string) (ifce *Interface, err error) { fmt.Println("Deprecated: NewTUN(..) may be removed in the future. Please use New() instead.") config := Config{DeviceType: TUN} config.Name = ifName - return newTUN(config) + return openDev(config) } diff --git a/ipv4_go1.11_test.go b/ipv4_go1.11_test.go new file mode 100644 index 0000000..3841065 --- /dev/null +++ b/ipv4_go1.11_test.go @@ -0,0 +1,36 @@ +// +build go1.11 + +package water + +import ( + "context" + "testing" + "time" +) + +func TestCloseUnblockPendingRead(t *testing.T) { + ifce, err := New(Config{DeviceType: TUN}) + if err != nil { + t.Fatalf("creating TUN error: %v\n", err) + } + + c := make(chan struct{}) + go func() { + ifce.Read(make([]byte, 1<<16)) + close(c) + }() + + // make sure ifce.Close() happens after ifce.Read() blocks + time.Sleep(1 * time.Second) + + ifce.Close() + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + select { + case <-c: + t.Log("Pending Read unblocked") + case <-ctx.Done(): + t.Fatal("Timeouted, pending read blocked") + } +} diff --git a/ipv4_test.go b/ipv4_test.go index 37979cb..c89cc7e 100644 --- a/ipv4_test.go +++ b/ipv4_test.go @@ -1,9 +1,7 @@ package water import ( - "context" "testing" - "time" ) const BUFFERSIZE = 1522 @@ -22,30 +20,3 @@ func startRead(t *testing.T, ifce *Interface, dataCh chan<- []byte, errCh chan<- } }() } - -func TestCloseUnblockPendingRead(t *testing.T) { - ifce, err := New(Config{DeviceType: TUN}) - if err != nil { - t.Fatalf("creating TUN error: %v\n", err) - } - - c := make(chan struct{}) - go func() { - ifce.Read(make([]byte, 1<<16)) - close(c) - }() - - // make sure ifce.Close() happens after ifce.Read() blocks - time.Sleep(1 * time.Second) - - ifce.Close() - ctx, cancel := context.WithTimeout(context.Background(), time.Second) - defer cancel() - - select { - case <-c: - t.Log("Pending Read unblocked") - case <-ctx.Done(): - t.Fatal("Timeouted, pending read blocked") - } -} diff --git a/syscalls_darwin.go b/syscalls_darwin.go index ec55349..b344034 100644 --- a/syscalls_darwin.go +++ b/syscalls_darwin.go @@ -59,7 +59,10 @@ type sockaddrCtl struct { var sockaddrCtlSize uintptr = 32 -func newTUN(config Config) (ifce *Interface, err error) { +func openDev(config Config) (ifce *Interface, err error) { + if config.DeviceType != TUN { + return nil, errors.New("only tun is implemented on this platform") + } var fd int // Supposed to be socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL), but ... // @@ -111,7 +114,7 @@ func newTUN(config Config) (ifce *Interface, err error) { return nil, fmt.Errorf("error in syscall.Syscall6(syscall.SYS_GETSOCKOPT, ...): %v", err) } - if err = syscall.SetNonblock(fd, true); err != nil { + if err = setNonBlock(fd); err != nil { return nil, fmt.Errorf("setting non-blocking error") } @@ -124,10 +127,6 @@ func newTUN(config Config) (ifce *Interface, err error) { }, nil } -func newTAP(config Config) (ifce *Interface, err error) { - return nil, errors.New("tap interface not implemented on this platform") -} - // tunReadCloser is a hack to work around the first 4 bytes "packet // information" because there doesn't seem to be an IFF_NO_PI for darwin. type tunReadCloser struct { diff --git a/syscalls_darwin_go1.11.go b/syscalls_darwin_go1.11.go new file mode 100644 index 0000000..b02f734 --- /dev/null +++ b/syscalls_darwin_go1.11.go @@ -0,0 +1,9 @@ +// +build darwin,go1.11 + +package water + +import "syscall" + +func setNonBlock(fd int) error { + return syscall.SetNonblock(fd, true) +} diff --git a/syscalls_darwin_legacy.go b/syscalls_darwin_legacy.go new file mode 100644 index 0000000..d174540 --- /dev/null +++ b/syscalls_darwin_legacy.go @@ -0,0 +1,10 @@ +// +build darwin,!go1.11 + +package water + +func setNonBlock(fd int) error { + // There's a but pre-go1.11 that causes 'resource temporarily unavailable' + // error in non-blocking mode. So just skip it here. Close() won't be able + // to unblock a pending read, but that's better than being broken. + return nil +} diff --git a/syscalls_linux.go b/syscalls_linux.go index 1a4844a..af382d1 100644 --- a/syscalls_linux.go +++ b/syscalls_linux.go @@ -28,54 +28,26 @@ func ioctl(fd uintptr, request uintptr, argp uintptr) error { return nil } -func newTAP(config Config) (ifce *Interface, err error) { - fdInt, err := syscall.Open("/dev/net/tun", os.O_RDWR|syscall.O_NONBLOCK, 0) - if err != nil { - return nil, err +func setupFd(config Config, fd uintptr) (name string, err error) { + var flags uint16 = cIFFNOPI + if config.DeviceType == TUN { + flags |= cIFFTUN + } else { + flags |= cIFFTAP } - fd := uintptr(fdInt) - - var flags uint16 - flags = cIFFTAP | cIFFNOPI if config.PlatformSpecificParams.MultiQueue { flags |= cIFFMULTIQUEUE } - name, err := createInterface(fd, config.Name, flags) - if err != nil { - return nil, err + + if name, err = createInterface(fd, config.Name, flags); err != nil { + return "", err } if err = setDeviceOptions(fd, config); err != nil { - return nil, err + return "", err } - ifce = &Interface{isTAP: true, ReadWriteCloser: os.NewFile(fd, "tun"), name: name} - return -} - -func newTUN(config Config) (ifce *Interface, err error) { - fdInt, err := syscall.Open("/dev/net/tun", os.O_RDWR|syscall.O_NONBLOCK, 0) - if err != nil { - return nil, err - } - fd := uintptr(fdInt) - - var flags uint16 - flags = cIFFTUN | cIFFNOPI - if config.PlatformSpecificParams.MultiQueue { - flags |= cIFFMULTIQUEUE - } - name, err := createInterface(fd, config.Name, flags) - if err != nil { - return nil, err - } - - if err = setDeviceOptions(fd, config); err != nil { - return nil, err - } - - ifce = &Interface{isTAP: false, ReadWriteCloser: os.NewFile(fd, "tun"), name: name} - return + return name, nil } func createInterface(fd uintptr, ifName string, flags uint16) (createdIFName string, err error) { diff --git a/syscalls_linux_go1.11.go b/syscalls_linux_go1.11.go new file mode 100644 index 0000000..90c47a1 --- /dev/null +++ b/syscalls_linux_go1.11.go @@ -0,0 +1,27 @@ +// +build linux,go1.11 + +package water + +import ( + "os" + "syscall" +) + +func openDev(config Config) (ifce *Interface, err error) { + var fdInt int + if fdInt, err = syscall.Open( + "/dev/net/tun", os.O_RDWR|syscall.O_NONBLOCK, 0); err != nil { + return nil, err + } + + name, err := setupFd(config, uintptr(fdInt)) + if err != nil { + return nil, err + } + + return &Interface{ + isTAP: config.DeviceType == TAP, + ReadWriteCloser: os.NewFile(uintptr(fdInt), "tun"), + name: name, + }, nil +} diff --git a/syscalls_linux_legacy.go b/syscalls_linux_legacy.go new file mode 100644 index 0000000..6499b37 --- /dev/null +++ b/syscalls_linux_legacy.go @@ -0,0 +1,26 @@ +// +build linux,!go1.11 + +package water + +import ( + "os" +) + +func openDev(config Config) (ifce *Interface, err error) { + var file *os.File + if file, err = os.OpenFile( + "/dev/net/tun", os.O_RDWR, 0); err != nil { + return nil, err + } + + name, err := setupFd(config, file.Fd()) + if err != nil { + return nil, err + } + + return &Interface{ + isTAP: config.DeviceType == TAP, + ReadWriteCloser: file, + name: name, + }, nil +} diff --git a/syscalls_other.go b/syscalls_other.go index ed49244..c5ed1b3 100644 --- a/syscalls_other.go +++ b/syscalls_other.go @@ -4,10 +4,6 @@ package water import "errors" -func newTAP(config Config) (ifce *Interface, err error) { - return nil, errors.New("tap interface not implemented on this platform") -} - -func newTUN(config Config) (ifce *Interface, err error) { - return nil, errors.New("tap interface not implemented on this platform") +func openDev(config Config) (*Interface, error) { + return nil, errors.New("not implemented on this platform") } diff --git a/syscalls_windows.go b/syscalls_windows.go index c0bf47e..d495262 100644 --- a/syscalls_windows.go +++ b/syscalls_windows.go @@ -306,11 +306,3 @@ func openDev(config Config) (ifce *Interface, err error) { return nil, errIfceNameNotFound } - -func newTAP(config Config) (ifce *Interface, err error) { - return openDev(config) -} - -func newTUN(config Config) (ifce *Interface, err error) { - return openDev(config) -}