From 4d58427608787b599b0a1e6cf89c61f97ef3985a Mon Sep 17 00:00:00 2001 From: LE Manh Cuong Date: Fri, 9 Nov 2018 09:30:00 +0700 Subject: [PATCH] Linux set interface underlying fd to non-blocking --- ipv4_test.go | 34 ++++++++++++++++++++++++++++++++++ syscalls_linux.go | 15 +++++++++++++-- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/ipv4_test.go b/ipv4_test.go index ec6d89b..b72f798 100644 --- a/ipv4_test.go +++ b/ipv4_test.go @@ -1,6 +1,7 @@ package water import ( + "context" "net" "testing" "time" @@ -74,3 +75,36 @@ readFrame: } } } + +func TestCloseUnblockPendingRead(t *testing.T) { + var ( + self = net.IPv4(192, 168, 150, 1) + mask = net.IPv4Mask(255, 255, 255, 0) + ) + + ifce, err := New(Config{DeviceType: TUN}) + if err != nil { + t.Fatalf("creating TUN error: %v\n", err) + } + + setupIfce(t, net.IPNet{IP: self, Mask: mask}, ifce.Name()) + c := make(chan struct{}) + go func() { + ifce.Read(make([]byte, 1<<16)) + close(c) + }() + + // make sure ifce.Close() happens after ifce.Read() + 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_linux.go b/syscalls_linux.go index 56c062b..e6edbdb 100644 --- a/syscalls_linux.go +++ b/syscalls_linux.go @@ -31,7 +31,7 @@ func ioctl(fd uintptr, request uintptr, argp uintptr) error { } func newTAP(config Config) (ifce *Interface, err error) { - file, err := os.OpenFile("/dev/net/tun", os.O_RDWR, 0) + file, err := openTun() if err != nil { return nil, err } @@ -55,7 +55,7 @@ func newTAP(config Config) (ifce *Interface, err error) { } func newTUN(config Config) (ifce *Interface, err error) { - file, err := os.OpenFile("/dev/net/tun", os.O_RDWR, 0) + file, err := openTun() if err != nil { return nil, err } @@ -107,5 +107,16 @@ func setDeviceOptions(fd uintptr, config Config) (err error) { if config.Persist { value = 1 } + return ioctl(fd, syscall.TUNSETPERSIST, uintptr(value)) } + +func openTun() (*os.File, error) { + tunFile := "/dev/net/tun" + bfile, err := os.OpenFile(tunFile, os.O_RDWR, 0) + if err != nil { + return nil, err + } + + return os.NewFile(bfile.Fd(), tunFile), nil +}