From 086d32186ad4bddbed8bacc7f99705c8e62a1f9d Mon Sep 17 00:00:00 2001 From: Mathias Hall-Andersen Date: Wed, 19 Jul 2017 13:12:25 +0200 Subject: [PATCH 1/3] Reviewed and added OSX patch The patch was provided by: Naveen Nathan The following modifications to the patch was made: - Added copyright notice - Fixed file descriptor leak in .MTU() method - Migrated to the new(er) golang.org/x/sys/unix package - Removed non-functioning Daemonize method --- src/daemon_darwin.go | 9 ++ src/tun_darwin.go | 278 +++++++++++++++++++++++++++++++++++++++++++ src/uapi_darwin.go | 83 +++++++++++++ 3 files changed, 370 insertions(+) create mode 100644 src/daemon_darwin.go create mode 100644 src/tun_darwin.go create mode 100644 src/uapi_darwin.go diff --git a/src/daemon_darwin.go b/src/daemon_darwin.go new file mode 100644 index 0000000..913af0e --- /dev/null +++ b/src/daemon_darwin.go @@ -0,0 +1,9 @@ +package main + +import ( + "errors" +) + +func Daemonize() error { + return errors.New("Not implemented on OSX") +} diff --git a/src/tun_darwin.go b/src/tun_darwin.go new file mode 100644 index 0000000..146817d --- /dev/null +++ b/src/tun_darwin.go @@ -0,0 +1,278 @@ +/* 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" + "os" + "sync" + "unsafe" +) + +const utunControlName = "com.apple.net.utun_control" + +// _CTLIOCGINFO value derived from /usr/include/sys/{kern_control,ioccom}.h +const _CTLIOCGINFO = (0x40000000 | 0x80000000) | ((100 & 0x1fff) << 16) | uint32(byte('N'))<<8 | 3 + +// sockaddr_ctl specifeid in /usr/include/sys/kern_control.h +type sockaddrCtl struct { + scLen uint8 + scFamily uint8 + ssSysaddr uint16 + scID uint32 + scUnit uint32 + scReserved [5]uint32 +} + +var sockaddrCtlSize uintptr = 32 + +func CreateTUN(name string) (ifce TUNDevice, err error) { + ifIndex := -1 + fmt.Sscanf(name, "utun%d", &ifIndex) + if ifIndex < 0 { + return nil, fmt.Errorf("error parsing interface name %s, must be utun[0-9]+", name) + } + + fd, err := unix.Socket(unix.AF_SYSTEM, unix.SOCK_DGRAM, 2) + + if err != nil { + return nil, fmt.Errorf("error in unix.Socket: %v", err) + } + + var ctlInfo = &struct { + ctlID uint32 + ctlName [96]byte + }{} + + copy(ctlInfo.ctlName[:], []byte(utunControlName)) + + _, _, errno := unix.Syscall( + unix.SYS_IOCTL, + uintptr(fd), + uintptr(_CTLIOCGINFO), + uintptr(unsafe.Pointer(ctlInfo)), + ) + + if errno != 0 { + err = errno + return nil, fmt.Errorf("error in unix.Syscall(unix.SYS_IOTL, ...): %v", err) + } + + sc := sockaddrCtl{ + scLen: uint8(sockaddrCtlSize), + scFamily: unix.AF_SYSTEM, + ssSysaddr: 2, + scID: ctlInfo.ctlID, + scUnit: uint32(ifIndex) + 1, + } + + scPointer := unsafe.Pointer(&sc) + + _, _, errno = unix.RawSyscall( + unix.SYS_CONNECT, + uintptr(fd), + uintptr(scPointer), + uintptr(sockaddrCtlSize), + ) + + if errno != 0 { + err = errno + return nil, fmt.Errorf("error in unix.RawSyscall(unix.SYS_CONNECT, ...): %v", err) + } + + // read (new) name of interface + + var ifName struct { + name [16]byte + } + ifNameSize := uintptr(16) + + _, _, 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 := &tunReadCloser{ + name: string(ifName.name[:ifNameSize-1 /* -1 is for \0 */]), + f: os.NewFile(uintptr(fd), string(ifName.name[:])), + mtu: 1500, + } + + // set default MTU + + err = device.setMTU(DefaultMTU) + + return device, err +} + +// 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 { + name string + f io.ReadWriteCloser + mtu int + + rMu sync.Mutex + rBuf []byte + + wMu sync.Mutex + wBuf []byte +} + +var _ io.ReadWriteCloser = (*tunReadCloser)(nil) + +func (t *tunReadCloser) 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) + } + t.rBuf = t.rBuf[:len(to)+4] + + n, err := t.f.Read(t.rBuf) + copy(to, t.rBuf[4:]) + return n - 4, err +} + +func (t *tunReadCloser) Write(from []byte) (int, error) { + + if len(from) == 0 { + return 0, unix.EIO + } + + t.wMu.Lock() + defer t.wMu.Unlock() + + if cap(t.wBuf) < len(from)+4 { + t.wBuf = make([]byte, len(from)+4) + } + t.wBuf = t.wBuf[:len(from)+4] + + // determine the IP Family for the NULL L2 Header + + 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 + } else { + return 0, errors.New("Unable to determine IP version from packet.") + } + + copy(t.wBuf[4:], from) + + n, err := t.f.Write(t.wBuf) + return n - 4, err +} + +func (t *tunReadCloser) 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 (t *tunReadCloser) Name() string { + return t.name +} + +func (t *tunReadCloser) setMTU(n int) error { + + // open datagram 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[:], t.name) + binary.LittleEndian.PutUint32(ifr[16:20], uint32(n)) + _, _, errno := unix.Syscall( + unix.SYS_IOCTL, + uintptr(fd), + uintptr(unix.SIOCSIFMTU), + uintptr(unsafe.Pointer(&ifr[0])), + ) + + if errno != 0 { + return fmt.Errorf("Failed to set MTU on %s", t.name) + } + + return nil +} + +func (t *tunReadCloser) MTU() (int, error) { + + // open datagram socket + + fd, err := unix.Socket( + unix.AF_INET, + unix.SOCK_DGRAM, + 0, + ) + + if err != nil { + return 0, err + } + + defer unix.Close(fd) + + // do ioctl call + + var ifr [64]byte + copy(ifr[:], t.name) + _, _, errno := unix.Syscall( + unix.SYS_IOCTL, + uintptr(fd), + uintptr(unix.SIOCGIFMTU), + uintptr(unsafe.Pointer(&ifr[0])), + ) + if errno != 0 { + return 0, fmt.Errorf("Failed to get MTU on %s", t.name) + } + + // convert result to signed 32-bit int + + val := binary.LittleEndian.Uint32(ifr[16:20]) + if val >= (1 << 31) { + return int(val-(1<<31)) - (1 << 31), nil + } + return int(val), nil +} diff --git a/src/uapi_darwin.go b/src/uapi_darwin.go new file mode 100644 index 0000000..ee6ee0b --- /dev/null +++ b/src/uapi_darwin.go @@ -0,0 +1,83 @@ +package main + +import ( + "fmt" + "net" + "os" + "time" +) + +/* TODO: + * This code can be improved by using fsnotify once: + * https://github.com/fsnotify/fsnotify/pull/205 + * Is merged + */ + +type UAPIListener struct { + listener net.Listener // unix socket listener + connNew chan net.Conn + connErr chan error +} + +func (l *UAPIListener) Accept() (net.Conn, error) { + for { + select { + case conn := <-l.connNew: + return conn, nil + + case err := <-l.connErr: + return nil, err + } + } +} + +func (l *UAPIListener) Close() error { + return l.listener.Close() +} + +func (l *UAPIListener) Addr() net.Addr { + return nil +} + +func NewUAPIListener(name string) (net.Listener, error) { + + // open UNIX socket + + socketPath := fmt.Sprintf("/var/run/wireguard/%s.sock", name) + listener, err := net.Listen("unix", socketPath) + if err != nil { + return nil, err + } + + uapi := &UAPIListener{ + listener: listener, + connNew: make(chan net.Conn, 1), + connErr: make(chan error, 1), + } + + // watch for deletion of socket + + go func(l *UAPIListener) { + for ; ; time.Sleep(time.Second) { + if _, err := os.Stat(socketPath); os.IsNotExist(err) { + l.connErr <- err + return + } + } + }(uapi) + + // watch for new connections + + go func(l *UAPIListener) { + for { + conn, err := l.listener.Accept() + if err != nil { + l.connErr <- err + break + } + l.connNew <- conn + } + }(uapi) + + return uapi, nil +} From 2a6dd2ed926ddc44cd98d8c940aa01f830213ea0 Mon Sep 17 00:00:00 2001 From: Mathias Hall-Andersen Date: Fri, 21 Jul 2017 15:17:43 +0200 Subject: [PATCH 2/3] Fixed UAPI deadlock --- src/config.go | 11 ++++------- src/noise_protocol.go | 2 -- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/config.go b/src/config.go index d92e8d7..2d9ac50 100644 --- a/src/config.go +++ b/src/config.go @@ -106,17 +106,17 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError { key := parts[0] value := parts[1] + fmt.Println(key, value) + switch key { /* interface configuration */ case "private_key": + var sk NoisePrivateKey if value == "" { - device.mutex.Lock() - device.privateKey = NoisePrivateKey{} - device.mutex.Unlock() + device.SetPrivateKey(sk) } else { - var sk NoisePrivateKey err := sk.FromHex(value) if err != nil { logError.Println("Failed to set private_key:", err) @@ -183,9 +183,7 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError { switch key { case "remove": - peer.mutex.Lock() device.RemovePeer(peer.handshake.remoteStatic) - peer.mutex.Unlock() logDebug.Println("Removing", peer.String()) peer = nil @@ -236,7 +234,6 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError { return &IPCError{Code: ipcErrorInvalidValue} } ones, _ := network.Mask.Size() - logError.Println(network, ones, network.IP) device.routingTable.Insert(network.IP, uint(ones), peer) default: diff --git a/src/noise_protocol.go b/src/noise_protocol.go index 5fe6fb2..ea0fd52 100644 --- a/src/noise_protocol.go +++ b/src/noise_protocol.go @@ -253,8 +253,6 @@ func (device *Device) ConsumeMessageInitiation(msg *MessageInitiation) *Peer { } hash = mixHash(hash, msg.Timestamp[:]) - // TODO: check for flood attack - // check for replay attack return timestamp.After(handshake.lastTimestamp) From c3d9ae402d431b7697686dbaf021f879c8ccab36 Mon Sep 17 00:00:00 2001 From: Mathias Hall-Andersen Date: Sun, 23 Jul 2017 16:21:08 +0200 Subject: [PATCH 3/3] Close UDP connection when listen port changes --- src/config.go | 31 ++++++++++++++----------------- src/uapi_darwin.go | 6 ------ 2 files changed, 14 insertions(+), 23 deletions(-) diff --git a/src/config.go b/src/config.go index c889de0..9751a18 100644 --- a/src/config.go +++ b/src/config.go @@ -2,7 +2,6 @@ package main import ( "bufio" - "errors" "fmt" "io" "net" @@ -105,8 +104,6 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError { key := parts[0] value := parts[1] - fmt.Println(key, value) - switch key { /* interface configuration */ @@ -125,16 +122,21 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError { } case "listen_port": - var port int - _, err := fmt.Sscanf(value, "%d", &port) - if err != nil || port > (1<<16) || port < 0 { + port, err := strconv.ParseUint(value, 10, 16) + if err != nil { logError.Println("Failed to set listen_port:", err) return &IPCError{Code: ipcErrorInvalidValue} } - device.net.mutex.Lock() - device.net.addr.Port = port - device.net.conn, err = net.ListenUDP("udp", device.net.addr) - device.net.mutex.Unlock() + netc := &device.net + netc.mutex.Lock() + if netc.addr.Port != int(port) { + if netc.conn != nil { + netc.conn.Close() + } + netc.addr.Port = int(port) + netc.conn, err = net.ListenUDP("udp", netc.addr) + } + netc.mutex.Unlock() if err != nil { logError.Println("Failed to create UDP listener:", err) return &IPCError{Code: ipcErrorInvalidValue} @@ -151,15 +153,10 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError { return &IPCError{Code: ipcErrorInvalidValue} } device.mutex.RLock() - found, ok := device.peers[pubKey] + peer, _ := device.peers[pubKey] device.mutex.RUnlock() - if ok { - peer = found - } else { - peer = device.NewPeer(pubKey) - } if peer == nil { - panic(errors.New("bug: failed to find / create peer")) + peer = device.NewPeer(pubKey) } case "replace_peers": diff --git a/src/uapi_darwin.go b/src/uapi_darwin.go index ee6ee0b..9eee53c 100644 --- a/src/uapi_darwin.go +++ b/src/uapi_darwin.go @@ -7,12 +7,6 @@ import ( "time" ) -/* TODO: - * This code can be improved by using fsnotify once: - * https://github.com/fsnotify/fsnotify/pull/205 - * Is merged - */ - type UAPIListener struct { listener net.Listener // unix socket listener connNew chan net.Conn