From bb42ec7d185ab5f5cd3867ac1258edff86b7f307 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Sat, 20 Apr 2019 11:29:07 +0900 Subject: tun: freebsd: work around numerous kernel panics on shutdown There are numerous race conditions. But even this will crash it: while true; do ifconfig tun0 create; ifconfig tun0 destroy; done It seems like LLv6 is related, which we're not using anyway, so explicitly disable it on the interface. --- tun/tun_freebsd.go | 105 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 82 insertions(+), 23 deletions(-) (limited to 'tun') diff --git a/tun/tun_freebsd.go b/tun/tun_freebsd.go index 01a4348..c9c89ef 100644 --- a/tun/tun_freebsd.go +++ b/tun/tun_freebsd.go @@ -19,9 +19,19 @@ import ( // _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 +const ( + _TUNSIFHEAD = 0x80047460 + _TUNSIFMODE = 0x8004745e + _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 +) // Iface status string max len const _IFSTATMAX = 800 @@ -32,7 +42,7 @@ const SIZEOF_UINTPTR = 4 << (^uintptr(0) >> 32 & 1) type ifreq_ptr struct { Name [unix.IFNAMSIZ]byte Data uintptr - Pad0 [24 - SIZEOF_UINTPTR]byte + Pad0 [16 - SIZEOF_UINTPTR]byte } // Structure for iface mtu get/set ioctls @@ -48,6 +58,23 @@ type ifstat struct { Ascii [_IFSTATMAX]byte } +// Structures for nd6 flag manipulation +type in6_ndireq struct { + Name [unix.IFNAMSIZ]byte + Linkmtu uint32 + Maxmtu uint32 + Basereachable uint32 + Reachable uint32 + Retrans uint32 + Flags uint32 + Recalctm int + Chlim uint8 + Initialized uint8 + Randomseed0 [8]byte + Randomseed1 [8]byte + Randomid [8]byte +} + type NativeTun struct { name string tunFile *os.File @@ -191,23 +218,18 @@ func tunName(fd uintptr) (string, error) { // Destroy a named system interface func tunDestroy(name string) error { - // open control socket + // 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( @@ -216,7 +238,6 @@ func tunDestroy(name string) error { uintptr(unix.SIOCIFDESTROY), uintptr(unsafe.Pointer(&ifr[0])), ) - if errno != 0 { return fmt.Errorf("failed to destroy interface %s: %s", name, errno.Error()) } @@ -263,33 +284,71 @@ func CreateTUN(name string, mtu int) (TUNDevice, error) { }) if errno != 0 { - return nil, fmt.Errorf("error %s", errno.Error()) + tunFile.Close() + tunDestroy(assignedName) + return nil, fmt.Errorf("Unable to put into IFHEAD mode: %v", errno) } - // Rename tun interface - - // Open control socket + // Open control sockets confd, err := unix.Socket( unix.AF_INET, unix.SOCK_DGRAM, 0, ) - if err != nil { + tunFile.Close() + tunDestroy(assignedName) return nil, err } - defer unix.Close(confd) + confd6, err := unix.Socket( + unix.AF_INET6, + unix.SOCK_DGRAM, + 0, + ) + if err != nil { + tunFile.Close() + tunDestroy(assignedName) + return nil, err + } + defer unix.Close(confd6) - // set up struct for iface rename + // 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 + copy(ndireq.Name[:], assignedName) + _, _, 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) + } + 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) + } + + // 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])) - - //do actual ioctl to rename iface _, _, errno = unix.Syscall( unix.SYS_IOCTL, uintptr(confd), @@ -298,8 +357,8 @@ func CreateTUN(name string, mtu int) (TUNDevice, error) { ) if errno != 0 { tunFile.Close() - tunDestroy(name) - return nil, fmt.Errorf("failed to rename %s to %s: %s", assignedName, name, errno.Error()) + tunDestroy(assignedName) + return nil, fmt.Errorf("Failed to rename %s to %s: %v", assignedName, name, errno) } return CreateTUNFromFile(tunFile, mtu) -- cgit v1.2.3-59-g8ed1b