From 0a63188afab1dd49380f916963307f9b2efdcac1 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Wed, 23 May 2018 02:10:54 +0200 Subject: Move tun to subpackage --- device.go | 7 +- main.go | 7 +- misc.go | 7 - tun.go | 26 +-- tun/tun.go | 27 +++ tun/tun_darwin.go | 382 +++++++++++++++++++++++++++++++++++++++ tun/tun_freebsd.go | 518 ++++++++++++++++++++++++++++++++++++++++++++++++++++ tun/tun_linux.go | 468 +++++++++++++++++++++++++++++++++++++++++++++++ tun/tun_openbsd.go | 359 ++++++++++++++++++++++++++++++++++++ tun_darwin.go | 384 --------------------------------------- tun_freebsd.go | 519 ----------------------------------------------------- tun_linux.go | 472 ------------------------------------------------ tun_openbsd.go | 360 ------------------------------------- 13 files changed, 1766 insertions(+), 1770 deletions(-) create mode 100644 tun/tun.go create mode 100644 tun/tun_darwin.go create mode 100644 tun/tun_freebsd.go create mode 100644 tun/tun_linux.go create mode 100644 tun/tun_openbsd.go delete mode 100644 tun_darwin.go delete mode 100644 tun_freebsd.go delete mode 100644 tun_linux.go delete mode 100644 tun_openbsd.go diff --git a/device.go b/device.go index 5f468b2..e116731 100644 --- a/device.go +++ b/device.go @@ -8,6 +8,7 @@ package main import ( "./ratelimiter" + "./tun" "runtime" "sync" "sync/atomic" @@ -80,7 +81,7 @@ type Device struct { } tun struct { - device TUNDevice + device tun.TUNDevice mtu int32 } } @@ -250,7 +251,7 @@ func (device *Device) PutMessageBuffer(msg *[MaxMessageSize]byte) { device.pool.messageBuffers.Put(msg) } -func NewDevice(tun TUNDevice, logger *Logger) *Device { +func NewDevice(tunDevice tun.TUNDevice, logger *Logger) *Device { device := new(Device) device.isUp.Set(false) @@ -258,7 +259,7 @@ func NewDevice(tun TUNDevice, logger *Logger) *Device { device.log = logger - device.tun.device = tun + device.tun.device = tunDevice mtu, err := device.tun.device.MTU() if err != nil { logger.Error.Println("Trouble determining MTU, assuming default:", err) diff --git a/main.go b/main.go index 523a927..a04cd15 100644 --- a/main.go +++ b/main.go @@ -7,6 +7,7 @@ package main import ( + "./tun" "fmt" "os" "os/signal" @@ -125,10 +126,10 @@ func main() { // open TUN device (or use supplied fd) - tun, err := func() (TUNDevice, error) { + tun, err := func() (tun.TUNDevice, error) { tunFdStr := os.Getenv(ENV_WG_TUN_FD) if tunFdStr == "" { - return CreateTUN(interfaceName) + return tun.CreateTUN(interfaceName, DefaultMTU) } // construct tun device from supplied fd @@ -139,7 +140,7 @@ func main() { } file := os.NewFile(uintptr(fd), "") - return CreateTUNFromFile(file) + return tun.CreateTUNFromFile(file, DefaultMTU) }() if err == nil { diff --git a/misc.go b/misc.go index aeda813..af61718 100644 --- a/misc.go +++ b/misc.go @@ -41,13 +41,6 @@ func (a *AtomicBool) Set(val bool) { atomic.StoreInt32(&a.flag, flag) } -/* Integer manipulation */ - -func toInt32(n uint32) int32 { - mask := uint32(1 << 31) - return int32(-(n & mask) + (n & ^mask)) -} - func min(a, b uint) uint { if a > b { return b diff --git a/tun.go b/tun.go index d2fb708..9752636 100644 --- a/tun.go +++ b/tun.go @@ -7,30 +7,12 @@ package main import ( - "os" + "./tun" "sync/atomic" ) const DefaultMTU = 1420 -type TUNEvent int - -const ( - TUNEventUp = 1 << iota - TUNEventDown - TUNEventMTUUpdate -) - -type TUNDevice interface { - File() *os.File // returns the file descriptor of the device - Read([]byte, int) (int, error) // read a packet from the device (without any additional headers) - Write([]byte, int) (int, error) // writes a packet to the device (without any additional headers) - MTU() (int, error) // returns the MTU of the device - Name() (string, error) // fetches and returns the current name - Events() chan TUNEvent // returns a constant channel of events related to the device - Close() error // stops the device and closes the event channel -} - func (device *Device) RoutineTUNEventReader() { setUp := false logDebug := device.log.Debug @@ -41,7 +23,7 @@ func (device *Device) RoutineTUNEventReader() { device.state.starting.Done() for event := range device.tun.device.Events() { - if event&TUNEventMTUUpdate != 0 { + if event&tun.TUNEventMTUUpdate != 0 { mtu, err := device.tun.device.MTU() old := atomic.LoadInt32(&device.tun.mtu) if err != nil { @@ -56,13 +38,13 @@ func (device *Device) RoutineTUNEventReader() { } } - if event&TUNEventUp != 0 && !setUp { + if event&tun.TUNEventUp != 0 && !setUp { logInfo.Println("Interface set up") setUp = true device.Up() } - if event&TUNEventDown != 0 && setUp { + if event&tun.TUNEventDown != 0 && setUp { logInfo.Println("Interface set down") setUp = false device.Down() diff --git a/tun/tun.go b/tun/tun.go new file mode 100644 index 0000000..a39ced5 --- /dev/null +++ b/tun/tun.go @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2017-2018 Jason A. Donenfeld . All Rights Reserved. + * Copyright (C) 2017-2018 Mathias N. Hall-Andersen . + */ + +package tun + +import "os" + +type TUNEvent int + +const ( + TUNEventUp = 1 << iota + TUNEventDown + TUNEventMTUUpdate +) + +type TUNDevice interface { + File() *os.File // returns the file descriptor of the device + Read([]byte, int) (int, error) // read a packet from the device (without any additional headers) + Write([]byte, int) (int, error) // writes a packet to the device (without any additional headers) + MTU() (int, error) // returns the MTU of the device + Name() (string, error) // fetches and returns the current name + Events() chan TUNEvent // returns a constant channel of events related to the device + Close() error // stops the device and closes the event channel +} diff --git a/tun/tun_darwin.go b/tun/tun_darwin.go new file mode 100644 index 0000000..fcbb4f9 --- /dev/null +++ b/tun/tun_darwin.go @@ -0,0 +1,382 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2017-2018 Jason A. Donenfeld . All Rights Reserved. + * Copyright (C) 2017-2018 Mathias N. Hall-Andersen . + */ + +package tun + +import ( + "../rwcancel" + "errors" + "fmt" + "golang.org/x/net/ipv6" + "golang.org/x/sys/unix" + "io/ioutil" + "net" + "os" + "unsafe" +) + +const utunControlName = "com.apple.net.utun_control" + +// _CTLIOCGINFO value derived from /usr/include/sys/{kern_control,ioccom}.h +const _CTLIOCGINFO = (0x40000000 | 0x80000000) | ((100 & 0x1fff) << 16) | uint32(byte('N'))<<8 | 3 + +// sockaddr_ctl specifeid in /usr/include/sys/kern_control.h +type sockaddrCtl struct { + scLen uint8 + scFamily uint8 + ssSysaddr uint16 + scID uint32 + scUnit uint32 + scReserved [5]uint32 +} + +type nativeTun struct { + name string + fd *os.File + rwcancel *rwcancel.RWCancel + events chan TUNEvent + errors chan error + routeSocket int +} + +var sockaddrCtlSize uintptr = 32 + +func (tun *nativeTun) routineRouteListener(tunIfindex int) { + var ( + statusUp bool + statusMTU int + ) + + defer close(tun.events) + + data := make([]byte, os.Getpagesize()) + for { + n, err := unix.Read(tun.routeSocket, data) + if err != nil { + tun.errors <- err + return + } + + if n < 14 { + continue + } + + if data[3 /* type */] != unix.RTM_IFINFO { + continue + } + ifindex := int(*(*uint16)(unsafe.Pointer(&data[12 /* ifindex */]))) + if ifindex != tunIfindex { + continue + } + + iface, err := net.InterfaceByIndex(ifindex) + if err != nil { + tun.errors <- err + return + } + + // Up / Down event + up := (iface.Flags & net.FlagUp) != 0 + if up != statusUp && up { + tun.events <- TUNEventUp + } + if up != statusUp && !up { + tun.events <- TUNEventDown + } + statusUp = up + + // MTU changes + if iface.MTU != statusMTU { + tun.events <- TUNEventMTUUpdate + } + statusMTU = iface.MTU + } +} + +func CreateTUN(name string, mtu int) (TUNDevice, error) { + ifIndex := -1 + if name != "utun" { + _, err := fmt.Sscanf(name, "utun%d", &ifIndex) + if err != nil || ifIndex < 0 { + return nil, fmt.Errorf("Interface name must be utun[0-9]*") + } + } + + fd, err := unix.Socket(unix.AF_SYSTEM, unix.SOCK_DGRAM, 2) + + if err != nil { + return nil, err + } + + var ctlInfo = &struct { + ctlID uint32 + ctlName [96]byte + }{} + + copy(ctlInfo.ctlName[:], []byte(utunControlName)) + + _, _, errno := unix.Syscall( + unix.SYS_IOCTL, + uintptr(fd), + uintptr(_CTLIOCGINFO), + uintptr(unsafe.Pointer(ctlInfo)), + ) + + if errno != 0 { + return nil, fmt.Errorf("_CTLIOCGINFO: %v", errno) + } + + sc := sockaddrCtl{ + scLen: uint8(sockaddrCtlSize), + scFamily: unix.AF_SYSTEM, + ssSysaddr: 2, + scID: ctlInfo.ctlID, + scUnit: uint32(ifIndex) + 1, + } + + scPointer := unsafe.Pointer(&sc) + + _, _, errno = unix.RawSyscall( + unix.SYS_CONNECT, + uintptr(fd), + uintptr(scPointer), + uintptr(sockaddrCtlSize), + ) + + if errno != 0 { + return nil, fmt.Errorf("SYS_CONNECT: %v", errno) + } + + tun, err := CreateTUNFromFile(os.NewFile(uintptr(fd), ""), mtu) + + if err == nil && name == "utun" { + fname := os.Getenv("WG_TUN_NAME_FILE") + if fname != "" { + ioutil.WriteFile(fname, []byte(tun.(*nativeTun).name+"\n"), 0400) + } + } + + return tun, err +} + +func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) { + + tun := &nativeTun{ + fd: file, + events: make(chan TUNEvent, 10), + errors: make(chan error, 1), + } + + name, err := tun.Name() + if err != nil { + tun.fd.Close() + return nil, err + } + + tunIfindex, err := func() (int, error) { + iface, err := net.InterfaceByName(name) + if err != nil { + return -1, err + } + return iface.Index, nil + }() + if err != nil { + tun.fd.Close() + return nil, err + } + + tun.rwcancel, err = rwcancel.NewRWCancel(int(file.Fd())) + if err != nil { + tun.fd.Close() + return nil, err + } + + tun.routeSocket, err = unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC) + if err != nil { + tun.fd.Close() + return nil, err + } + + go tun.routineRouteListener(tunIfindex) + + err = tun.setMTU(mtu) + if err != nil { + tun.Close() + return nil, err + } + + return tun, nil +} + +func (tun *nativeTun) Name() (string, error) { + + var ifName struct { + name [16]byte + } + ifNameSize := uintptr(16) + + _, _, errno := unix.Syscall6( + unix.SYS_GETSOCKOPT, + uintptr(tun.fd.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) + } + + tun.name = string(ifName.name[:ifNameSize-1]) + return tun.name, nil +} + +func (tun *nativeTun) File() *os.File { + return tun.fd +} + +func (tun *nativeTun) Events() chan TUNEvent { + return tun.events +} + +func (tun *nativeTun) doRead(buff []byte, offset int) (int, error) { + select { + case err := <-tun.errors: + return 0, err + default: + buff := buff[offset-4:] + n, err := tun.fd.Read(buff[:]) + if n < 4 { + return 0, err + } + return n - 4, err + } +} + +func (tun *nativeTun) Read(buff []byte, offset int) (int, error) { + for { + n, err := tun.doRead(buff, offset) + if err == nil || !rwcancel.ErrorIsEAGAIN(err) { + return n, err + } + if !tun.rwcancel.ReadyRead() { + return 0, errors.New("tun device closed") + } + } +} + +func (tun *nativeTun) Write(buff []byte, offset int) (int, error) { + + // reserve space for header + + buff = buff[offset-4:] + + // add packet information header + + buff[0] = 0x00 + buff[1] = 0x00 + buff[2] = 0x00 + + if buff[4]>>4 == ipv6.Version { + buff[3] = unix.AF_INET6 + } else { + buff[3] = unix.AF_INET + } + + // write + + return tun.fd.Write(buff) +} + +func (tun *nativeTun) Close() error { + var err3 error + err1 := tun.rwcancel.Cancel() + err2 := tun.fd.Close() + if tun.routeSocket != -1 { + unix.Shutdown(tun.routeSocket, unix.SHUT_RDWR) + err3 = unix.Close(tun.routeSocket) + tun.routeSocket = -1 + } else if tun.events != nil { + close(tun.events) + } + if err1 != nil { + return err1 + } + if err2 != nil { + return err2 + } + return err3 +} + +func (tun *nativeTun) setMTU(n int) error { + + // open datagram socket + + var fd int + + fd, err := unix.Socket( + unix.AF_INET, + unix.SOCK_DGRAM, + 0, + ) + + if err != nil { + return err + } + + defer unix.Close(fd) + + // do ioctl call + + var ifr [32]byte + copy(ifr[:], tun.name) + *(*uint32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])) = uint32(n) + _, _, errno := unix.Syscall( + unix.SYS_IOCTL, + uintptr(fd), + uintptr(unix.SIOCSIFMTU), + uintptr(unsafe.Pointer(&ifr[0])), + ) + + if errno != 0 { + return fmt.Errorf("failed to set MTU on %s", tun.name) + } + + return nil +} + +func (tun *nativeTun) MTU() (int, error) { + + // open datagram socket + + fd, err := unix.Socket( + unix.AF_INET, + unix.SOCK_DGRAM, + 0, + ) + + if err != nil { + return 0, err + } + + defer unix.Close(fd) + + // do ioctl call + + var ifr [64]byte + copy(ifr[:], tun.name) + _, _, errno := unix.Syscall( + unix.SYS_IOCTL, + uintptr(fd), + uintptr(unix.SIOCGIFMTU), + uintptr(unsafe.Pointer(&ifr[0])), + ) + if errno != 0 { + return 0, fmt.Errorf("failed to get MTU on %s", tun.name) + } + + return int(*(*int32)(unsafe.Pointer(&ifr[16]))), nil +} diff --git a/tun/tun_freebsd.go b/tun/tun_freebsd.go new file mode 100644 index 0000000..b4cf57d --- /dev/null +++ b/tun/tun_freebsd.go @@ -0,0 +1,518 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2017-2018 Jason A. Donenfeld . All Rights Reserved. + */ + +package tun + +import ( + "../rwcancel" + "bytes" + "errors" + "fmt" + "golang.org/x/net/ipv6" + "golang.org/x/sys/unix" + "net" + "os" + "unsafe" +) + +// _TUNSIFHEAD, value derived from sys/net/{if_tun,ioccom}.h +// const _TUNSIFHEAD = ((0x80000000) | (((4) & ((1 << 13) - 1) ) << 16) | (uint32(byte('t')) << 8) | (96)) +const _TUNSIFHEAD = 0x80047460 +const _TUNSIFMODE = 0x8004745e +const _TUNSIFPID = 0x2000745f + +// Iface status string max len +const _IFSTATMAX = 800 + +const SIZEOF_UINTPTR = 4 << (^uintptr(0) >> 32 & 1) + +// structure for iface requests with a pointer +type ifreq_ptr struct { + Name [unix.IFNAMSIZ]byte + Data uintptr + Pad0 [24 - SIZEOF_UINTPTR]byte +} + +// Structure for iface mtu get/set ioctls +type ifreq_mtu struct { + Name [unix.IFNAMSIZ]byte + MTU uint32 + Pad0 [12]byte +} + +// Structure for interface status request ioctl +type ifstat struct { + IfsName [unix.IFNAMSIZ]byte + Ascii [_IFSTATMAX]byte +} + +type nativeTun struct { + name string + fd *os.File + rwcancel *rwcancel.RWCancel + events chan TUNEvent + errors chan error + routeSocket int +} + +func (tun *nativeTun) routineRouteListener(tunIfindex int) { + var ( + statusUp bool + statusMTU int + ) + + defer close(tun.events) + + data := make([]byte, os.Getpagesize()) + for { + n, err := unix.Read(tun.routeSocket, data) + if err != nil { + tun.errors <- err + return + } + + if n < 14 { + continue + } + + if data[3 /* type */] != unix.RTM_IFINFO { + continue + } + ifindex := int(*(*uint16)(unsafe.Pointer(&data[12 /* ifindex */]))) + if ifindex != tunIfindex { + continue + } + + iface, err := net.InterfaceByIndex(ifindex) + if err != nil { + tun.errors <- err + return + } + + // Up / Down event + up := (iface.Flags & net.FlagUp) != 0 + if up != statusUp && up { + tun.events <- TUNEventUp + } + if up != statusUp && !up { + tun.events <- TUNEventDown + } + statusUp = up + + // MTU changes + if iface.MTU != statusMTU { + tun.events <- TUNEventMTUUpdate + } + statusMTU = iface.MTU + } +} + +func tunName(fd uintptr) (string, error) { + //Terrible hack to make up for freebsd not having a TUNGIFNAME + + //First, make sure the tun pid matches this proc's pid + _, _, errno := unix.Syscall( + unix.SYS_IOCTL, + uintptr(fd), + uintptr(_TUNSIFPID), + uintptr(0), + ) + + if errno != 0 { + return "", fmt.Errorf("failed to set tun device PID: %s", errno.Error()) + } + + // Open iface control socket + + confd, err := unix.Socket( + unix.AF_INET, + unix.SOCK_DGRAM, + 0, + ) + + if err != nil { + return "", err + } + + defer unix.Close(confd) + + procPid := os.Getpid() + + //Try to find interface with matching PID + for i := 1; ; i++ { + iface, _ := net.InterfaceByIndex(i) + if err != nil || iface == nil { + break + } + + // Structs for getting data in and out of SIOCGIFSTATUS ioctl + var ifstatus ifstat + copy(ifstatus.IfsName[:], iface.Name) + + // Make the syscall to get the status string + _, _, errno := unix.Syscall( + unix.SYS_IOCTL, + uintptr(confd), + uintptr(unix.SIOCGIFSTATUS), + uintptr(unsafe.Pointer(&ifstatus)), + ) + + if errno != 0 { + continue + } + + nullStr := ifstatus.Ascii[:] + i := bytes.IndexByte(nullStr, 0) + if i < 1 { + continue + } + statStr := string(nullStr[:i]) + var pidNum int = 0 + + // Finally get the owning PID + // Format string taken from sys/net/if_tun.c + _, err := fmt.Sscanf(statStr, "\tOpened by PID %d\n", &pidNum) + if err != nil { + continue + } + + if pidNum == procPid { + return iface.Name, nil + } + } + + return "", nil +} + +// Destroy a named system interface +func tunDestroy(name string) error { + // open control socket + var fd int + + fd, err := unix.Socket( + unix.AF_INET, + unix.SOCK_DGRAM, + 0, + ) + + if err != nil { + return err + } + + defer unix.Close(fd) + + // do ioctl call + + var ifr [32]byte + copy(ifr[:], name) + _, _, errno := unix.Syscall( + unix.SYS_IOCTL, + uintptr(fd), + uintptr(unix.SIOCIFDESTROY), + uintptr(unsafe.Pointer(&ifr[0])), + ) + + if errno != 0 { + return fmt.Errorf("failed to destroy interface %s: %s", name, errno.Error()) + } + + return nil +} + +func CreateTUN(name string, mtu int) (TUNDevice, error) { + if len(name) > unix.IFNAMSIZ-1 { + return nil, errors.New("interface name too long") + } + + // See if interface already exists + iface, _ := net.InterfaceByName(name) + if iface != nil { + return nil, fmt.Errorf("interface %s already exists", name) + } + + tunfile, err := os.OpenFile("/dev/tun", unix.O_RDWR, 0) + + if err != nil { + return nil, err + } + tunfd := tunfile.Fd() + assignedName, err := tunName(tunfd) + if err != nil { + tunfile.Close() + return nil, err + } + + // 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)), + ) + + if errno != 0 { + return nil, fmt.Errorf("error %s", errno.Error()) + } + + // Set TUN iface to broadcast mode. TUN inferfaces on freebsd come up in point to point by default + ifmodemode := unix.IFF_BROADCAST + _, _, errno = unix.Syscall( + unix.SYS_IOCTL, + uintptr(tunfd), + uintptr(_TUNSIFMODE), + uintptr(unsafe.Pointer(&ifmodemode)), + ) + + if errno != 0 { + return nil, fmt.Errorf("error %s", errno.Error()) + } + + // Rename tun interface + + // Open control socket + confd, err := unix.Socket( + unix.AF_INET, + unix.SOCK_DGRAM, + 0, + ) + + if err != nil { + return nil, err + } + + defer unix.Close(confd) + + // set up struct for iface rename + var newnp [unix.IFNAMSIZ]byte + copy(newnp[:], name) + + var ifr ifreq_ptr + copy(ifr.Name[:], assignedName) + ifr.Data = uintptr(unsafe.Pointer(&newnp[0])) + + //do actual ioctl to rename iface + _, _, errno = unix.Syscall( + unix.SYS_IOCTL, + uintptr(confd), + uintptr(unix.SIOCSIFNAME), + uintptr(unsafe.Pointer(&ifr)), + ) + if errno != 0 { + tunfile.Close() + tunDestroy(name) + return nil, fmt.Errorf("failed to rename %s to %s: %s", assignedName, name, errno.Error()) + } + + return CreateTUNFromFile(tunfile, mtu) +} + +func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) { + + tun := &nativeTun{ + fd: file, + events: make(chan TUNEvent, 10), + errors: make(chan error, 1), + } + + name, err := tun.Name() + if err != nil { + tun.fd.Close() + return nil, err + } + + tunIfindex, err := func() (int, error) { + iface, err := net.InterfaceByName(name) + if err != nil { + return -1, err + } + return iface.Index, nil + }() + if err != nil { + tun.fd.Close() + return nil, err + } + + tun.rwcancel, err = rwcancel.NewRWCancel(int(file.Fd())) + if err != nil { + tun.fd.Close() + return nil, err + } + + tun.routeSocket, err = unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC) + if err != nil { + tun.fd.Close() + return nil, err + } + + go tun.routineRouteListener(tunIfindex) + + err = tun.setMTU(mtu) + if err != nil { + tun.Close() + return nil, err + } + + return tun, nil +} + +func (tun *nativeTun) Name() (string, error) { + name, err := tunName(tun.fd.Fd()) + if err != nil { + return "", err + } + tun.name = name + return name, nil +} + +func (tun *nativeTun) File() *os.File { + return tun.fd +} + +func (tun *nativeTun) Events() chan TUNEvent { + return tun.events +} + +func (tun *nativeTun) doRead(buff []byte, offset int) (int, error) { + select { + case err := <-tun.errors: + return 0, err + default: + buff := buff[offset-4:] + n, err := tun.fd.Read(buff[:]) + if n < 4 { + return 0, err + } + return n - 4, err + } +} + +func (tun *nativeTun) Read(buff []byte, offset int) (int, error) { + for { + n, err := tun.doRead(buff, offset) + if err == nil || !rwcancel.ErrorIsEAGAIN(err) { + return n, err + } + if !tun.rwcancel.ReadyRead() { + return 0, errors.New("tun device closed") + } + } +} + +func (tun *nativeTun) Write(buff []byte, offset int) (int, error) { + + // reserve space for header + + buff = buff[offset-4:] + + // add packet information header + + buff[0] = 0x00 + buff[1] = 0x00 + buff[2] = 0x00 + + if buff[4]>>4 == ipv6.Version { + buff[3] = unix.AF_INET6 + } else { + buff[3] = unix.AF_INET + } + + // write + + return tun.fd.Write(buff) +} + +func (tun *nativeTun) Close() error { + var err4 error + err1 := tun.rwcancel.Cancel() + err2 := tun.fd.Close() + err3 := tunDestroy(tun.name) + if tun.routeSocket != -1 { + unix.Shutdown(tun.routeSocket, unix.SHUT_RDWR) + err4 = unix.Close(tun.routeSocket) + tun.routeSocket = -1 + } else if tun.events != nil { + close(tun.events) + } + if err1 != nil { + return err1 + } + if err2 != nil { + return err2 + } + if err3 != nil { + return err3 + } + return err4 +} + +func (tun *nativeTun) setMTU(n int) error { + // open datagram socket + + var fd int + + fd, err := unix.Socket( + unix.AF_INET, + unix.SOCK_DGRAM, + 0, + ) + + if err != nil { + return err + } + + defer unix.Close(fd) + + // do ioctl call + + var ifr ifreq_mtu + copy(ifr.Name[:], tun.name) + ifr.MTU = uint32(n) + + _, _, errno := unix.Syscall( + unix.SYS_IOCTL, + uintptr(fd), + uintptr(unix.SIOCSIFMTU), + uintptr(unsafe.Pointer(&ifr)), + ) + + if errno != 0 { + return fmt.Errorf("failed to set MTU on %s", tun.name) + } + + return nil +} + +func (tun *nativeTun) MTU() (int, error) { + // open datagram socket + + fd, err := unix.Socket( + unix.AF_INET, + unix.SOCK_DGRAM, + 0, + ) + + if err != nil { + return 0, err + } + + defer unix.Close(fd) + + // do ioctl call + var ifr ifreq_mtu + copy(ifr.Name[:], tun.name) + + _, _, errno := unix.Syscall( + unix.SYS_IOCTL, + uintptr(fd), + uintptr(unix.SIOCGIFMTU), + uintptr(unsafe.Pointer(&ifr)), + ) + if errno != 0 { + return 0, fmt.Errorf("failed to get MTU on %s", tun.name) + } + + return int(*(*int32)(unsafe.Pointer(&ifr.MTU))), nil +} diff --git a/tun/tun_linux.go b/tun/tun_linux.go new file mode 100644 index 0000000..acfb942 --- /dev/null +++ b/tun/tun_linux.go @@ -0,0 +1,468 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2017-2018 Jason A. Donenfeld . All Rights Reserved. + * Copyright (C) 2017-2018 Mathias N. Hall-Andersen . + */ + +/* Copyright 2018 Jason A. Donenfeld . All Rights Reserved. */ + +package tun + +/* Implementation of the TUN device interface for linux + */ + +import ( + "../rwcancel" + "bytes" + "errors" + "fmt" + "golang.org/x/net/ipv6" + "golang.org/x/sys/unix" + "net" + "os" + "strconv" + "sync" + "time" + "unsafe" +) + +const ( + cloneDevicePath = "/dev/net/tun" + ifReqSize = unix.IFNAMSIZ + 64 +) + +type nativeTun struct { + fd *os.File + fdCancel *rwcancel.RWCancel + index int32 // if index + name string // name of interface + errors chan error // async error handling + events chan TUNEvent // device related events + nopi bool // the device was pased IFF_NO_PI + netlinkSock int + netlinkCancel *rwcancel.RWCancel + hackListenerClosed sync.Mutex + statusListenersShutdown chan struct{} +} + +func (tun *nativeTun) File() *os.File { + return tun.fd +} + +func (tun *nativeTun) routineHackListener() { + defer tun.hackListenerClosed.Unlock() + /* 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.Fd()) + for { + _, err := unix.Write(fd, nil) + switch err { + case unix.EINVAL: + tun.events <- TUNEventUp + case unix.EIO: + tun.events <- TUNEventDown + default: + return + } + select { + case <-time.After(time.Second): + case <-tun.statusListenersShutdown: + return + } + } +} + +func createNetlinkSocket() (int, error) { + sock, err := unix.Socket(unix.AF_NETLINK, unix.SOCK_RAW, unix.NETLINK_ROUTE) + if err != nil { + return -1, err + } + saddr := &unix.SockaddrNetlink{ + Family: unix.AF_NETLINK, + Groups: uint32((1 << (unix.RTNLGRP_LINK - 1)) | (1 << (unix.RTNLGRP_IPV4_IFADDR - 1)) | (1 << (unix.RTNLGRP_IPV6_IFADDR - 1))), + } + err = unix.Bind(sock, saddr) + if err != nil { + return -1, err + } + return sock, nil +} + +func (tun *nativeTun) routineNetlinkListener() { + defer func() { + unix.Close(tun.netlinkSock) + tun.hackListenerClosed.Lock() + close(tun.events) + }() + + for msg := make([]byte, 1<<16); ; { + + var err error + var msgn int + for { + msgn, _, _, _, err = unix.Recvmsg(tun.netlinkSock, msg[:], nil, 0) + if err == nil || !rwcancel.ErrorIsEAGAIN(err) { + break + } + if !tun.netlinkCancel.ReadyRead() { + tun.errors <- fmt.Errorf("netlink socket closed: %s", err.Error()) + return + } + } + if err != nil { + tun.errors <- fmt.Errorf("failed to receive netlink message: %s", err.Error()) + return + } + + select { + case <-tun.statusListenersShutdown: + return + default: + } + + for remain := msg[:msgn]; len(remain) >= unix.SizeofNlMsghdr; { + + hdr := *(*unix.NlMsghdr)(unsafe.Pointer(&remain[0])) + + if int(hdr.Len) > len(remain) { + break + } + + switch hdr.Type { + case unix.NLMSG_DONE: + remain = []byte{} + + case unix.RTM_NEWLINK: + info := *(*unix.IfInfomsg)(unsafe.Pointer(&remain[unix.SizeofNlMsghdr])) + remain = remain[hdr.Len:] + + if info.Index != tun.index { + // not our interface + continue + } + + if info.Flags&unix.IFF_RUNNING != 0 { + tun.events <- TUNEventUp + } + + if info.Flags&unix.IFF_RUNNING == 0 { + tun.events <- TUNEventDown + } + + tun.events <- TUNEventMTUUpdate + + default: + remain = remain[hdr.Len:] + } + } + } +} + +func (tun *nativeTun) isUp() (bool, error) { + inter, err := net.InterfaceByName(tun.name) + return inter.Flags&net.FlagUp != 0, err +} + +func getDummySock() (int, error) { + return unix.Socket( + unix.AF_INET, + unix.SOCK_DGRAM, + 0, + ) +} + +func getIFIndex(name string) (int32, error) { + fd, err := getDummySock() + if err != nil { + return 0, err + } + + defer unix.Close(fd) + + var ifr [ifReqSize]byte + copy(ifr[:], name) + _, _, errno := unix.Syscall( + unix.SYS_IOCTL, + uintptr(fd), + uintptr(unix.SIOCGIFINDEX), + uintptr(unsafe.Pointer(&ifr[0])), + ) + + if errno != 0 { + return 0, errno + } + + return *(*int32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])), nil +} + +func (tun *nativeTun) setMTU(n int) error { + + // open datagram socket + + fd, err := unix.Socket( + unix.AF_INET, + unix.SOCK_DGRAM, + 0, + ) + + if err != nil { + return err + } + + defer unix.Close(fd) + + // do ioctl call + + var ifr [ifReqSize]byte + copy(ifr[:], tun.name) + *(*uint32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])) = uint32(n) + _, _, errno := unix.Syscall( + unix.SYS_IOCTL, + uintptr(fd), + uintptr(unix.SIOCSIFMTU), + uintptr(unsafe.Pointer(&ifr[0])), + ) + + if errno != 0 { + return errors.New("failed to set MTU of TUN device") + } + + return nil +} + +func (tun *nativeTun) MTU() (int, error) { + + // open datagram socket + + fd, err := unix.Socket( + unix.AF_INET, + unix.SOCK_DGRAM, + 0, + ) + + if err != nil { + return 0, err + } + + defer unix.Close(fd) + + // do ioctl call + + var ifr [ifReqSize]byte + copy(ifr[:], tun.name) + _, _, errno := unix.Syscall( + unix.SYS_IOCTL, + uintptr(fd), + uintptr(unix.SIOCGIFMTU), + uintptr(unsafe.Pointer(&ifr[0])), + ) + if errno != 0 { + return 0, errors.New("failed to get MTU of TUN device: " + strconv.FormatInt(int64(errno), 10)) + } + + return int(*(*int32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ]))), nil +} + +func (tun *nativeTun) Name() (string, error) { + + var ifr [ifReqSize]byte + _, _, errno := unix.Syscall( + unix.SYS_IOCTL, + tun.fd.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)) + } + nullStr := ifr[:] + i := bytes.IndexByte(nullStr, 0) + if i != -1 { + nullStr = nullStr[:i] + } + tun.name = string(nullStr) + return tun.name, nil +} + +func (tun *nativeTun) Write(buff []byte, offset int) (int, error) { + + if tun.nopi { + buff = buff[offset:] + } else { + // reserve space for header + + buff = buff[offset-4:] + + // add packet information header + + buff[0] = 0x00 + buff[1] = 0x00 + + if buff[4]>>4 == ipv6.Version { + buff[2] = 0x86 + buff[3] = 0xdd + } else { + buff[2] = 0x08 + buff[3] = 0x00 + } + } + + // write + + return tun.fd.Write(buff) +} + +func (tun *nativeTun) doRead(buff []byte, offset int) (int, error) { + select { + case err := <-tun.errors: + return 0, err + default: + if tun.nopi { + return tun.fd.Read(buff[offset:]) + } else { + buff := buff[offset-4:] + n, err := tun.fd.Read(buff[:]) + if n < 4 { + return 0, err + } + return n - 4, err + } + } +} + +func (tun *nativeTun) Read(buff []byte, offset int) (int, error) { + for { + n, err := tun.doRead(buff, offset) + if err == nil || !rwcancel.ErrorIsEAGAIN(err) { + return n, err + } + if !tun.fdCancel.ReadyRead() { + return 0, errors.New("tun device closed") + } + } +} + +func (tun *nativeTun) Events() chan TUNEvent { + return tun.events +} + +func (tun *nativeTun) Close() error { + var err1 error + if tun.statusListenersShutdown != nil { + close(tun.statusListenersShutdown) + if tun.netlinkCancel != nil { + err1 = tun.netlinkCancel.Cancel() + } + } else if tun.events != nil { + close(tun.events) + } + err2 := tun.fd.Close() + err3 := tun.fdCancel.Cancel() + + if err1 != nil { + return err1 + } + if err2 != nil { + return err2 + } + return err3 +} + +func CreateTUN(name string, mtu int) (TUNDevice, error) { + + // open clone device + + // HACK: we open it as a raw Fd first, so that f.nonblock=false + // when we make it into a file object. + nfd, err := unix.Open(cloneDevicePath, os.O_RDWR, 0) + if err != nil { + return nil, err + } + + err = unix.SetNonblock(nfd, true) + if err != nil { + return nil, err + } + + fd := os.NewFile(uintptr(nfd), cloneDevicePath) + if err != nil { + return nil, err + } + + // create new device + + var ifr [ifReqSize]byte + var flags uint16 = unix.IFF_TUN // | unix.IFF_NO_PI (disabled for TUN status hack) + nameBytes := []byte(name) + if len(nameBytes) >= unix.IFNAMSIZ { + return nil, errors.New("interface name too long") + } + copy(ifr[:], nameBytes) + *(*uint16)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])) = flags + + _, _, errno := unix.Syscall( + unix.SYS_IOCTL, + fd.Fd(), + uintptr(unix.TUNSETIFF), + uintptr(unsafe.Pointer(&ifr[0])), + ) + if errno != 0 { + return nil, errno + } + + return CreateTUNFromFile(fd, mtu) +} + +func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) { + tun := &nativeTun{ + fd: file, + events: make(chan TUNEvent, 5), + errors: make(chan error, 5), + statusListenersShutdown: make(chan struct{}), + nopi: false, + } + var err error + + tun.fdCancel, err = rwcancel.NewRWCancel(int(file.Fd())) + if err != nil { + tun.fd.Close() + return nil, err + } + + _, err = tun.Name() + if err != nil { + tun.fd.Close() + return nil, err + } + + // start event listener + + tun.index, err = getIFIndex(tun.name) + if err != nil { + return nil, err + } + + tun.netlinkSock, err = createNetlinkSocket() + if err != nil { + tun.fd.Close() + return nil, err + } + tun.netlinkCancel, err = rwcancel.NewRWCancel(tun.netlinkSock) + if err != nil { + tun.fd.Close() + return nil, err + } + + tun.hackListenerClosed.Lock() + go tun.routineNetlinkListener() + go tun.routineHackListener() // cross namespace + + err = tun.setMTU(mtu) + if err != nil { + tun.Close() + return nil, err + } + + return tun, nil +} diff --git a/tun/tun_openbsd.go b/tun/tun_openbsd.go new file mode 100644 index 0000000..74b25e5 --- /dev/null +++ b/tun/tun_openbsd.go @@ -0,0 +1,359 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2017-2018 Jason A. Donenfeld . All Rights Reserved. + */ + +package tun + +import ( + "../rwcancel" + "errors" + "fmt" + "golang.org/x/net/ipv6" + "golang.org/x/sys/unix" + "io/ioutil" + "net" + "os" + "syscall" + "unsafe" +) + +// Structure for iface mtu get/set ioctls +type ifreq_mtu struct { + Name [unix.IFNAMSIZ]byte + MTU uint32 + Pad0 [12]byte +} + +const _TUNSIFMODE = 0x8004745d + +type nativeTun struct { + name string + fd *os.File + rwcancel *rwcancel.RWCancel + events chan TUNEvent + errors chan error + routeSocket int +} + +func (tun *nativeTun) routineRouteListener(tunIfindex int) { + var ( + statusUp bool + statusMTU int + ) + + defer close(tun.events) + + data := make([]byte, os.Getpagesize()) + for { + n, err := unix.Read(tun.routeSocket, data) + if err != nil { + tun.errors <- err + return + } + + if n < 8 { + continue + } + + if data[3 /* type */] != unix.RTM_IFINFO { + continue + } + ifindex := int(*(*uint16)(unsafe.Pointer(&data[6 /* ifindex */]))) + if ifindex != tunIfindex { + continue + } + + iface, err := net.InterfaceByIndex(ifindex) + if err != nil { + tun.errors <- err + return + } + + // Up / Down event + up := (iface.Flags & net.FlagUp) != 0 + if up != statusUp && up { + tun.events <- TUNEventUp + } + if up != statusUp && !up { + tun.events <- TUNEventDown + } + statusUp = up + + // MTU changes + if iface.MTU != statusMTU { + tun.events <- TUNEventMTUUpdate + } + statusMTU = iface.MTU + } +} + +func errorIsEBUSY(err error) bool { + if pe, ok := err.(*os.PathError); ok { + if errno, ok := pe.Err.(syscall.Errno); ok && errno == syscall.EBUSY { + return true + } + } + if errno, ok := err.(syscall.Errno); ok && errno == syscall.EBUSY { + return true + } + return false +} + +func CreateTUN(name string, mtu int) (TUNDevice, error) { + ifIndex := -1 + if name != "tun" { + _, err := fmt.Sscanf(name, "tun%d", &ifIndex) + if err != nil || ifIndex < 0 { + return nil, fmt.Errorf("Interface name must be tun[0-9]*") + } + } + + var tunfile *os.File + var err error + + if ifIndex != -1 { + tunfile, err = os.OpenFile(fmt.Sprintf("/dev/tun%d", ifIndex), unix.O_RDWR, 0) + } else { + for ifIndex = 0; ifIndex < 256; ifIndex += 1 { + tunfile, err = os.OpenFile(fmt.Sprintf("/dev/tun%d", ifIndex), unix.O_RDWR, 0) + if err == nil || !errorIsEBUSY(err) { + break + } + } + } + + if err != nil { + return nil, err + } + + // Set TUN iface to broadcast mode + ifmodemode := unix.IFF_BROADCAST + _, _, errno := unix.Syscall( + unix.SYS_IOCTL, + uintptr(tunfile.Fd()), + uintptr(_TUNSIFMODE), + uintptr(unsafe.Pointer(&ifmodemode)), + ) + + if errno != 0 { + return nil, fmt.Errorf("error %s", errno.Error()) + } + + tun, err := CreateTUNFromFile(tunfile, mtu) + + if err == nil && name == "tun" { + fname := os.Getenv("WG_TUN_NAME_FILE") + if fname != "" { + ioutil.WriteFile(fname, []byte(tun.(*nativeTun).name+"\n"), 0400) + } + } + + return tun, err +} + +func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) { + + tun := &nativeTun{ + fd: file, + events: make(chan TUNEvent, 10), + errors: make(chan error, 1), + } + + name, err := tun.Name() + if err != nil { + tun.fd.Close() + return nil, err + } + + tunIfindex, err := func() (int, error) { + iface, err := net.InterfaceByName(name) + if err != nil { + return -1, err + } + return iface.Index, nil + }() + if err != nil { + tun.fd.Close() + return nil, err + } + + tun.rwcancel, err = rwcancel.NewRWCancel(int(file.Fd())) + if err != nil { + tun.fd.Close() + return nil, err + } + + tun.routeSocket, err = unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC) + if err != nil { + tun.fd.Close() + return nil, err + } + + go tun.routineRouteListener(tunIfindex) + + err = tun.setMTU(mtu) + if err != nil { + tun.Close() + return nil, err + } + + return tun, nil +} + +func (tun *nativeTun) Name() (string, error) { + gostat, err := tun.fd.Stat() + if err != nil { + tun.name = "" + return "", err + } + stat := gostat.Sys().(*syscall.Stat_t) + tun.name = fmt.Sprintf("tun%d", stat.Rdev%256) + return tun.name, nil +} + +func (tun *nativeTun) File() *os.File { + return tun.fd +} + +func (tun *nativeTun) Events() chan TUNEvent { + return tun.events +} + +func (tun *nativeTun) doRead(buff []byte, offset int) (int, error) { + select { + case err := <-tun.errors: + return 0, err + default: + buff := buff[offset-4:] + n, err := tun.fd.Read(buff[:]) + if n < 4 { + return 0, err + } + return n - 4, err + } +} + +func (tun *nativeTun) Read(buff []byte, offset int) (int, error) { + for { + n, err := tun.doRead(buff, offset) + if err == nil || !rwcancel.ErrorIsEAGAIN(err) { + return n, err + } + if !tun.rwcancel.ReadyRead() { + return 0, errors.New("tun device closed") + } + } +} + +func (tun *nativeTun) Write(buff []byte, offset int) (int, error) { + + // reserve space for header + + buff = buff[offset-4:] + + // add packet information header + + buff[0] = 0x00 + buff[1] = 0x00 + buff[2] = 0x00 + + if buff[4]>>4 == ipv6.Version { + buff[3] = unix.AF_INET6 + } else { + buff[3] = unix.AF_INET + } + + // write + + return tun.fd.Write(buff) +} + +func (tun *nativeTun) Close() error { + var err3 error + err1 := tun.rwcancel.Cancel() + err2 := tun.fd.Close() + if tun.routeSocket != -1 { + unix.Shutdown(tun.routeSocket, unix.SHUT_RDWR) + err3 = unix.Close(tun.routeSocket) + tun.routeSocket = -1 + } else if tun.events != nil { + close(tun.events) + } + if err1 != nil { + return err1 + } + if err2 != nil { + return err2 + } + return err3 +} + +func (tun *nativeTun) setMTU(n int) error { + // open datagram socket + + var fd int + + fd, err := unix.Socket( + unix.AF_INET, + unix.SOCK_DGRAM, + 0, + ) + + if err != nil { + return err + } + + defer unix.Close(fd) + + // do ioctl call + + var ifr ifreq_mtu + copy(ifr.Name[:], tun.name) + ifr.MTU = uint32(n) + + _, _, errno := unix.Syscall( + unix.SYS_IOCTL, + uintptr(fd), + uintptr(unix.SIOCSIFMTU), + uintptr(unsafe.Pointer(&ifr)), + ) + + if errno != 0 { + return fmt.Errorf("failed to set MTU on %s", tun.name) + } + + return nil +} + +func (tun *nativeTun) MTU() (int, error) { + // open datagram socket + + fd, err := unix.Socket( + unix.AF_INET, + unix.SOCK_DGRAM, + 0, + ) + + if err != nil { + return 0, err + } + + defer unix.Close(fd) + + // do ioctl call + var ifr ifreq_mtu + copy(ifr.Name[:], tun.name) + + _, _, errno := unix.Syscall( + unix.SYS_IOCTL, + uintptr(fd), + uintptr(unix.SIOCGIFMTU), + uintptr(unsafe.Pointer(&ifr)), + ) + if errno != 0 { + return 0, fmt.Errorf("failed to get MTU on %s", tun.name) + } + + return int(*(*int32)(unsafe.Pointer(&ifr.MTU))), nil +} diff --git a/tun_darwin.go b/tun_darwin.go deleted file mode 100644 index e5a01a7..0000000 --- a/tun_darwin.go +++ /dev/null @@ -1,384 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 - * - * Copyright (C) 2017-2018 Jason A. Donenfeld . All Rights Reserved. - * Copyright (C) 2017-2018 Mathias N. Hall-Andersen . - */ - -package main - -import ( - "./rwcancel" - "encoding/binary" - "errors" - "fmt" - "golang.org/x/net/ipv6" - "golang.org/x/sys/unix" - "io/ioutil" - "net" - "os" - "unsafe" -) - -const utunControlName = "com.apple.net.utun_control" - -// _CTLIOCGINFO value derived from /usr/include/sys/{kern_control,ioccom}.h -const _CTLIOCGINFO = (0x40000000 | 0x80000000) | ((100 & 0x1fff) << 16) | uint32(byte('N'))<<8 | 3 - -// sockaddr_ctl specifeid in /usr/include/sys/kern_control.h -type sockaddrCtl struct { - scLen uint8 - scFamily uint8 - ssSysaddr uint16 - scID uint32 - scUnit uint32 - scReserved [5]uint32 -} - -type NativeTun struct { - name string - fd *os.File - rwcancel *rwcancel.RWCancel - events chan TUNEvent - errors chan error - routeSocket int -} - -var sockaddrCtlSize uintptr = 32 - -func (tun *NativeTun) RoutineRouteListener(tunIfindex int) { - var ( - statusUp bool - statusMTU int - ) - - defer close(tun.events) - - data := make([]byte, os.Getpagesize()) - for { - n, err := unix.Read(tun.routeSocket, data) - if err != nil { - tun.errors <- err - return - } - - if n < 14 { - continue - } - - if data[3 /* type */] != unix.RTM_IFINFO { - continue - } - ifindex := int(*(*uint16)(unsafe.Pointer(&data[12 /* ifindex */]))) - if ifindex != tunIfindex { - continue - } - - iface, err := net.InterfaceByIndex(ifindex) - if err != nil { - tun.errors <- err - return - } - - // Up / Down event - up := (iface.Flags & net.FlagUp) != 0 - if up != statusUp && up { - tun.events <- TUNEventUp - } - if up != statusUp && !up { - tun.events <- TUNEventDown - } - statusUp = up - - // MTU changes - if iface.MTU != statusMTU { - tun.events <- TUNEventMTUUpdate - } - statusMTU = iface.MTU - } -} - -func CreateTUN(name string) (TUNDevice, error) { - ifIndex := -1 - if name != "utun" { - _, err := fmt.Sscanf(name, "utun%d", &ifIndex) - if err != nil || ifIndex < 0 { - return nil, fmt.Errorf("Interface name must be utun[0-9]*") - } - } - - fd, err := unix.Socket(unix.AF_SYSTEM, unix.SOCK_DGRAM, 2) - - if err != nil { - return nil, err - } - - var ctlInfo = &struct { - ctlID uint32 - ctlName [96]byte - }{} - - copy(ctlInfo.ctlName[:], []byte(utunControlName)) - - _, _, errno := unix.Syscall( - unix.SYS_IOCTL, - uintptr(fd), - uintptr(_CTLIOCGINFO), - uintptr(unsafe.Pointer(ctlInfo)), - ) - - if errno != 0 { - return nil, fmt.Errorf("_CTLIOCGINFO: %v", errno) - } - - sc := sockaddrCtl{ - scLen: uint8(sockaddrCtlSize), - scFamily: unix.AF_SYSTEM, - ssSysaddr: 2, - scID: ctlInfo.ctlID, - scUnit: uint32(ifIndex) + 1, - } - - scPointer := unsafe.Pointer(&sc) - - _, _, errno = unix.RawSyscall( - unix.SYS_CONNECT, - uintptr(fd), - uintptr(scPointer), - uintptr(sockaddrCtlSize), - ) - - if errno != 0 { - return nil, fmt.Errorf("SYS_CONNECT: %v", errno) - } - - tun, err := CreateTUNFromFile(os.NewFile(uintptr(fd), "")) - - if err == nil && name == "utun" { - fname := os.Getenv("WG_TUN_NAME_FILE") - if fname != "" { - ioutil.WriteFile(fname, []byte(tun.(*NativeTun).name+"\n"), 0400) - } - } - - return tun, err -} - -func CreateTUNFromFile(file *os.File) (TUNDevice, error) { - - tun := &NativeTun{ - fd: file, - events: make(chan TUNEvent, 10), - errors: make(chan error, 1), - } - - name, err := tun.Name() - if err != nil { - tun.fd.Close() - return nil, err - } - - tunIfindex, err := func() (int, error) { - iface, err := net.InterfaceByName(name) - if err != nil { - return -1, err - } - return iface.Index, nil - }() - if err != nil { - tun.fd.Close() - return nil, err - } - - tun.rwcancel, err = rwcancel.NewRWCancel(int(file.Fd())) - if err != nil { - tun.fd.Close() - return nil, err - } - - tun.routeSocket, err = unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC) - if err != nil { - tun.fd.Close() - return nil, err - } - - go tun.RoutineRouteListener(tunIfindex) - - // set default MTU - err = tun.setMTU(DefaultMTU) - if err != nil { - tun.Close() - return nil, err - } - - return tun, nil -} - -func (tun *NativeTun) Name() (string, error) { - - var ifName struct { - name [16]byte - } - ifNameSize := uintptr(16) - - _, _, errno := unix.Syscall6( - unix.SYS_GETSOCKOPT, - uintptr(tun.fd.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) - } - - tun.name = string(ifName.name[:ifNameSize-1]) - return tun.name, nil -} - -func (tun *NativeTun) File() *os.File { - return tun.fd -} - -func (tun *NativeTun) Events() chan TUNEvent { - return tun.events -} - -func (tun *NativeTun) doRead(buff []byte, offset int) (int, error) { - select { - case err := <-tun.errors: - return 0, err - default: - buff := buff[offset-4:] - n, err := tun.fd.Read(buff[:]) - if n < 4 { - return 0, err - } - return n - 4, err - } -} - -func (tun *NativeTun) Read(buff []byte, offset int) (int, error) { - for { - n, err := tun.doRead(buff, offset) - if err == nil || !rwcancel.ErrorIsEAGAIN(err) { - return n, err - } - if !tun.rwcancel.ReadyRead() { - return 0, errors.New("tun device closed") - } - } -} - -func (tun *NativeTun) Write(buff []byte, offset int) (int, error) { - - // reserve space for header - - buff = buff[offset-4:] - - // add packet information header - - buff[0] = 0x00 - buff[1] = 0x00 - buff[2] = 0x00 - - if buff[4]>>4 == ipv6.Version { - buff[3] = unix.AF_INET6 - } else { - buff[3] = unix.AF_INET - } - - // write - - return tun.fd.Write(buff) -} - -func (tun *NativeTun) Close() error { - var err3 error - err1 := tun.rwcancel.Cancel() - err2 := tun.fd.Close() - if tun.routeSocket != -1 { - unix.Shutdown(tun.routeSocket, unix.SHUT_RDWR) - err3 = unix.Close(tun.routeSocket) - tun.routeSocket = -1 - } else if tun.events != nil { - close(tun.events) - } - if err1 != nil { - return err1 - } - if err2 != nil { - return err2 - } - return err3 -} - -func (tun *NativeTun) setMTU(n int) error { - - // open datagram socket - - var fd int - - fd, err := unix.Socket( - unix.AF_INET, - unix.SOCK_DGRAM, - 0, - ) - - if err != nil { - return err - } - - defer unix.Close(fd) - - // do ioctl call - - var ifr [32]byte - copy(ifr[:], tun.name) - binary.LittleEndian.PutUint32(ifr[16:20], uint32(n)) - _, _, errno := unix.Syscall( - unix.SYS_IOCTL, - uintptr(fd), - uintptr(unix.SIOCSIFMTU), - uintptr(unsafe.Pointer(&ifr[0])), - ) - - if errno != 0 { - return fmt.Errorf("failed to set MTU on %s", tun.name) - } - - return nil -} - -func (tun *NativeTun) MTU() (int, error) { - - // open datagram socket - - fd, err := unix.Socket( - unix.AF_INET, - unix.SOCK_DGRAM, - 0, - ) - - if err != nil { - return 0, err - } - - defer unix.Close(fd) - - // do ioctl call - - var ifr [64]byte - copy(ifr[:], tun.name) - _, _, errno := unix.Syscall( - unix.SYS_IOCTL, - uintptr(fd), - uintptr(unix.SIOCGIFMTU), - uintptr(unsafe.Pointer(&ifr[0])), - ) - if errno != 0 { - return 0, fmt.Errorf("failed to get MTU on %s", tun.name) - } - - return int(*(*int32)(unsafe.Pointer(&ifr[16]))), nil -} diff --git a/tun_freebsd.go b/tun_freebsd.go deleted file mode 100644 index 5461c45..0000000 --- a/tun_freebsd.go +++ /dev/null @@ -1,519 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 - * - * Copyright (C) 2017-2018 Jason A. Donenfeld . All Rights Reserved. - */ - -package main - -import ( - "./rwcancel" - "bytes" - "errors" - "fmt" - "golang.org/x/net/ipv6" - "golang.org/x/sys/unix" - "net" - "os" - "unsafe" -) - -// _TUNSIFHEAD, value derived from sys/net/{if_tun,ioccom}.h -// const _TUNSIFHEAD = ((0x80000000) | (((4) & ((1 << 13) - 1) ) << 16) | (uint32(byte('t')) << 8) | (96)) -const _TUNSIFHEAD = 0x80047460 -const _TUNSIFMODE = 0x8004745e -const _TUNSIFPID = 0x2000745f - -// Iface status string max len -const _IFSTATMAX = 800 - -const SIZEOF_UINTPTR = 4 << (^uintptr(0) >> 32 & 1) - -// structure for iface requests with a pointer -type ifreq_ptr struct { - Name [unix.IFNAMSIZ]byte - Data uintptr - Pad0 [24 - SIZEOF_UINTPTR]byte -} - -// Structure for iface mtu get/set ioctls -type ifreq_mtu struct { - Name [unix.IFNAMSIZ]byte - MTU uint32 - Pad0 [12]byte -} - -// Structure for interface status request ioctl -type ifstat struct { - IfsName [unix.IFNAMSIZ]byte - Ascii [_IFSTATMAX]byte -} - -type NativeTun struct { - name string - fd *os.File - rwcancel *rwcancel.RWCancel - events chan TUNEvent - errors chan error - routeSocket int -} - -func (tun *NativeTun) RoutineRouteListener(tunIfindex int) { - var ( - statusUp bool - statusMTU int - ) - - defer close(tun.events) - - data := make([]byte, os.Getpagesize()) - for { - n, err := unix.Read(tun.routeSocket, data) - if err != nil { - tun.errors <- err - return - } - - if n < 14 { - continue - } - - if data[3 /* type */] != unix.RTM_IFINFO { - continue - } - ifindex := int(*(*uint16)(unsafe.Pointer(&data[12 /* ifindex */]))) - if ifindex != tunIfindex { - continue - } - - iface, err := net.InterfaceByIndex(ifindex) - if err != nil { - tun.errors <- err - return - } - - // Up / Down event - up := (iface.Flags & net.FlagUp) != 0 - if up != statusUp && up { - tun.events <- TUNEventUp - } - if up != statusUp && !up { - tun.events <- TUNEventDown - } - statusUp = up - - // MTU changes - if iface.MTU != statusMTU { - tun.events <- TUNEventMTUUpdate - } - statusMTU = iface.MTU - } -} - -func tunName(fd uintptr) (string, error) { - //Terrible hack to make up for freebsd not having a TUNGIFNAME - - //First, make sure the tun pid matches this proc's pid - _, _, errno := unix.Syscall( - unix.SYS_IOCTL, - uintptr(fd), - uintptr(_TUNSIFPID), - uintptr(0), - ) - - if errno != 0 { - return "", fmt.Errorf("failed to set tun device PID: %s", errno.Error()) - } - - // Open iface control socket - - confd, err := unix.Socket( - unix.AF_INET, - unix.SOCK_DGRAM, - 0, - ) - - if err != nil { - return "", err - } - - defer unix.Close(confd) - - procPid := os.Getpid() - - //Try to find interface with matching PID - for i := 1; ; i++ { - iface, _ := net.InterfaceByIndex(i) - if err != nil || iface == nil { - break - } - - // Structs for getting data in and out of SIOCGIFSTATUS ioctl - var ifstatus ifstat - copy(ifstatus.IfsName[:], iface.Name) - - // Make the syscall to get the status string - _, _, errno := unix.Syscall( - unix.SYS_IOCTL, - uintptr(confd), - uintptr(unix.SIOCGIFSTATUS), - uintptr(unsafe.Pointer(&ifstatus)), - ) - - if errno != 0 { - continue - } - - nullStr := ifstatus.Ascii[:] - i := bytes.IndexByte(nullStr, 0) - if i < 1 { - continue - } - statStr := string(nullStr[:i]) - var pidNum int = 0 - - // Finally get the owning PID - // Format string taken from sys/net/if_tun.c - _, err := fmt.Sscanf(statStr, "\tOpened by PID %d\n", &pidNum) - if err != nil { - continue - } - - if pidNum == procPid { - return iface.Name, nil - } - } - - return "", nil -} - -// Destroy a named system interface -func tunDestroy(name string) error { - // open control socket - var fd int - - fd, err := unix.Socket( - unix.AF_INET, - unix.SOCK_DGRAM, - 0, - ) - - if err != nil { - return err - } - - defer unix.Close(fd) - - // do ioctl call - - var ifr [32]byte - copy(ifr[:], name) - _, _, errno := unix.Syscall( - unix.SYS_IOCTL, - uintptr(fd), - uintptr(unix.SIOCIFDESTROY), - uintptr(unsafe.Pointer(&ifr[0])), - ) - - if errno != 0 { - return fmt.Errorf("failed to destroy interface %s: %s", name, errno.Error()) - } - - return nil -} - -func CreateTUN(name string) (TUNDevice, error) { - if len(name) > unix.IFNAMSIZ-1 { - return nil, errors.New("interface name too long") - } - - // See if interface already exists - iface, _ := net.InterfaceByName(name) - if iface != nil { - return nil, fmt.Errorf("interface %s already exists", name) - } - - tunfile, err := os.OpenFile("/dev/tun", unix.O_RDWR, 0) - - if err != nil { - return nil, err - } - tunfd := tunfile.Fd() - assignedName, err := tunName(tunfd) - if err != nil { - tunfile.Close() - return nil, err - } - - // 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)), - ) - - if errno != 0 { - return nil, fmt.Errorf("error %s", errno.Error()) - } - - // Set TUN iface to broadcast mode. TUN inferfaces on freebsd come up in point to point by default - ifmodemode := unix.IFF_BROADCAST - _, _, errno = unix.Syscall( - unix.SYS_IOCTL, - uintptr(tunfd), - uintptr(_TUNSIFMODE), - uintptr(unsafe.Pointer(&ifmodemode)), - ) - - if errno != 0 { - return nil, fmt.Errorf("error %s", errno.Error()) - } - - // Rename tun interface - - // Open control socket - confd, err := unix.Socket( - unix.AF_INET, - unix.SOCK_DGRAM, - 0, - ) - - if err != nil { - return nil, err - } - - defer unix.Close(confd) - - // set up struct for iface rename - var newnp [unix.IFNAMSIZ]byte - copy(newnp[:], name) - - var ifr ifreq_ptr - copy(ifr.Name[:], assignedName) - ifr.Data = uintptr(unsafe.Pointer(&newnp[0])) - - //do actual ioctl to rename iface - _, _, errno = unix.Syscall( - unix.SYS_IOCTL, - uintptr(confd), - uintptr(unix.SIOCSIFNAME), - uintptr(unsafe.Pointer(&ifr)), - ) - if errno != 0 { - tunfile.Close() - tunDestroy(name) - return nil, fmt.Errorf("failed to rename %s to %s: %s", assignedName, name, errno.Error()) - } - - return CreateTUNFromFile(tunfile) -} - -func CreateTUNFromFile(file *os.File) (TUNDevice, error) { - - tun := &NativeTun{ - fd: file, - events: make(chan TUNEvent, 10), - errors: make(chan error, 1), - } - - name, err := tun.Name() - if err != nil { - tun.fd.Close() - return nil, err - } - - tunIfindex, err := func() (int, error) { - iface, err := net.InterfaceByName(name) - if err != nil { - return -1, err - } - return iface.Index, nil - }() - if err != nil { - tun.fd.Close() - return nil, err - } - - tun.rwcancel, err = rwcancel.NewRWCancel(int(file.Fd())) - if err != nil { - tun.fd.Close() - return nil, err - } - - tun.routeSocket, err = unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC) - if err != nil { - tun.fd.Close() - return nil, err - } - - go tun.RoutineRouteListener(tunIfindex) - - // set default MTU - err = tun.setMTU(DefaultMTU) - if err != nil { - tun.Close() - return nil, err - } - - return tun, nil -} - -func (tun *NativeTun) Name() (string, error) { - name, err := tunName(tun.fd.Fd()) - if err != nil { - return "", err - } - tun.name = name - return name, nil -} - -func (tun *NativeTun) File() *os.File { - return tun.fd -} - -func (tun *NativeTun) Events() chan TUNEvent { - return tun.events -} - -func (tun *NativeTun) doRead(buff []byte, offset int) (int, error) { - select { - case err := <-tun.errors: - return 0, err - default: - buff := buff[offset-4:] - n, err := tun.fd.Read(buff[:]) - if n < 4 { - return 0, err - } - return n - 4, err - } -} - -func (tun *NativeTun) Read(buff []byte, offset int) (int, error) { - for { - n, err := tun.doRead(buff, offset) - if err == nil || !rwcancel.ErrorIsEAGAIN(err) { - return n, err - } - if !tun.rwcancel.ReadyRead() { - return 0, errors.New("tun device closed") - } - } -} - -func (tun *NativeTun) Write(buff []byte, offset int) (int, error) { - - // reserve space for header - - buff = buff[offset-4:] - - // add packet information header - - buff[0] = 0x00 - buff[1] = 0x00 - buff[2] = 0x00 - - if buff[4]>>4 == ipv6.Version { - buff[3] = unix.AF_INET6 - } else { - buff[3] = unix.AF_INET - } - - // write - - return tun.fd.Write(buff) -} - -func (tun *NativeTun) Close() error { - var err4 error - err1 := tun.rwcancel.Cancel() - err2 := tun.fd.Close() - err3 := tunDestroy(tun.name) - if tun.routeSocket != -1 { - unix.Shutdown(tun.routeSocket, unix.SHUT_RDWR) - err4 = unix.Close(tun.routeSocket) - tun.routeSocket = -1 - } else if tun.events != nil { - close(tun.events) - } - if err1 != nil { - return err1 - } - if err2 != nil { - return err2 - } - if err3 != nil { - return err3 - } - return err4 -} - -func (tun *NativeTun) setMTU(n int) error { - // open datagram socket - - var fd int - - fd, err := unix.Socket( - unix.AF_INET, - unix.SOCK_DGRAM, - 0, - ) - - if err != nil { - return err - } - - defer unix.Close(fd) - - // do ioctl call - - var ifr ifreq_mtu - copy(ifr.Name[:], tun.name) - ifr.MTU = uint32(n) - - _, _, errno := unix.Syscall( - unix.SYS_IOCTL, - uintptr(fd), - uintptr(unix.SIOCSIFMTU), - uintptr(unsafe.Pointer(&ifr)), - ) - - if errno != 0 { - return fmt.Errorf("failed to set MTU on %s", tun.name) - } - - return nil -} - -func (tun *NativeTun) MTU() (int, error) { - // open datagram socket - - fd, err := unix.Socket( - unix.AF_INET, - unix.SOCK_DGRAM, - 0, - ) - - if err != nil { - return 0, err - } - - defer unix.Close(fd) - - // do ioctl call - var ifr ifreq_mtu - copy(ifr.Name[:], tun.name) - - _, _, errno := unix.Syscall( - unix.SYS_IOCTL, - uintptr(fd), - uintptr(unix.SIOCGIFMTU), - uintptr(unsafe.Pointer(&ifr)), - ) - if errno != 0 { - return 0, fmt.Errorf("failed to get MTU on %s", tun.name) - } - - return int(*(*int32)(unsafe.Pointer(&ifr.MTU))), nil -} diff --git a/tun_linux.go b/tun_linux.go deleted file mode 100644 index db9cb51..0000000 --- a/tun_linux.go +++ /dev/null @@ -1,472 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 - * - * Copyright (C) 2017-2018 Jason A. Donenfeld . All Rights Reserved. - * Copyright (C) 2017-2018 Mathias N. Hall-Andersen . - */ - -/* Copyright 2018 Jason A. Donenfeld . All Rights Reserved. */ - -package main - -/* Implementation of the TUN device interface for linux - */ - -import ( - "./rwcancel" - "bytes" - "encoding/binary" - "errors" - "fmt" - "golang.org/x/net/ipv6" - "golang.org/x/sys/unix" - "net" - "os" - "strconv" - "sync" - "time" - "unsafe" -) - -const ( - cloneDevicePath = "/dev/net/tun" - ifReqSize = unix.IFNAMSIZ + 64 -) - -type NativeTun struct { - fd *os.File - fdCancel *rwcancel.RWCancel - index int32 // if index - name string // name of interface - errors chan error // async error handling - events chan TUNEvent // device related events - nopi bool // the device was pased IFF_NO_PI - netlinkSock int - netlinkCancel *rwcancel.RWCancel - hackListenerClosed sync.Mutex - statusListenersShutdown chan struct{} -} - -func (tun *NativeTun) File() *os.File { - return tun.fd -} - -func (tun *NativeTun) RoutineHackListener() { - defer tun.hackListenerClosed.Unlock() - /* 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.Fd()) - for { - _, err := unix.Write(fd, nil) - switch err { - case unix.EINVAL: - tun.events <- TUNEventUp - case unix.EIO: - tun.events <- TUNEventDown - default: - return - } - select { - case <-time.After(time.Second): - case <-tun.statusListenersShutdown: - return - } - } -} - -func createNetlinkSocket() (int, error) { - sock, err := unix.Socket(unix.AF_NETLINK, unix.SOCK_RAW, unix.NETLINK_ROUTE) - if err != nil { - return -1, err - } - saddr := &unix.SockaddrNetlink{ - Family: unix.AF_NETLINK, - Groups: uint32((1 << (unix.RTNLGRP_LINK - 1)) | (1 << (unix.RTNLGRP_IPV4_IFADDR - 1)) | (1 << (unix.RTNLGRP_IPV6_IFADDR - 1))), - } - err = unix.Bind(sock, saddr) - if err != nil { - return -1, err - } - return sock, nil -} - -func (tun *NativeTun) RoutineNetlinkListener() { - defer func() { - unix.Close(tun.netlinkSock) - tun.hackListenerClosed.Lock() - close(tun.events) - }() - - for msg := make([]byte, 1<<16); ; { - - var err error - var msgn int - for { - msgn, _, _, _, err = unix.Recvmsg(tun.netlinkSock, msg[:], nil, 0) - if err == nil || !rwcancel.ErrorIsEAGAIN(err) { - break - } - if !tun.netlinkCancel.ReadyRead() { - tun.errors <- fmt.Errorf("netlink socket closed: %s", err.Error()) - return - } - } - if err != nil { - tun.errors <- fmt.Errorf("failed to receive netlink message: %s", err.Error()) - return - } - - select { - case <-tun.statusListenersShutdown: - return - default: - } - - for remain := msg[:msgn]; len(remain) >= unix.SizeofNlMsghdr; { - - hdr := *(*unix.NlMsghdr)(unsafe.Pointer(&remain[0])) - - if int(hdr.Len) > len(remain) { - break - } - - switch hdr.Type { - case unix.NLMSG_DONE: - remain = []byte{} - - case unix.RTM_NEWLINK: - info := *(*unix.IfInfomsg)(unsafe.Pointer(&remain[unix.SizeofNlMsghdr])) - remain = remain[hdr.Len:] - - if info.Index != tun.index { - // not our interface - continue - } - - if info.Flags&unix.IFF_RUNNING != 0 { - tun.events <- TUNEventUp - } - - if info.Flags&unix.IFF_RUNNING == 0 { - tun.events <- TUNEventDown - } - - tun.events <- TUNEventMTUUpdate - - default: - remain = remain[hdr.Len:] - } - } - } -} - -func (tun *NativeTun) isUp() (bool, error) { - inter, err := net.InterfaceByName(tun.name) - return inter.Flags&net.FlagUp != 0, err -} - -func getDummySock() (int, error) { - return unix.Socket( - unix.AF_INET, - unix.SOCK_DGRAM, - 0, - ) -} - -func getIFIndex(name string) (int32, error) { - fd, err := getDummySock() - if err != nil { - return 0, err - } - - defer unix.Close(fd) - - var ifr [ifReqSize]byte - copy(ifr[:], name) - _, _, errno := unix.Syscall( - unix.SYS_IOCTL, - uintptr(fd), - uintptr(unix.SIOCGIFINDEX), - uintptr(unsafe.Pointer(&ifr[0])), - ) - - if errno != 0 { - return 0, errno - } - - index := binary.LittleEndian.Uint32(ifr[unix.IFNAMSIZ:]) - return toInt32(index), nil -} - -func (tun *NativeTun) setMTU(n int) error { - - // open datagram socket - - fd, err := unix.Socket( - unix.AF_INET, - unix.SOCK_DGRAM, - 0, - ) - - if err != nil { - return err - } - - defer unix.Close(fd) - - // do ioctl call - - var ifr [ifReqSize]byte - copy(ifr[:], tun.name) - binary.LittleEndian.PutUint32(ifr[16:20], uint32(n)) - _, _, errno := unix.Syscall( - unix.SYS_IOCTL, - uintptr(fd), - uintptr(unix.SIOCSIFMTU), - uintptr(unsafe.Pointer(&ifr[0])), - ) - - if errno != 0 { - return errors.New("failed to set MTU of TUN device") - } - - return nil -} - -func (tun *NativeTun) MTU() (int, error) { - - // open datagram socket - - fd, err := unix.Socket( - unix.AF_INET, - unix.SOCK_DGRAM, - 0, - ) - - if err != nil { - return 0, err - } - - defer unix.Close(fd) - - // do ioctl call - - var ifr [ifReqSize]byte - copy(ifr[:], tun.name) - _, _, errno := unix.Syscall( - unix.SYS_IOCTL, - uintptr(fd), - uintptr(unix.SIOCGIFMTU), - uintptr(unsafe.Pointer(&ifr[0])), - ) - if errno != 0 { - return 0, errors.New("failed to get MTU of TUN device: " + strconv.FormatInt(int64(errno), 10)) - } - - return int(*(*int32)(unsafe.Pointer(&ifr[16]))), nil -} - -func (tun *NativeTun) Name() (string, error) { - - var ifr [ifReqSize]byte - _, _, errno := unix.Syscall( - unix.SYS_IOCTL, - tun.fd.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)) - } - nullStr := ifr[:] - i := bytes.IndexByte(nullStr, 0) - if i != -1 { - nullStr = nullStr[:i] - } - tun.name = string(nullStr) - return tun.name, nil -} - -func (tun *NativeTun) Write(buff []byte, offset int) (int, error) { - - if tun.nopi { - buff = buff[offset:] - } else { - // reserve space for header - - buff = buff[offset-4:] - - // add packet information header - - buff[0] = 0x00 - buff[1] = 0x00 - - if buff[4]>>4 == ipv6.Version { - buff[2] = 0x86 - buff[3] = 0xdd - } else { - buff[2] = 0x08 - buff[3] = 0x00 - } - } - - // write - - return tun.fd.Write(buff) -} - -func (tun *NativeTun) doRead(buff []byte, offset int) (int, error) { - select { - case err := <-tun.errors: - return 0, err - default: - if tun.nopi { - return tun.fd.Read(buff[offset:]) - } else { - buff := buff[offset-4:] - n, err := tun.fd.Read(buff[:]) - if n < 4 { - return 0, err - } - return n - 4, err - } - } -} - -func (tun *NativeTun) Read(buff []byte, offset int) (int, error) { - for { - n, err := tun.doRead(buff, offset) - if err == nil || !rwcancel.ErrorIsEAGAIN(err) { - return n, err - } - if !tun.fdCancel.ReadyRead() { - return 0, errors.New("tun device closed") - } - } -} - -func (tun *NativeTun) Events() chan TUNEvent { - return tun.events -} - -func (tun *NativeTun) Close() error { - var err1 error - if tun.statusListenersShutdown != nil { - close(tun.statusListenersShutdown) - if tun.netlinkCancel != nil { - err1 = tun.netlinkCancel.Cancel() - } - } else if tun.events != nil { - close(tun.events) - } - err2 := tun.fd.Close() - err3 := tun.fdCancel.Cancel() - - if err1 != nil { - return err1 - } - if err2 != nil { - return err2 - } - return err3 -} - -func CreateTUN(name string) (TUNDevice, error) { - - // open clone device - - // HACK: we open it as a raw Fd first, so that f.nonblock=false - // when we make it into a file object. - nfd, err := unix.Open(cloneDevicePath, os.O_RDWR, 0) - if err != nil { - return nil, err - } - - err = unix.SetNonblock(nfd, true) - if err != nil { - return nil, err - } - - fd := os.NewFile(uintptr(nfd), cloneDevicePath) - if err != nil { - return nil, err - } - - // create new device - - var ifr [ifReqSize]byte - var flags uint16 = unix.IFF_TUN // | unix.IFF_NO_PI (disabled for TUN status hack) - nameBytes := []byte(name) - if len(nameBytes) >= unix.IFNAMSIZ { - return nil, errors.New("interface name too long") - } - copy(ifr[:], nameBytes) - binary.LittleEndian.PutUint16(ifr[16:], flags) - - _, _, errno := unix.Syscall( - unix.SYS_IOCTL, - fd.Fd(), - uintptr(unix.TUNSETIFF), - uintptr(unsafe.Pointer(&ifr[0])), - ) - if errno != 0 { - return nil, errno - } - - return CreateTUNFromFile(fd) -} - -func CreateTUNFromFile(fd *os.File) (TUNDevice, error) { - tun := &NativeTun{ - fd: fd, - events: make(chan TUNEvent, 5), - errors: make(chan error, 5), - statusListenersShutdown: make(chan struct{}), - nopi: false, - } - var err error - - tun.fdCancel, err = rwcancel.NewRWCancel(int(fd.Fd())) - if err != nil { - tun.fd.Close() - return nil, err - } - - _, err = tun.Name() - if err != nil { - tun.fd.Close() - return nil, err - } - - // start event listener - - tun.index, err = getIFIndex(tun.name) - if err != nil { - return nil, err - } - - tun.netlinkSock, err = createNetlinkSocket() - if err != nil { - tun.fd.Close() - return nil, err - } - tun.netlinkCancel, err = rwcancel.NewRWCancel(tun.netlinkSock) - if err != nil { - tun.fd.Close() - return nil, err - } - - tun.hackListenerClosed.Lock() - go tun.RoutineNetlinkListener() - go tun.RoutineHackListener() // cross namespace - - // set default MTU - - err = tun.setMTU(DefaultMTU) - if err != nil { - tun.Close() - return nil, err - } - - return tun, nil -} diff --git a/tun_openbsd.go b/tun_openbsd.go deleted file mode 100644 index 932404e..0000000 --- a/tun_openbsd.go +++ /dev/null @@ -1,360 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 - * - * Copyright (C) 2017-2018 Jason A. Donenfeld . All Rights Reserved. - */ - -package main - -import ( - "./rwcancel" - "errors" - "fmt" - "golang.org/x/net/ipv6" - "golang.org/x/sys/unix" - "io/ioutil" - "net" - "os" - "syscall" - "unsafe" -) - -// Structure for iface mtu get/set ioctls -type ifreq_mtu struct { - Name [unix.IFNAMSIZ]byte - MTU uint32 - Pad0 [12]byte -} - -const _TUNSIFMODE = 0x8004745d - -type NativeTun struct { - name string - fd *os.File - rwcancel *rwcancel.RWCancel - events chan TUNEvent - errors chan error - routeSocket int -} - -func (tun *NativeTun) RoutineRouteListener(tunIfindex int) { - var ( - statusUp bool - statusMTU int - ) - - defer close(tun.events) - - data := make([]byte, os.Getpagesize()) - for { - n, err := unix.Read(tun.routeSocket, data) - if err != nil { - tun.errors <- err - return - } - - if n < 8 { - continue - } - - if data[3 /* type */] != unix.RTM_IFINFO { - continue - } - ifindex := int(*(*uint16)(unsafe.Pointer(&data[6 /* ifindex */]))) - if ifindex != tunIfindex { - continue - } - - iface, err := net.InterfaceByIndex(ifindex) - if err != nil { - tun.errors <- err - return - } - - // Up / Down event - up := (iface.Flags & net.FlagUp) != 0 - if up != statusUp && up { - tun.events <- TUNEventUp - } - if up != statusUp && !up { - tun.events <- TUNEventDown - } - statusUp = up - - // MTU changes - if iface.MTU != statusMTU { - tun.events <- TUNEventMTUUpdate - } - statusMTU = iface.MTU - } -} - -func errorIsEBUSY(err error) bool { - if pe, ok := err.(*os.PathError); ok { - if errno, ok := pe.Err.(syscall.Errno); ok && errno == syscall.EBUSY { - return true - } - } - if errno, ok := err.(syscall.Errno); ok && errno == syscall.EBUSY { - return true - } - return false -} - -func CreateTUN(name string) (TUNDevice, error) { - ifIndex := -1 - if name != "tun" { - _, err := fmt.Sscanf(name, "tun%d", &ifIndex) - if err != nil || ifIndex < 0 { - return nil, fmt.Errorf("Interface name must be tun[0-9]*") - } - } - - var tunfile *os.File - var err error - - if ifIndex != -1 { - tunfile, err = os.OpenFile(fmt.Sprintf("/dev/tun%d", ifIndex), unix.O_RDWR, 0) - } else { - for ifIndex = 0; ifIndex < 256; ifIndex += 1 { - tunfile, err = os.OpenFile(fmt.Sprintf("/dev/tun%d", ifIndex), unix.O_RDWR, 0) - if err == nil || !errorIsEBUSY(err) { - break - } - } - } - - if err != nil { - return nil, err - } - - // Set TUN iface to broadcast mode - ifmodemode := unix.IFF_BROADCAST - _, _, errno := unix.Syscall( - unix.SYS_IOCTL, - uintptr(tunfile.Fd()), - uintptr(_TUNSIFMODE), - uintptr(unsafe.Pointer(&ifmodemode)), - ) - - if errno != 0 { - return nil, fmt.Errorf("error %s", errno.Error()) - } - - tun, err := CreateTUNFromFile(tunfile) - - if err == nil && name == "tun" { - fname := os.Getenv("WG_TUN_NAME_FILE") - if fname != "" { - ioutil.WriteFile(fname, []byte(tun.(*NativeTun).name+"\n"), 0400) - } - } - - return tun, err -} - -func CreateTUNFromFile(file *os.File) (TUNDevice, error) { - - tun := &NativeTun{ - fd: file, - events: make(chan TUNEvent, 10), - errors: make(chan error, 1), - } - - name, err := tun.Name() - if err != nil { - tun.fd.Close() - return nil, err - } - - tunIfindex, err := func() (int, error) { - iface, err := net.InterfaceByName(name) - if err != nil { - return -1, err - } - return iface.Index, nil - }() - if err != nil { - tun.fd.Close() - return nil, err - } - - tun.rwcancel, err = rwcancel.NewRWCancel(int(file.Fd())) - if err != nil { - tun.fd.Close() - return nil, err - } - - tun.routeSocket, err = unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC) - if err != nil { - tun.fd.Close() - return nil, err - } - - go tun.RoutineRouteListener(tunIfindex) - - // set default MTU - err = tun.setMTU(DefaultMTU) - if err != nil { - tun.Close() - return nil, err - } - - return tun, nil -} - -func (tun *NativeTun) Name() (string, error) { - gostat, err := tun.fd.Stat() - if err != nil { - tun.name = "" - return "", err - } - stat := gostat.Sys().(*syscall.Stat_t) - tun.name = fmt.Sprintf("tun%d", stat.Rdev%256) - return tun.name, nil -} - -func (tun *NativeTun) File() *os.File { - return tun.fd -} - -func (tun *NativeTun) Events() chan TUNEvent { - return tun.events -} - -func (tun *NativeTun) doRead(buff []byte, offset int) (int, error) { - select { - case err := <-tun.errors: - return 0, err - default: - buff := buff[offset-4:] - n, err := tun.fd.Read(buff[:]) - if n < 4 { - return 0, err - } - return n - 4, err - } -} - -func (tun *NativeTun) Read(buff []byte, offset int) (int, error) { - for { - n, err := tun.doRead(buff, offset) - if err == nil || !rwcancel.ErrorIsEAGAIN(err) { - return n, err - } - if !tun.rwcancel.ReadyRead() { - return 0, errors.New("tun device closed") - } - } -} - -func (tun *NativeTun) Write(buff []byte, offset int) (int, error) { - - // reserve space for header - - buff = buff[offset-4:] - - // add packet information header - - buff[0] = 0x00 - buff[1] = 0x00 - buff[2] = 0x00 - - if buff[4]>>4 == ipv6.Version { - buff[3] = unix.AF_INET6 - } else { - buff[3] = unix.AF_INET - } - - // write - - return tun.fd.Write(buff) -} - -func (tun *NativeTun) Close() error { - var err3 error - err1 := tun.rwcancel.Cancel() - err2 := tun.fd.Close() - if tun.routeSocket != -1 { - unix.Shutdown(tun.routeSocket, unix.SHUT_RDWR) - err3 = unix.Close(tun.routeSocket) - tun.routeSocket = -1 - } else if tun.events != nil { - close(tun.events) - } - if err1 != nil { - return err1 - } - if err2 != nil { - return err2 - } - return err3 -} - -func (tun *NativeTun) setMTU(n int) error { - // open datagram socket - - var fd int - - fd, err := unix.Socket( - unix.AF_INET, - unix.SOCK_DGRAM, - 0, - ) - - if err != nil { - return err - } - - defer unix.Close(fd) - - // do ioctl call - - var ifr ifreq_mtu - copy(ifr.Name[:], tun.name) - ifr.MTU = uint32(n) - - _, _, errno := unix.Syscall( - unix.SYS_IOCTL, - uintptr(fd), - uintptr(unix.SIOCSIFMTU), - uintptr(unsafe.Pointer(&ifr)), - ) - - if errno != 0 { - return fmt.Errorf("failed to set MTU on %s", tun.name) - } - - return nil -} - -func (tun *NativeTun) MTU() (int, error) { - // open datagram socket - - fd, err := unix.Socket( - unix.AF_INET, - unix.SOCK_DGRAM, - 0, - ) - - if err != nil { - return 0, err - } - - defer unix.Close(fd) - - // do ioctl call - var ifr ifreq_mtu - copy(ifr.Name[:], tun.name) - - _, _, errno := unix.Syscall( - unix.SYS_IOCTL, - uintptr(fd), - uintptr(unix.SIOCGIFMTU), - uintptr(unsafe.Pointer(&ifr)), - ) - if errno != 0 { - return 0, fmt.Errorf("failed to get MTU on %s", tun.name) - } - - return int(*(*int32)(unsafe.Pointer(&ifr.MTU))), nil -} -- cgit v1.2.3-59-g8ed1b