aboutsummaryrefslogtreecommitdiffstats
path: root/tun/tun_freebsd.go
diff options
context:
space:
mode:
Diffstat (limited to 'tun/tun_freebsd.go')
-rw-r--r--tun/tun_freebsd.go398
1 files changed, 136 insertions, 262 deletions
diff --git a/tun/tun_freebsd.go b/tun/tun_freebsd.go
index 6cf9313..7c65fd9 100644
--- a/tun/tun_freebsd.go
+++ b/tun/tun_freebsd.go
@@ -1,66 +1,57 @@
/* SPDX-License-Identifier: MIT
*
- * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
+ * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/
package tun
import (
- "bytes"
"errors"
"fmt"
+ "io"
"net"
"os"
+ "sync"
"syscall"
"unsafe"
- "golang.org/x/net/ipv6"
"golang.org/x/sys/unix"
)
-// _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
_TUNSIFMODE = 0x8004745e
+ _TUNGIFNAME = 0x4020745d
_TUNSIFPID = 0x2000745f
-)
-// TODO: move into x/sys/unix
-const (
- SIOCGIFINFO_IN6 = 0xc048696c
- SIOCSIFINFO_IN6 = 0xc048696d
- ND6_IFF_AUTO_LINKLOCAL = 0x20
- ND6_IFF_NO_DAD = 0x100
+ _SIOCGIFINFO_IN6 = 0xc048696c
+ _SIOCSIFINFO_IN6 = 0xc048696d
+ _ND6_IFF_AUTO_LINKLOCAL = 0x20
+ _ND6_IFF_NO_DAD = 0x100
)
-// Iface status string max len
-const _IFSTATMAX = 800
-
-const SIZEOF_UINTPTR = 4 << (^uintptr(0) >> 32 & 1)
+// Iface requests with just the name
+type ifreqName struct {
+ Name [unix.IFNAMSIZ]byte
+ _ [16]byte
+}
-// structure for iface requests with a pointer
-type ifreq_ptr struct {
+// Iface requests with a pointer
+type ifreqPtr struct {
Name [unix.IFNAMSIZ]byte
Data uintptr
- Pad0 [16 - SIZEOF_UINTPTR]byte
+ _ [16 - unsafe.Sizeof(uintptr(0))]byte
}
-// Structure for iface mtu get/set ioctls
-type ifreq_mtu struct {
+// Iface requests with MTU
+type ifreqMtu 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
+ _ [12]byte
}
-// Structures for nd6 flag manipulation
-type in6_ndireq struct {
+// ND6 flag manipulation
+type nd6Req struct {
Name [unix.IFNAMSIZ]byte
Linkmtu uint32
Maxmtu uint32
@@ -82,6 +73,7 @@ type NativeTun struct {
events chan Event
errors chan error
routeSocket int
+ closeOnce sync.Once
}
func (tun *NativeTun) routineRouteListener(tunIfindex int) {
@@ -97,7 +89,7 @@ func (tun *NativeTun) routineRouteListener(tunIfindex int) {
retry:
n, err := unix.Read(tun.routeSocket, data)
if err != nil {
- if errno, ok := err.(syscall.Errno); ok && errno == syscall.EINTR {
+ if errors.Is(err, syscall.EINTR) {
goto retry
}
tun.errors <- err
@@ -141,91 +133,17 @@ func (tun *NativeTun) routineRouteListener(tunIfindex int) {
}
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 {
+ var ifreq ifreqName
+ _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, _TUNGIFNAME, uintptr(unsafe.Pointer(&ifreq)))
+ if err != 0 {
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
+ return unix.ByteSliceToString(ifreq.Name[:]), 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,
- )
+ fd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM|unix.SOCK_CLOEXEC, 0)
if err != nil {
return err
}
@@ -233,14 +151,9 @@ func tunDestroy(name string) error {
var ifr [32]byte
copy(ifr[:], name)
- _, _, errno := unix.Syscall(
- unix.SYS_IOCTL,
- uintptr(fd),
- uintptr(unix.SIOCIFDESTROY),
- uintptr(unsafe.Pointer(&ifr[0])),
- )
+ _, _, 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 fmt.Errorf("failed to destroy interface %s: %w", name, errno)
}
return nil
@@ -257,7 +170,7 @@ func CreateTUN(name string, mtu int) (Device, error) {
return nil, fmt.Errorf("interface %s already exists", name)
}
- tunFile, err := os.OpenFile("/dev/tun", unix.O_RDWR, 0)
+ tunFile, err := os.OpenFile("/dev/tun", unix.O_RDWR|unix.O_CLOEXEC, 0)
if err != nil {
return nil, err
}
@@ -276,103 +189,94 @@ func CreateTUN(name string, mtu int) (Device, error) {
ifheadmode := 1
var errno syscall.Errno
tun.operateOnFd(func(fd uintptr) {
- _, _, errno = unix.Syscall(
- unix.SYS_IOCTL,
- fd,
- uintptr(_TUNSIFHEAD),
- uintptr(unsafe.Pointer(&ifheadmode)),
- )
+ _, _, errno = unix.Syscall(unix.SYS_IOCTL, fd, _TUNSIFHEAD, uintptr(unsafe.Pointer(&ifheadmode)))
})
if errno != 0 {
tunFile.Close()
tunDestroy(assignedName)
- return nil, fmt.Errorf("Unable to put into IFHEAD mode: %v", errno)
+ return nil, fmt.Errorf("unable to put into IFHEAD mode: %w", errno)
}
- // Open control sockets
- confd, err := unix.Socket(
- unix.AF_INET,
- unix.SOCK_DGRAM,
- 0,
- )
- if err != nil {
+ // Get out of PTP mode.
+ ifflags := syscall.IFF_BROADCAST | syscall.IFF_MULTICAST
+ tun.operateOnFd(func(fd uintptr) {
+ _, _, errno = unix.Syscall(unix.SYS_IOCTL, fd, uintptr(_TUNSIFMODE), uintptr(unsafe.Pointer(&ifflags)))
+ })
+
+ if errno != 0 {
tunFile.Close()
tunDestroy(assignedName)
- return nil, err
+ return nil, fmt.Errorf("unable to put into IFF_BROADCAST mode: %w", errno)
}
- defer unix.Close(confd)
- confd6, err := unix.Socket(
- unix.AF_INET6,
- unix.SOCK_DGRAM,
- 0,
- )
+
+ // Disable link-local v6, not just because WireGuard doesn't do that anyway, but
+ // also because there are serious races with attaching and detaching LLv6 addresses
+ // in relation to interface lifetime within the FreeBSD kernel.
+ confd6, err := unix.Socket(unix.AF_INET6, unix.SOCK_DGRAM|unix.SOCK_CLOEXEC, 0)
if err != nil {
tunFile.Close()
tunDestroy(assignedName)
return nil, err
}
defer unix.Close(confd6)
-
- // Disable link-local v6, not just because WireGuard doesn't do that anyway, but
- // also because there are serious races with attaching and detaching LLv6 addresses
- // in relation to interface lifetime within the FreeBSD kernel.
- var ndireq in6_ndireq
+ var ndireq nd6Req
copy(ndireq.Name[:], assignedName)
- _, _, errno = unix.Syscall(
- unix.SYS_IOCTL,
- uintptr(confd6),
- uintptr(SIOCGIFINFO_IN6),
- uintptr(unsafe.Pointer(&ndireq)),
- )
+ _, _, errno = unix.Syscall(unix.SYS_IOCTL, uintptr(confd6), uintptr(_SIOCGIFINFO_IN6), uintptr(unsafe.Pointer(&ndireq)))
if errno != 0 {
tunFile.Close()
tunDestroy(assignedName)
- return nil, fmt.Errorf("Unable to get nd6 flags for %s: %v", assignedName, errno)
+ return nil, fmt.Errorf("unable to get nd6 flags for %s: %w", assignedName, errno)
}
- ndireq.Flags = ndireq.Flags &^ ND6_IFF_AUTO_LINKLOCAL
- ndireq.Flags = ndireq.Flags | ND6_IFF_NO_DAD
- _, _, errno = unix.Syscall(
- unix.SYS_IOCTL,
- uintptr(confd6),
- uintptr(SIOCSIFINFO_IN6),
- uintptr(unsafe.Pointer(&ndireq)),
- )
+ ndireq.Flags = ndireq.Flags &^ _ND6_IFF_AUTO_LINKLOCAL
+ ndireq.Flags = ndireq.Flags | _ND6_IFF_NO_DAD
+ _, _, errno = unix.Syscall(unix.SYS_IOCTL, uintptr(confd6), uintptr(_SIOCSIFINFO_IN6), uintptr(unsafe.Pointer(&ndireq)))
if errno != 0 {
tunFile.Close()
tunDestroy(assignedName)
- return nil, fmt.Errorf("Unable to set nd6 flags for %s: %v", assignedName, errno)
+ return nil, fmt.Errorf("unable to set nd6 flags for %s: %w", assignedName, errno)
}
- // Rename the interface
- var newnp [unix.IFNAMSIZ]byte
- copy(newnp[:], name)
- var ifr ifreq_ptr
- copy(ifr.Name[:], assignedName)
- ifr.Data = uintptr(unsafe.Pointer(&newnp[0]))
- _, _, errno = unix.Syscall(
- unix.SYS_IOCTL,
- uintptr(confd),
- uintptr(unix.SIOCSIFNAME),
- uintptr(unsafe.Pointer(&ifr)),
- )
- if errno != 0 {
- tunFile.Close()
- tunDestroy(assignedName)
- return nil, fmt.Errorf("Failed to rename %s to %s: %v", assignedName, name, errno)
+ if name != "" {
+ confd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM|unix.SOCK_CLOEXEC, 0)
+ if err != nil {
+ tunFile.Close()
+ tunDestroy(assignedName)
+ return nil, err
+ }
+ defer unix.Close(confd)
+ var newnp [unix.IFNAMSIZ]byte
+ copy(newnp[:], name)
+ var ifr ifreqPtr
+ copy(ifr.Name[:], assignedName)
+ ifr.Data = uintptr(unsafe.Pointer(&newnp[0]))
+ _, _, errno = unix.Syscall(unix.SYS_IOCTL, uintptr(confd), uintptr(unix.SIOCSIFNAME), uintptr(unsafe.Pointer(&ifr)))
+ if errno != 0 {
+ tunFile.Close()
+ tunDestroy(assignedName)
+ return nil, fmt.Errorf("Failed to rename %s to %s: %w", assignedName, name, errno)
+ }
}
return CreateTUNFromFile(tunFile, mtu)
}
func CreateTUNFromFile(file *os.File, mtu int) (Device, error) {
-
tun := &NativeTun{
tunFile: file,
events: make(chan Event, 10),
errors: make(chan error, 1),
}
+ var errno syscall.Errno
+ tun.operateOnFd(func(fd uintptr) {
+ _, _, errno = unix.Syscall(unix.SYS_IOCTL, fd, _TUNSIFPID, uintptr(0))
+ })
+ if errno != 0 {
+ tun.tunFile.Close()
+ return nil, fmt.Errorf("unable to become controlling TUN process: %w", errno)
+ }
+
name, err := tun.Name()
if err != nil {
tun.tunFile.Close()
@@ -391,7 +295,7 @@ func CreateTUNFromFile(file *os.File, mtu int) (Device, error) {
return nil, err
}
- tun.routeSocket, err = unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC)
+ tun.routeSocket, err = unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW|unix.SOCK_CLOEXEC, unix.AF_UNSPEC)
if err != nil {
tun.tunFile.Close()
return nil, err
@@ -425,63 +329,65 @@ func (tun *NativeTun) File() *os.File {
return tun.tunFile
}
-func (tun *NativeTun) Events() chan Event {
+func (tun *NativeTun) Events() <-chan Event {
return tun.events
}
-func (tun *NativeTun) Read(buff []byte, offset int) (int, error) {
+func (tun *NativeTun) Read(bufs [][]byte, sizes []int, offset int) (int, error) {
select {
case err := <-tun.errors:
return 0, err
default:
- buff := buff[offset-4:]
- n, err := tun.tunFile.Read(buff[:])
+ buf := bufs[0][offset-4:]
+ n, err := tun.tunFile.Read(buf[:])
if n < 4 {
return 0, err
}
- return n - 4, err
+ sizes[0] = n - 4
+ return 1, err
}
}
-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
+func (tun *NativeTun) Write(bufs [][]byte, offset int) (int, error) {
+ if offset < 4 {
+ return 0, io.ErrShortBuffer
}
-
- // write
-
- return tun.tunFile.Write(buff)
-}
-
-func (tun *NativeTun) Flush() error {
- // TODO: can flushing be implemented by buffering and using sendmmsg?
- return nil
+ for i, buf := range bufs {
+ buf = buf[offset-4:]
+ if len(buf) < 5 {
+ return i, io.ErrShortBuffer
+ }
+ buf[0] = 0x00
+ buf[1] = 0x00
+ buf[2] = 0x00
+ switch buf[4] >> 4 {
+ case 4:
+ buf[3] = unix.AF_INET
+ case 6:
+ buf[3] = unix.AF_INET6
+ default:
+ return i, unix.EAFNOSUPPORT
+ }
+ if _, err := tun.tunFile.Write(buf); err != nil {
+ return i, err
+ }
+ }
+ return len(bufs), nil
}
func (tun *NativeTun) Close() error {
- var err3 error
- err1 := tun.tunFile.Close()
- err2 := tunDestroy(tun.name)
- 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)
- }
+ var err1, err2, err3 error
+ tun.closeOnce.Do(func() {
+ err1 = tun.tunFile.Close()
+ err2 = tunDestroy(tun.name)
+ 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
}
@@ -492,70 +398,38 @@ func (tun *NativeTun) Close() error {
}
func (tun *NativeTun) setMTU(n int) error {
- // open datagram socket
-
- var fd int
-
- fd, err := unix.Socket(
- unix.AF_INET,
- unix.SOCK_DGRAM,
- 0,
- )
-
+ fd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM|unix.SOCK_CLOEXEC, 0)
if err != nil {
return err
}
-
defer unix.Close(fd)
- // do ioctl call
-
- var ifr ifreq_mtu
+ var ifr ifreqMtu
copy(ifr.Name[:], tun.name)
ifr.MTU = uint32(n)
-
- _, _, errno := unix.Syscall(
- unix.SYS_IOCTL,
- uintptr(fd),
- uintptr(unix.SIOCSIFMTU),
- uintptr(unsafe.Pointer(&ifr)),
- )
-
+ _, _, 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 fmt.Errorf("failed to set MTU on %s: %w", tun.name, errno)
}
-
return nil
}
func (tun *NativeTun) MTU() (int, error) {
- // open datagram socket
-
- fd, err := unix.Socket(
- unix.AF_INET,
- unix.SOCK_DGRAM,
- 0,
- )
-
+ fd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM|unix.SOCK_CLOEXEC, 0)
if err != nil {
return 0, err
}
-
defer unix.Close(fd)
- // do ioctl call
- var ifr ifreq_mtu
+ var ifr ifreqMtu
copy(ifr.Name[:], tun.name)
-
- _, _, errno := unix.Syscall(
- unix.SYS_IOCTL,
- uintptr(fd),
- uintptr(unix.SIOCGIFMTU),
- uintptr(unsafe.Pointer(&ifr)),
- )
+ _, _, 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 0, fmt.Errorf("failed to get MTU on %s: %w", tun.name, errno)
}
-
return int(*(*int32)(unsafe.Pointer(&ifr.MTU))), nil
}
+
+func (tun *NativeTun) BatchSize() int {
+ return 1
+}