From 258a9223b9ab18a973c44b238e029a0dc5640102 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Thu, 3 May 2018 04:49:35 +0200 Subject: [PATCH] Start to dust off Darwin --- daemon_linux.go => daemon.go | 9 +- daemon_darwin.go | 9 -- daemon_windows.go | 34 ------ main.go | 2 + tun_darwin.go | 194 +++++++++++++++-------------------- uapi_darwin.go | 79 +++++++++++--- warning_default.go | 19 ++++ warning_linux.go | 39 +++++++ 8 files changed, 206 insertions(+), 179 deletions(-) rename daemon_linux.go => daemon.go (55%) delete mode 100644 daemon_darwin.go delete mode 100644 daemon_windows.go create mode 100644 warning_default.go create mode 100644 warning_linux.go diff --git a/daemon_linux.go b/daemon.go similarity index 55% rename from daemon_linux.go rename to daemon.go index e1aaede..e2ded87 100644 --- a/daemon_linux.go +++ b/daemon.go @@ -2,17 +2,10 @@ package main import ( "os" - "os/exec" ) -/* Daemonizes the process on linux - * - * This is done by spawning and releasing a copy with the --foreground flag - */ func Daemonize(attr *os.ProcAttr) error { - // I would like to use os.Executable, - // however this means dropping support for Go <1.8 - path, err := exec.LookPath(os.Args[0]) + path, err := os.Executable() if err != nil { return err } diff --git a/daemon_darwin.go b/daemon_darwin.go deleted file mode 100644 index 913af0e..0000000 --- a/daemon_darwin.go +++ /dev/null @@ -1,9 +0,0 @@ -package main - -import ( - "errors" -) - -func Daemonize() error { - return errors.New("Not implemented on OSX") -} diff --git a/daemon_windows.go b/daemon_windows.go deleted file mode 100644 index 527718a..0000000 --- a/daemon_windows.go +++ /dev/null @@ -1,34 +0,0 @@ -package main - -import ( - "os" -) - -/* Daemonizes the process on windows - * - * This is done by spawning and releasing a copy with the --foreground flag - */ - -func Daemonize() error { - argv := []string{os.Args[0], "--foreground"} - argv = append(argv, os.Args[1:]...) - attr := &os.ProcAttr{ - Dir: ".", - Env: os.Environ(), - Files: []*os.File{ - os.Stdin, - nil, - nil, - }, - } - process, err := os.StartProcess( - argv[0], - argv, - attr, - ) - if err != nil { - return err - } - process.Release() - return nil -} diff --git a/main.go b/main.go index 7742eef..3358469 100644 --- a/main.go +++ b/main.go @@ -25,6 +25,8 @@ func printUsage() { func main() { + Warning() + // parse arguments var foreground bool diff --git a/tun_darwin.go b/tun_darwin.go index 87f6af6..d03ff48 100644 --- a/tun_darwin.go +++ b/tun_darwin.go @@ -1,22 +1,12 @@ -/* Copyright (c) 2016, Song Gao - * All rights reserved. - * - * Code from https://github.com/songgao/water - */ - package main import ( "encoding/binary" - "errors" "fmt" - "golang.org/x/net/ipv4" "golang.org/x/net/ipv6" "golang.org/x/sys/unix" - "io" "net" "os" - "sync" "time" "unsafe" ) @@ -36,26 +26,20 @@ type sockaddrCtl struct { scReserved [5]uint32 } -// NativeTUN is a hack to work around the first 4 bytes "packet +// NativeTun 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 NativeTUN struct { +type NativeTun struct { name string - f io.ReadWriteCloser + fd *os.File mtu int - rMu sync.Mutex - rBuf []byte - - wMu sync.Mutex - wBuf []byte - events chan TUNEvent errors chan error } var sockaddrCtlSize uintptr = 32 -func CreateTUN(name string) (ifce TUNDevice, err error) { +func CreateTUN(name string) (TUNDevice, error) { ifIndex := -1 fmt.Sscanf(name, "utun%d", &ifIndex) if ifIndex < 0 { @@ -65,7 +49,7 @@ func CreateTUN(name string) (ifce TUNDevice, err error) { fd, err := unix.Socket(unix.AF_SYSTEM, unix.SOCK_DGRAM, 2) if err != nil { - return nil, fmt.Errorf("error in unix.Socket: %v", err) + return nil, err } var ctlInfo = &struct { @@ -83,8 +67,7 @@ func CreateTUN(name string) (ifce TUNDevice, err error) { ) if errno != 0 { - err = errno - return nil, fmt.Errorf("error in unix.Syscall(unix.SYS_IOTL, ...): %v", err) + return nil, fmt.Errorf("_CTLIOCGINFO: %v", errno) } sc := sockaddrCtl{ @@ -105,148 +88,133 @@ func CreateTUN(name string) (ifce TUNDevice, err error) { ) if errno != 0 { - err = errno - return nil, fmt.Errorf("error in unix.RawSyscall(unix.SYS_CONNECT, ...): %v", err) + return nil, fmt.Errorf("SYS_CONNECT: %v", errno) } - // read (new) name of interface + return CreateTUNFromFile(os.NewFile(uintptr(fd), "")) +} - var ifName struct { - name [16]byte - } - ifNameSize := uintptr(16) +func CreateTUNFromFile(file *os.File) (TUNDevice, error) { - _, _, errno = unix.Syscall6( - unix.SYS_GETSOCKOPT, - uintptr(fd), - 2, /* #define SYSPROTO_CONTROL 2 */ - 2, /* #define UTUN_OPT_IFNAME 2 */ - uintptr(unsafe.Pointer(&ifName)), - uintptr(unsafe.Pointer(&ifNameSize)), 0) - - if errno != 0 { - err = errno - return nil, fmt.Errorf("error in unix.Syscall6(unix.SYS_GETSOCKOPT, ...): %v", err) - } - - device := &NativeTUN{ - name: string(ifName.name[:ifNameSize-1 /* -1 is for \0 */]), - f: os.NewFile(uintptr(fd), string(ifName.name[:])), + tun := &NativeTun{ + fd: file, mtu: 1500, events: make(chan TUNEvent, 10), errors: make(chan error, 1), } - // start listener + _, err := tun.Name() + if err != nil { + return nil, err + } - go func(native *NativeTUN) { - // TODO: Fix this very niave implementation + // TODO: Fix this very naive implementation + go func(tun *NativeTun) { var ( statusUp bool statusMTU int ) for ; ; time.Sleep(time.Second) { - intr, err := net.InterfaceByName(device.name) + intr, err := net.InterfaceByName(tun.name) if err != nil { - native.errors <- err + tun.errors <- err return } // Up / Down event up := (intr.Flags & net.FlagUp) != 0 if up != statusUp && up { - native.events <- TUNEventUp + tun.events <- TUNEventUp } if up != statusUp && !up { - native.events <- TUNEventDown + tun.events <- TUNEventDown } statusUp = up // MTU changes if intr.MTU != statusMTU { - native.events <- TUNEventMTUUpdate + tun.events <- TUNEventMTUUpdate } statusMTU = intr.MTU } - }(device) + }(tun) // set default MTU + err = tun.setMTU(DefaultMTU) - err = device.setMTU(DefaultMTU) - - return device, err + return tun, err } -var _ io.ReadWriteCloser = (*NativeTUN)(nil) +func (tun *NativeTun) Name() (string, error) { -func (t *NativeTUN) Events() chan TUNEvent { - return t.events -} - -func (t *NativeTUN) Read(to []byte) (int, error) { - t.rMu.Lock() - defer t.rMu.Unlock() - - if cap(t.rBuf) < len(to)+4 { - t.rBuf = make([]byte, len(to)+4) + var ifName struct { + name [16]byte } - t.rBuf = t.rBuf[:len(to)+4] + ifNameSize := uintptr(16) - n, err := t.f.Read(t.rBuf) - copy(to, t.rBuf[4:]) + _, _, 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) Read(buff []byte, offset int) (int, error) { + + buff = buff[offset-4:] + n, err := tun.fd.Read(buff[:]) + if n < 4 { + return 0, err + } return n - 4, err } -func (t *NativeTUN) Write(from []byte) (int, error) { +func (tun *NativeTun) Write(buff []byte, offset int) (int, error) { - if len(from) == 0 { - return 0, unix.EIO - } + // reserve space for header - t.wMu.Lock() - defer t.wMu.Unlock() + buff = buff[offset-4:] - if cap(t.wBuf) < len(from)+4 { - t.wBuf = make([]byte, len(from)+4) - } - t.wBuf = t.wBuf[:len(from)+4] + // add packet information header - // determine the IP Family for the NULL L2 Header + buff[0] = 0x00 + buff[1] = 0x00 + buff[2] = 0x00 - ipVer := from[0] >> 4 - if ipVer == ipv4.Version { - t.wBuf[3] = unix.AF_INET - } else if ipVer == ipv6.Version { - t.wBuf[3] = unix.AF_INET6 + if buff[4]>>4 == ipv6.Version { + buff[3] = unix.AF_INET6 } else { - return 0, errors.New("Unable to determine IP version from packet.") + buff[3] = unix.AF_INET } - copy(t.wBuf[4:], from) + // write - n, err := t.f.Write(t.wBuf) - return n - 4, err + return tun.fd.Write(buff) } -func (t *NativeTUN) Close() error { - - // lock to make sure no read/write is in process. - - t.rMu.Lock() - defer t.rMu.Unlock() - - t.wMu.Lock() - defer t.wMu.Unlock() - - return t.f.Close() +func (tun *NativeTun) Close() error { + return tun.fd.Close() } -func (t *NativeTUN) Name() string { - return t.name -} - -func (t *NativeTUN) setMTU(n int) error { +func (tun *NativeTun) setMTU(n int) error { // open datagram socket @@ -267,7 +235,7 @@ func (t *NativeTUN) setMTU(n int) error { // do ioctl call var ifr [32]byte - copy(ifr[:], t.name) + copy(ifr[:], tun.name) binary.LittleEndian.PutUint32(ifr[16:20], uint32(n)) _, _, errno := unix.Syscall( unix.SYS_IOCTL, @@ -277,13 +245,13 @@ func (t *NativeTUN) setMTU(n int) error { ) if errno != 0 { - return fmt.Errorf("Failed to set MTU on %s", t.name) + return fmt.Errorf("Failed to set MTU on %s", tun.name) } return nil } -func (t *NativeTUN) MTU() (int, error) { +func (tun *NativeTun) MTU() (int, error) { // open datagram socket @@ -302,7 +270,7 @@ func (t *NativeTUN) MTU() (int, error) { // do ioctl call var ifr [64]byte - copy(ifr[:], t.name) + copy(ifr[:], tun.name) _, _, errno := unix.Syscall( unix.SYS_IOCTL, uintptr(fd), @@ -310,7 +278,7 @@ func (t *NativeTUN) MTU() (int, error) { uintptr(unsafe.Pointer(&ifr[0])), ) if errno != 0 { - return 0, fmt.Errorf("Failed to get MTU on %s", t.name) + return 0, fmt.Errorf("Failed to get MTU on %s", tun.name) } // convert result to signed 32-bit int diff --git a/uapi_darwin.go b/uapi_darwin.go index 2850184..4cb4e62 100644 --- a/uapi_darwin.go +++ b/uapi_darwin.go @@ -1,6 +1,7 @@ package main import ( + "errors" "fmt" "golang.org/x/sys/unix" "net" @@ -44,23 +45,11 @@ func (l *UAPIListener) Addr() net.Addr { return nil } -func NewUAPIListener(name string) (net.Listener, error) { +func UAPIListen(name string, file *os.File) (net.Listener, error) { - // check if path exist + // wrap file in listener - err := os.MkdirAll(socketDirectory, 077) - if err != nil && !os.IsExist(err) { - return nil, err - } - - // open UNIX socket - - socketPath := path.Join( - socketDirectory, - fmt.Sprintf(socketName, name), - ) - - listener, err := net.Listen("unix", socketPath) + listener, err := net.FileListener(file) if err != nil { return nil, err } @@ -73,6 +62,13 @@ func NewUAPIListener(name string) (net.Listener, error) { // watch for deletion of socket + socketPath := path.Join( + socketDirectory, + fmt.Sprintf(socketName, name), + ) + + // watch for deletion of socket + go func(l *UAPIListener) { for ; ; time.Sleep(time.Second) { if _, err := os.Stat(socketPath); os.IsNotExist(err) { @@ -97,3 +93,56 @@ func NewUAPIListener(name string) (net.Listener, error) { return uapi, nil } + +func UAPIOpen(name string) (*os.File, error) { + + // check if path exist + + err := os.MkdirAll(socketDirectory, 0600) + if err != nil && !os.IsExist(err) { + return nil, err + } + + // open UNIX socket + + socketPath := path.Join( + socketDirectory, + fmt.Sprintf(socketName, name), + ) + + addr, err := net.ResolveUnixAddr("unix", socketPath) + if err != nil { + return nil, err + } + + listener, err := func() (*net.UnixListener, error) { + + // initial connection attempt + + listener, err := net.ListenUnix("unix", addr) + if err == nil { + return listener, nil + } + + // check if socket already active + + _, err = net.Dial("unix", socketPath) + if err == nil { + return nil, errors.New("unix socket in use") + } + + // cleanup & attempt again + + err = os.Remove(socketPath) + if err != nil { + return nil, err + } + return net.ListenUnix("unix", addr) + }() + + if err != nil { + return nil, err + } + + return listener.File() +} diff --git a/warning_default.go b/warning_default.go new file mode 100644 index 0000000..92d1b1d --- /dev/null +++ b/warning_default.go @@ -0,0 +1,19 @@ +// +build !linux + +package main + +import ( + "fmt" + "os" +) + +func Warning() { + fmt.Fprintln(os.Stderr, "WARNING WARNING WARNING WARNING WARNING WARNING WARNING") + fmt.Fprintln(os.Stderr, "W G") + fmt.Fprintln(os.Stderr, "W This is alpha software. It will very likely not G") + fmt.Fprintln(os.Stderr, "W do what it is supposed to do, and things may go G") + fmt.Fprintln(os.Stderr, "W horribly wrong. You have been warned. Proceed G") + fmt.Fprintln(os.Stderr, "W at your own risk. G") + fmt.Fprintln(os.Stderr, "W G") + fmt.Fprintln(os.Stderr, "WARNING WARNING WARNING WARNING WARNING WARNING WARNING") +} diff --git a/warning_linux.go b/warning_linux.go new file mode 100644 index 0000000..d82805f --- /dev/null +++ b/warning_linux.go @@ -0,0 +1,39 @@ +package main + +import ( + "fmt" + "os" +) + +func Warning() { + shouldQuit := os.Getenv("WG_I_PREFER_BUGGY_USERSPACE_TO_POLISHED_KMOD") != "1" + + fmt.Fprintln(os.Stderr, "WARNING WARNING WARNING WARNING WARNING WARNING WARNING") + fmt.Fprintln(os.Stderr, "W G") + fmt.Fprintln(os.Stderr, "W This is alpha software. It will very likely not G") + fmt.Fprintln(os.Stderr, "W do what it is supposed to do, and things may go G") + fmt.Fprintln(os.Stderr, "W horribly wrong. You have been warned. Proceed G") + fmt.Fprintln(os.Stderr, "W at your own risk. G") + fmt.Fprintln(os.Stderr, "W G") + fmt.Fprintln(os.Stderr, "W Furthermore, you are running this software on a G") + fmt.Fprintln(os.Stderr, "W Linux kernel, which is probably unnecessary and G") + fmt.Fprintln(os.Stderr, "W foolish. This is because the Linux kernel has G") + fmt.Fprintln(os.Stderr, "W built-in first class support for WireGuard, and G") + fmt.Fprintln(os.Stderr, "W this support is much more refined than this G") + fmt.Fprintln(os.Stderr, "W program. For more information on installing the G") + fmt.Fprintln(os.Stderr, "W kernel module, please visit: G") + fmt.Fprintln(os.Stderr, "W https://www.wireguard.com/install G") + if shouldQuit { + fmt.Fprintln(os.Stderr, "W G") + fmt.Fprintln(os.Stderr, "W If you still want to use this program, against G") + fmt.Fprintln(os.Stderr, "W the sage advice here, please first export this G") + fmt.Fprintln(os.Stderr, "W environment variable: G") + fmt.Fprintln(os.Stderr, "W WG_I_PREFER_BUGGY_USERSPACE_TO_POLISHED_KMOD=1 G") + } + fmt.Fprintln(os.Stderr, "W G") + fmt.Fprintln(os.Stderr, "WARNING WARNING WARNING WARNING WARNING WARNING WARNING") + + if shouldQuit { + os.Exit(1) + } +}