From ab0f442dafba417c3a13d883a447e882d7983690 Mon Sep 17 00:00:00 2001
From: "Jason A. Donenfeld" <Jason@zx2c4.com>
Date: Wed, 27 Feb 2019 01:06:43 +0100
Subject: [PATCH] tun: use sysconn instead of .Fd with Go 1.12

---
 README.md          |  2 +-
 tun/tun.go         | 17 ++++++++++++++++-
 tun/tun_darwin.go  | 27 ++++++++++++++-------------
 tun/tun_freebsd.go | 38 ++++++++++++++++++++++++--------------
 tun/tun_linux.go   | 45 +++++++++++++++++++++------------------------
 tun/tun_openbsd.go |  6 ++++--
 6 files changed, 80 insertions(+), 55 deletions(-)

diff --git a/README.md b/README.md
index cbdf4e3..b6a9f39 100644
--- a/README.md
+++ b/README.md
@@ -48,7 +48,7 @@ This will run on OpenBSD. It does not yet support sticky sockets. Fwmark is mapp
 
 ## Building
 
-This requires an installation of [go](https://golang.org) ≥ 1.11.
+This requires an installation of [go](https://golang.org) ≥ 1.12.
 
 ```
 $ git clone https://git.zx2c4.com/wireguard-go
diff --git a/tun/tun.go b/tun/tun.go
index baf6fbb..d9c3c15 100644
--- a/tun/tun.go
+++ b/tun/tun.go
@@ -5,7 +5,10 @@
 
 package tun
 
-import "os"
+import (
+	"fmt"
+	"os"
+)
 
 type TUNEvent int
 
@@ -24,3 +27,15 @@ type TUNDevice interface {
 	Events() chan TUNEvent          // returns a constant channel of events related to the device
 	Close() error                   // stops the device and closes the event channel
 }
+
+func (tun *nativeTun) operateOnFd(fn func(fd uintptr)) {
+	sysconn, err := tun.tunFile.SyscallConn()
+	if err != nil {
+		tun.errors <- fmt.Errorf("unable to find sysconn for tunfile: %s", err.Error())
+		return
+	}
+	err = sysconn.Control(fn)
+	if err != nil {
+		tun.errors <- fmt.Errorf("unable to control sysconn for tunfile: %s", err.Error())
+	}
+}
\ No newline at end of file
diff --git a/tun/tun_darwin.go b/tun/tun_darwin.go
index 9eef4e8..1190a9d 100644
--- a/tun/tun_darwin.go
+++ b/tun/tun_darwin.go
@@ -8,9 +8,9 @@ package tun
 import (
 	"errors"
 	"fmt"
-	"golang.zx2c4.com/wireguard/rwcancel"
 	"golang.org/x/net/ipv6"
 	"golang.org/x/sys/unix"
+	"golang.zx2c4.com/wireguard/rwcancel"
 	"io/ioutil"
 	"net"
 	"os"
@@ -36,7 +36,6 @@ type sockaddrCtl struct {
 type nativeTun struct {
 	name        string
 	tunFile     *os.File
-	fd          uintptr
 	rwcancel    *rwcancel.RWCancel
 	events      chan TUNEvent
 	errors      chan error
@@ -168,10 +167,8 @@ func CreateTUN(name string, mtu int) (TUNDevice, error) {
 }
 
 func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
-
 	tun := &nativeTun{
 		tunFile: file,
-		fd:      file.Fd(),
 		events:  make(chan TUNEvent, 10),
 		errors:  make(chan error, 1),
 	}
@@ -194,7 +191,9 @@ func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
 		return nil, err
 	}
 
-	tun.rwcancel, err = rwcancel.NewRWCancel(int(tun.fd))
+	tun.operateOnFd(func (fd uintptr) {
+		tun.rwcancel, err = rwcancel.NewRWCancel(int(fd))
+	})
 	if err != nil {
 		tun.tunFile.Close()
 		return nil, err
@@ -218,19 +217,21 @@ func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
 }
 
 func (tun *nativeTun) Name() (string, error) {
-
 	var ifName struct {
 		name [16]byte
 	}
 	ifNameSize := uintptr(16)
 
-	_, _, errno := unix.Syscall6(
-		unix.SYS_GETSOCKOPT,
-		uintptr(tun.fd),
-		2, /* #define SYSPROTO_CONTROL 2 */
-		2, /* #define UTUN_OPT_IFNAME 2 */
-		uintptr(unsafe.Pointer(&ifName)),
-		uintptr(unsafe.Pointer(&ifNameSize)), 0)
+	var errno syscall.Errno
+	tun.operateOnFd(func(fd uintptr) {
+		_, _, errno = unix.Syscall6(
+			unix.SYS_GETSOCKOPT,
+			fd,
+			2, /* #define SYSPROTO_CONTROL 2 */
+			2, /* #define UTUN_OPT_IFNAME 2 */
+			uintptr(unsafe.Pointer(&ifName)),
+			uintptr(unsafe.Pointer(&ifNameSize)), 0)
+	})
 
 	if errno != 0 {
 		return "", fmt.Errorf("SYS_GETSOCKOPT: %v", errno)
diff --git a/tun/tun_freebsd.go b/tun/tun_freebsd.go
index a2c4562..1aec123 100644
--- a/tun/tun_freebsd.go
+++ b/tun/tun_freebsd.go
@@ -9,9 +9,9 @@ import (
 	"bytes"
 	"errors"
 	"fmt"
-	"golang.zx2c4.com/wireguard/rwcancel"
 	"golang.org/x/net/ipv6"
 	"golang.org/x/sys/unix"
+	"golang.zx2c4.com/wireguard/rwcancel"
 	"net"
 	"os"
 	"syscall"
@@ -52,7 +52,6 @@ type ifstat struct {
 type nativeTun struct {
 	name        string
 	tunFile     *os.File
-	fd          uintptr
 	rwcancel    *rwcancel.RWCancel
 	events      chan TUNEvent
 	errors      chan error
@@ -239,12 +238,15 @@ func CreateTUN(name string, mtu int) (TUNDevice, error) {
 	}
 
 	tunFile, err := os.OpenFile("/dev/tun", unix.O_RDWR, 0)
-
 	if err != nil {
 		return nil, err
 	}
-	tunfd := tunFile.Fd()
-	assignedName, err := tunName(tunfd)
+
+	tun := nativeTun{tunFile: tunFile}
+	var assignedName string
+	tun.operateOnFd(func(fd uintptr) {
+		assignedName, err = tunName(fd)
+	})
 	if err != nil {
 		tunFile.Close()
 		return nil, err
@@ -252,12 +254,15 @@ func CreateTUN(name string, mtu int) (TUNDevice, error) {
 
 	// Enable ifhead mode, otherwise tun will complain if it gets a non-AF_INET packet
 	ifheadmode := 1
-	_, _, errno := unix.Syscall(
-		unix.SYS_IOCTL,
-		uintptr(tunfd),
-		uintptr(_TUNSIFHEAD),
-		uintptr(unsafe.Pointer(&ifheadmode)),
-	)
+	var errno syscall.Errno
+	tun.operateOnFd(func(fd uintptr) {
+		_, _, errno = unix.Syscall(
+			unix.SYS_IOCTL,
+			fd,
+			uintptr(_TUNSIFHEAD),
+			uintptr(unsafe.Pointer(&ifheadmode)),
+		)
+	})
 
 	if errno != 0 {
 		return nil, fmt.Errorf("error %s", errno.Error())
@@ -306,7 +311,6 @@ func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
 
 	tun := &nativeTun{
 		tunFile: file,
-		fd:      file.Fd(),
 		events:  make(chan TUNEvent, 10),
 		errors:  make(chan error, 1),
 	}
@@ -329,7 +333,9 @@ func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
 		return nil, err
 	}
 
-	tun.rwcancel, err = rwcancel.NewRWCancel(int(tun.fd))
+	tun.operateOnFd(func(fd uintptr) {
+		tun.rwcancel, err = rwcancel.NewRWCancel(int(fd))
+	})
 	if err != nil {
 		tun.tunFile.Close()
 		return nil, err
@@ -353,7 +359,11 @@ func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
 }
 
 func (tun *nativeTun) Name() (string, error) {
-	name, err := tunName(tun.fd)
+	var name string
+	var err error
+	tun.operateOnFd(func(fd uintptr) {
+		name, err = tunName(fd)
+	})
 	if err != nil {
 		return "", err
 	}
diff --git a/tun/tun_linux.go b/tun/tun_linux.go
index 7a0647f..abdcce2 100644
--- a/tun/tun_linux.go
+++ b/tun/tun_linux.go
@@ -12,13 +12,14 @@ import (
 	"bytes"
 	"errors"
 	"fmt"
-	"golang.zx2c4.com/wireguard/rwcancel"
 	"golang.org/x/net/ipv6"
 	"golang.org/x/sys/unix"
+	"golang.zx2c4.com/wireguard/rwcancel"
 	"net"
 	"os"
 	"strconv"
 	"sync"
+	"syscall"
 	"time"
 	"unsafe"
 )
@@ -30,7 +31,6 @@ const (
 
 type nativeTun struct {
 	tunFile                 *os.File
-	fd                      uintptr
 	fdCancel                *rwcancel.RWCancel
 	index                   int32         // if index
 	name                    string        // name of interface
@@ -52,9 +52,11 @@ func (tun *nativeTun) routineHackListener() {
 	/* This is needed for the detection to work across network namespaces
 	 * If you are reading this and know a better method, please get in touch.
 	 */
-	fd := int(tun.fd)
 	for {
-		_, err := unix.Write(fd, nil)
+		var err error
+		tun.operateOnFd(func(fd uintptr) {
+			_, err = unix.Write(int(fd), nil)
+		})
 		switch err {
 		case unix.EINVAL:
 			tun.events <- TUNEventUp
@@ -162,16 +164,12 @@ func (tun *nativeTun) isUp() (bool, error) {
 	return inter.Flags&net.FlagUp != 0, err
 }
 
-func getDummySock() (int, error) {
-	return unix.Socket(
+func getIFIndex(name string) (int32, error) {
+	fd, err := unix.Socket(
 		unix.AF_INET,
 		unix.SOCK_DGRAM,
 		0,
 	)
-}
-
-func getIFIndex(name string) (int32, error) {
-	fd, err := getDummySock()
 	if err != nil {
 		return 0, err
 	}
@@ -195,9 +193,7 @@ func getIFIndex(name string) (int32, error) {
 }
 
 func (tun *nativeTun) setMTU(n int) error {
-
 	// open datagram socket
-
 	fd, err := unix.Socket(
 		unix.AF_INET,
 		unix.SOCK_DGRAM,
@@ -230,9 +226,7 @@ func (tun *nativeTun) setMTU(n int) error {
 }
 
 func (tun *nativeTun) MTU() (int, error) {
-
 	// open datagram socket
-
 	fd, err := unix.Socket(
 		unix.AF_INET,
 		unix.SOCK_DGRAM,
@@ -263,14 +257,16 @@ func (tun *nativeTun) MTU() (int, error) {
 }
 
 func (tun *nativeTun) Name() (string, error) {
-
 	var ifr [ifReqSize]byte
-	_, _, errno := unix.Syscall(
-		unix.SYS_IOCTL,
-		tun.fd,
-		uintptr(unix.TUNGETIFF),
-		uintptr(unsafe.Pointer(&ifr[0])),
-	)
+	var errno syscall.Errno
+	tun.operateOnFd(func(fd uintptr) {
+		_, _, errno = unix.Syscall(
+			unix.SYS_IOCTL,
+			fd,
+			uintptr(unix.TUNGETIFF),
+			uintptr(unsafe.Pointer(&ifr[0])),
+		)
+	})
 	if errno != 0 {
 		return "", errors.New("failed to get name of TUN device: " + strconv.FormatInt(int64(errno), 10))
 	}
@@ -391,7 +387,7 @@ func CreateTUN(name string, mtu int) (TUNDevice, error) {
 
 	_, _, errno := unix.Syscall(
 		unix.SYS_IOCTL,
-		fd.Fd(),
+		nfd,
 		uintptr(unix.TUNSETIFF),
 		uintptr(unsafe.Pointer(&ifr[0])),
 	)
@@ -405,7 +401,6 @@ func CreateTUN(name string, mtu int) (TUNDevice, error) {
 func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
 	tun := &nativeTun{
 		tunFile:                 file,
-		fd:                      file.Fd(),
 		events:                  make(chan TUNEvent, 5),
 		errors:                  make(chan error, 5),
 		statusListenersShutdown: make(chan struct{}),
@@ -413,7 +408,9 @@ func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
 	}
 	var err error
 
-	tun.fdCancel, err = rwcancel.NewRWCancel(int(tun.fd))
+	tun.operateOnFd(func(fd uintptr) {
+		tun.fdCancel, err = rwcancel.NewRWCancel(int(fd))
+	})
 	if err != nil {
 		tun.tunFile.Close()
 		return nil, err
diff --git a/tun/tun_openbsd.go b/tun/tun_openbsd.go
index ab4c87b..b10c460 100644
--- a/tun/tun_openbsd.go
+++ b/tun/tun_openbsd.go
@@ -8,9 +8,9 @@ package tun
 import (
 	"errors"
 	"fmt"
-	"golang.zx2c4.com/wireguard/rwcancel"
 	"golang.org/x/net/ipv6"
 	"golang.org/x/sys/unix"
+	"golang.zx2c4.com/wireguard/rwcancel"
 	"io/ioutil"
 	"net"
 	"os"
@@ -167,7 +167,9 @@ func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
 		return nil, err
 	}
 
-	tun.rwcancel, err = rwcancel.NewRWCancel(int(file.Fd()))
+	tun.operateOnFd(func(fd uintptr) {
+		tun.rwcancel, err = rwcancel.NewRWCancel(int(fd))
+	})
 	if err != nil {
 		tun.tunFile.Close()
 		return nil, err