Skip to content

Commit

Permalink
use blocking mode pre-go1.11
Browse files Browse the repository at this point in the history
  • Loading branch information
songgao committed Apr 2, 2019
1 parent 240a3d7 commit 3fe638a
Show file tree
Hide file tree
Showing 13 changed files with 131 additions and 94 deletions.
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -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
Expand Down
6 changes: 2 additions & 4 deletions if.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
Expand Down
4 changes: 2 additions & 2 deletions if_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
}
36 changes: 36 additions & 0 deletions ipv4_go1.11_test.go
Original file line number Diff line number Diff line change
@@ -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")
}
}
29 changes: 0 additions & 29 deletions ipv4_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package water

import (
"context"
"testing"
"time"
)

const BUFFERSIZE = 1522
Expand All @@ -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")
}
}
11 changes: 5 additions & 6 deletions syscalls_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 ...
//
Expand Down Expand Up @@ -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")
}

Expand All @@ -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 {
Expand Down
9 changes: 9 additions & 0 deletions syscalls_darwin_go1.11.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// +build darwin,go1.11

package water

import "syscall"

func setNonBlock(fd int) error {
return syscall.SetNonblock(fd, true)
}
10 changes: 10 additions & 0 deletions syscalls_darwin_legacy.go
Original file line number Diff line number Diff line change
@@ -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
}
48 changes: 10 additions & 38 deletions syscalls_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 err = setDeviceOptions(fd, config); err != nil {
return nil, 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 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: false, ReadWriteCloser: os.NewFile(fd, "tun"), name: name}
return
return name, nil
}

func createInterface(fd uintptr, ifName string, flags uint16) (createdIFName string, err error) {
Expand Down
27 changes: 27 additions & 0 deletions syscalls_linux_go1.11.go
Original file line number Diff line number Diff line change
@@ -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
}
26 changes: 26 additions & 0 deletions syscalls_linux_legacy.go
Original file line number Diff line number Diff line change
@@ -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
}
8 changes: 2 additions & 6 deletions syscalls_other.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
8 changes: 0 additions & 8 deletions syscalls_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

0 comments on commit 3fe638a

Please sign in to comment.