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.
This commit is contained in:
parent
f1dc167901
commit
bb42ec7d18
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue