From aa5a321f820a7c326ee00b3de1a829b1cc252cb2 Mon Sep 17 00:00:00 2001 From: Yifan Gu Date: Mon, 20 May 2019 18:10:45 -0400 Subject: [PATCH] osxtuntap support --- params_darwin.go | 15 ++++++++++++ syscalls_darwin.go | 61 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 74 insertions(+), 2 deletions(-) diff --git a/params_darwin.go b/params_darwin.go index 57c7813..0c5846b 100644 --- a/params_darwin.go +++ b/params_darwin.go @@ -1,10 +1,25 @@ package water +const ( + // SystemDriver refers to the default P2P driver + SystemDriver = 0 + // TunTapOSXDriver refers to the third-party tuntaposx driver + // see https://sourceforge.net/p/tuntaposx + TunTapOSXDriver = 1 +) + // PlatformSpecificParams defines parameters in Config that are specific to // macOS. A zero-value of such type is valid, yielding an interface // with OS defined name. // Currently it is not possible to set the interface name in macOS. type PlatformSpecificParams struct { + // Name is the name for the interface to be used. + // e.g. "tap0" + // Only valid if using TunTapOSXDriver. + Name string + // Driver should be set if an alternative driver is desired + // e.g. TunTapOSXDriver + Driver int } func defaultPlatformSpecificParams() PlatformSpecificParams { diff --git a/syscalls_darwin.go b/syscalls_darwin.go index b344034..bfc1ca9 100644 --- a/syscalls_darwin.go +++ b/syscalls_darwin.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "os" + "strings" "sync" "syscall" "unsafe" @@ -60,8 +61,13 @@ type sockaddrCtl struct { var sockaddrCtlSize uintptr = 32 func openDev(config Config) (ifce *Interface, err error) { + if config.Driver == TunTapOSXDriver { + return openDevTunTapOSX(config) + } else if config.Driver != SystemDriver { + return nil, errors.New("unrecognized driver") + } if config.DeviceType != TUN { - return nil, errors.New("only tun is implemented on this platform") + return nil, errors.New("only tun is implemented for SystemDriver, use TunTapOSXDriver for tap") } var fd int // Supposed to be socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL), but ... @@ -83,7 +89,7 @@ func openDev(config Config) (ifce *Interface, err error) { if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(appleCTLIOCGINFO), uintptr(unsafe.Pointer(ctlInfo))); errno != 0 { err = errno - return nil, fmt.Errorf("error in syscall.Syscall(syscall.SYS_IOTL, ...): %v", err) + return nil, fmt.Errorf("error in syscall.Syscall(syscall.SYS_IOCTL, ...): %v", err) } addrP := unsafe.Pointer(&sockaddrCtl{ @@ -127,6 +133,57 @@ func openDev(config Config) (ifce *Interface, err error) { }, nil } +// openDevTunTapOSX opens tun / tap device, assuming tuntaposx is installed +func openDevTunTapOSX(config Config) (ifce *Interface, err error) { + var fd int + var socketFD int + + if config.DeviceType == TAP && !strings.HasPrefix(config.Name, "tap") { + return nil, errors.New("device name does not start with tap when creating a tap device") + } else if config.DeviceType == TUN && !strings.HasPrefix(config.Name, "tun") { + return nil, errors.New("device name does not start with tun when creating a tun device") + } else if config.DeviceType != TAP && config.DeviceType != TUN { + return nil, errors.New("unsupported DeviceType") + } + if len(config.Name) >= 15 { + return nil, errors.New("device name is too long") + } + + if fd, err = syscall.Open( + "/dev/"+config.Name, os.O_RDWR|syscall.O_NONBLOCK, 0); err != nil { + return nil, err + } + // Note that we are not setting NONBLOCK on the fd itself since it breaks tuntaposx + // see https://sourceforge.net/p/tuntaposx/bugs/6/ + + // create socket so we can do SIO ioctls, we are not using it afterwards + if socketFD, err = syscall.Socket(syscall.AF_SYSTEM, syscall.SOCK_DGRAM, 2); err != nil { + return nil, fmt.Errorf("error in syscall.Socket: %v", err) + } + var ifReq = &struct { + ifName [16]byte + ifruFlags int16 + pad [16]byte + }{} + copy(ifReq.ifName[:], []byte(config.Name)) + if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(socketFD), uintptr(syscall.SIOCGIFFLAGS), uintptr(unsafe.Pointer(ifReq))); errno != 0 { + err = errno + return nil, fmt.Errorf("error in syscall.Syscall(syscall.SYS_IOCTL, ...): %v", err) + } + ifReq.ifruFlags |= syscall.IFF_RUNNING | syscall.IFF_UP + if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(socketFD), uintptr(syscall.SIOCSIFFLAGS), uintptr(unsafe.Pointer(ifReq))); errno != 0 { + err = errno + return nil, fmt.Errorf("error in syscall.Syscall(syscall.SYS_IOCTL, ...): %v", err) + } + syscall.Close(socketFD) + + return &Interface{ + isTAP: config.DeviceType == TAP, + ReadWriteCloser: os.NewFile(uintptr(fd), "tun"), + name: config.Name, + }, nil +} + // tunReadCloser is a hack to work around the first 4 bytes "packet // information" because there doesn't seem to be an IFF_NO_PI for darwin. type tunReadCloser struct {