tun: use sysconn instead of .Fd with Go 1.12

This commit is contained in:
Jason A. Donenfeld 2019-02-27 01:06:43 +01:00
parent 66524c1f7e
commit ab0f442daf
6 changed files with 80 additions and 55 deletions

View file

@ -48,7 +48,7 @@ This will run on OpenBSD. It does not yet support sticky sockets. Fwmark is mapp
## Building ## 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 $ git clone https://git.zx2c4.com/wireguard-go

View file

@ -5,7 +5,10 @@
package tun package tun
import "os" import (
"fmt"
"os"
)
type TUNEvent int type TUNEvent int
@ -24,3 +27,15 @@ type TUNDevice interface {
Events() chan TUNEvent // returns a constant channel of events related to the device Events() chan TUNEvent // returns a constant channel of events related to the device
Close() error // stops the device and closes the event channel 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())
}
}

View file

@ -8,9 +8,9 @@ package tun
import ( import (
"errors" "errors"
"fmt" "fmt"
"golang.zx2c4.com/wireguard/rwcancel"
"golang.org/x/net/ipv6" "golang.org/x/net/ipv6"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
"golang.zx2c4.com/wireguard/rwcancel"
"io/ioutil" "io/ioutil"
"net" "net"
"os" "os"
@ -36,7 +36,6 @@ type sockaddrCtl struct {
type nativeTun struct { type nativeTun struct {
name string name string
tunFile *os.File tunFile *os.File
fd uintptr
rwcancel *rwcancel.RWCancel rwcancel *rwcancel.RWCancel
events chan TUNEvent events chan TUNEvent
errors chan error errors chan error
@ -168,10 +167,8 @@ func CreateTUN(name string, mtu int) (TUNDevice, error) {
} }
func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) { func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
tun := &nativeTun{ tun := &nativeTun{
tunFile: file, tunFile: file,
fd: file.Fd(),
events: make(chan TUNEvent, 10), events: make(chan TUNEvent, 10),
errors: make(chan error, 1), errors: make(chan error, 1),
} }
@ -194,7 +191,9 @@ func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
return nil, err 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 { if err != nil {
tun.tunFile.Close() tun.tunFile.Close()
return nil, err return nil, err
@ -218,19 +217,21 @@ func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
} }
func (tun *nativeTun) Name() (string, error) { func (tun *nativeTun) Name() (string, error) {
var ifName struct { var ifName struct {
name [16]byte name [16]byte
} }
ifNameSize := uintptr(16) ifNameSize := uintptr(16)
_, _, errno := unix.Syscall6( var errno syscall.Errno
unix.SYS_GETSOCKOPT, tun.operateOnFd(func(fd uintptr) {
uintptr(tun.fd), _, _, errno = unix.Syscall6(
2, /* #define SYSPROTO_CONTROL 2 */ unix.SYS_GETSOCKOPT,
2, /* #define UTUN_OPT_IFNAME 2 */ fd,
uintptr(unsafe.Pointer(&ifName)), 2, /* #define SYSPROTO_CONTROL 2 */
uintptr(unsafe.Pointer(&ifNameSize)), 0) 2, /* #define UTUN_OPT_IFNAME 2 */
uintptr(unsafe.Pointer(&ifName)),
uintptr(unsafe.Pointer(&ifNameSize)), 0)
})
if errno != 0 { if errno != 0 {
return "", fmt.Errorf("SYS_GETSOCKOPT: %v", errno) return "", fmt.Errorf("SYS_GETSOCKOPT: %v", errno)

View file

@ -9,9 +9,9 @@ import (
"bytes" "bytes"
"errors" "errors"
"fmt" "fmt"
"golang.zx2c4.com/wireguard/rwcancel"
"golang.org/x/net/ipv6" "golang.org/x/net/ipv6"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
"golang.zx2c4.com/wireguard/rwcancel"
"net" "net"
"os" "os"
"syscall" "syscall"
@ -52,7 +52,6 @@ type ifstat struct {
type nativeTun struct { type nativeTun struct {
name string name string
tunFile *os.File tunFile *os.File
fd uintptr
rwcancel *rwcancel.RWCancel rwcancel *rwcancel.RWCancel
events chan TUNEvent events chan TUNEvent
errors chan error 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) tunFile, err := os.OpenFile("/dev/tun", unix.O_RDWR, 0)
if err != nil { if err != nil {
return nil, err 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 { if err != nil {
tunFile.Close() tunFile.Close()
return nil, err 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 // Enable ifhead mode, otherwise tun will complain if it gets a non-AF_INET packet
ifheadmode := 1 ifheadmode := 1
_, _, errno := unix.Syscall( var errno syscall.Errno
unix.SYS_IOCTL, tun.operateOnFd(func(fd uintptr) {
uintptr(tunfd), _, _, errno = unix.Syscall(
uintptr(_TUNSIFHEAD), unix.SYS_IOCTL,
uintptr(unsafe.Pointer(&ifheadmode)), fd,
) uintptr(_TUNSIFHEAD),
uintptr(unsafe.Pointer(&ifheadmode)),
)
})
if errno != 0 { if errno != 0 {
return nil, fmt.Errorf("error %s", errno.Error()) return nil, fmt.Errorf("error %s", errno.Error())
@ -306,7 +311,6 @@ func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
tun := &nativeTun{ tun := &nativeTun{
tunFile: file, tunFile: file,
fd: file.Fd(),
events: make(chan TUNEvent, 10), events: make(chan TUNEvent, 10),
errors: make(chan error, 1), errors: make(chan error, 1),
} }
@ -329,7 +333,9 @@ func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
return nil, err 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 { if err != nil {
tun.tunFile.Close() tun.tunFile.Close()
return nil, err return nil, err
@ -353,7 +359,11 @@ func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
} }
func (tun *nativeTun) Name() (string, 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 { if err != nil {
return "", err return "", err
} }

View file

@ -12,13 +12,14 @@ import (
"bytes" "bytes"
"errors" "errors"
"fmt" "fmt"
"golang.zx2c4.com/wireguard/rwcancel"
"golang.org/x/net/ipv6" "golang.org/x/net/ipv6"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
"golang.zx2c4.com/wireguard/rwcancel"
"net" "net"
"os" "os"
"strconv" "strconv"
"sync" "sync"
"syscall"
"time" "time"
"unsafe" "unsafe"
) )
@ -30,7 +31,6 @@ const (
type nativeTun struct { type nativeTun struct {
tunFile *os.File tunFile *os.File
fd uintptr
fdCancel *rwcancel.RWCancel fdCancel *rwcancel.RWCancel
index int32 // if index index int32 // if index
name string // name of interface name string // name of interface
@ -52,9 +52,11 @@ func (tun *nativeTun) routineHackListener() {
/* This is needed for the detection to work across network namespaces /* 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. * If you are reading this and know a better method, please get in touch.
*/ */
fd := int(tun.fd)
for { for {
_, err := unix.Write(fd, nil) var err error
tun.operateOnFd(func(fd uintptr) {
_, err = unix.Write(int(fd), nil)
})
switch err { switch err {
case unix.EINVAL: case unix.EINVAL:
tun.events <- TUNEventUp tun.events <- TUNEventUp
@ -162,16 +164,12 @@ func (tun *nativeTun) isUp() (bool, error) {
return inter.Flags&net.FlagUp != 0, err return inter.Flags&net.FlagUp != 0, err
} }
func getDummySock() (int, error) { func getIFIndex(name string) (int32, error) {
return unix.Socket( fd, err := unix.Socket(
unix.AF_INET, unix.AF_INET,
unix.SOCK_DGRAM, unix.SOCK_DGRAM,
0, 0,
) )
}
func getIFIndex(name string) (int32, error) {
fd, err := getDummySock()
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -195,9 +193,7 @@ func getIFIndex(name string) (int32, error) {
} }
func (tun *nativeTun) setMTU(n int) error { func (tun *nativeTun) setMTU(n int) error {
// open datagram socket // open datagram socket
fd, err := unix.Socket( fd, err := unix.Socket(
unix.AF_INET, unix.AF_INET,
unix.SOCK_DGRAM, unix.SOCK_DGRAM,
@ -230,9 +226,7 @@ func (tun *nativeTun) setMTU(n int) error {
} }
func (tun *nativeTun) MTU() (int, error) { func (tun *nativeTun) MTU() (int, error) {
// open datagram socket // open datagram socket
fd, err := unix.Socket( fd, err := unix.Socket(
unix.AF_INET, unix.AF_INET,
unix.SOCK_DGRAM, unix.SOCK_DGRAM,
@ -263,14 +257,16 @@ func (tun *nativeTun) MTU() (int, error) {
} }
func (tun *nativeTun) Name() (string, error) { func (tun *nativeTun) Name() (string, error) {
var ifr [ifReqSize]byte var ifr [ifReqSize]byte
_, _, errno := unix.Syscall( var errno syscall.Errno
unix.SYS_IOCTL, tun.operateOnFd(func(fd uintptr) {
tun.fd, _, _, errno = unix.Syscall(
uintptr(unix.TUNGETIFF), unix.SYS_IOCTL,
uintptr(unsafe.Pointer(&ifr[0])), fd,
) uintptr(unix.TUNGETIFF),
uintptr(unsafe.Pointer(&ifr[0])),
)
})
if errno != 0 { if errno != 0 {
return "", errors.New("failed to get name of TUN device: " + strconv.FormatInt(int64(errno), 10)) 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( _, _, errno := unix.Syscall(
unix.SYS_IOCTL, unix.SYS_IOCTL,
fd.Fd(), nfd,
uintptr(unix.TUNSETIFF), uintptr(unix.TUNSETIFF),
uintptr(unsafe.Pointer(&ifr[0])), 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) { func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
tun := &nativeTun{ tun := &nativeTun{
tunFile: file, tunFile: file,
fd: file.Fd(),
events: make(chan TUNEvent, 5), events: make(chan TUNEvent, 5),
errors: make(chan error, 5), errors: make(chan error, 5),
statusListenersShutdown: make(chan struct{}), statusListenersShutdown: make(chan struct{}),
@ -413,7 +408,9 @@ func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
} }
var err 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 { if err != nil {
tun.tunFile.Close() tun.tunFile.Close()
return nil, err return nil, err

View file

@ -8,9 +8,9 @@ package tun
import ( import (
"errors" "errors"
"fmt" "fmt"
"golang.zx2c4.com/wireguard/rwcancel"
"golang.org/x/net/ipv6" "golang.org/x/net/ipv6"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
"golang.zx2c4.com/wireguard/rwcancel"
"io/ioutil" "io/ioutil"
"net" "net"
"os" "os"
@ -167,7 +167,9 @@ func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
return nil, err 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 { if err != nil {
tun.tunFile.Close() tun.tunFile.Close()
return nil, err return nil, err