diff options
Diffstat (limited to 'tun_darwin.go')
-rw-r--r-- | tun_darwin.go | 186 |
1 files changed, 77 insertions, 109 deletions
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 <song@gao.io> - * 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) - } - - // read (new) name of interface - - var ifName struct { - name [16]byte + return nil, fmt.Errorf("SYS_CONNECT: %v", errno) } - 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) + return CreateTUNFromFile(os.NewFile(uintptr(fd), "")) +} - if errno != 0 { - err = errno - return nil, fmt.Errorf("error in unix.Syscall6(unix.SYS_GETSOCKOPT, ...): %v", err) - } +func CreateTUNFromFile(file *os.File) (TUNDevice, error) { - 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 -} + var ifName struct { + name [16]byte + } + ifNameSize := uintptr(16) -func (t *NativeTUN) Read(to []byte) (int, error) { - t.rMu.Lock() - defer t.rMu.Unlock() + _, _, 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 cap(t.rBuf) < len(to)+4 { - t.rBuf = make([]byte, len(to)+4) + if errno != 0 { + return "", fmt.Errorf("SYS_GETSOCKOPT: %v", errno) } - t.rBuf = t.rBuf[:len(to)+4] - n, err := t.f.Read(t.rBuf) - copy(to, t.rBuf[4:]) - return n - 4, err + tun.name = string(ifName.name[:ifNameSize-1]) + return tun.name, nil } -func (t *NativeTUN) Write(from []byte) (int, error) { +func (tun *NativeTun) File() *os.File { + return tun.fd +} - if len(from) == 0 { - return 0, unix.EIO - } +func (tun *NativeTun) Events() chan TUNEvent { + return tun.events +} - t.wMu.Lock() - defer t.wMu.Unlock() +func (tun *NativeTun) Read(buff []byte, offset int) (int, error) { - if cap(t.wBuf) < len(from)+4 { - t.wBuf = make([]byte, len(from)+4) + buff = buff[offset-4:] + n, err := tun.fd.Read(buff[:]) + if n < 4 { + return 0, err } - t.wBuf = t.wBuf[:len(from)+4] + return n - 4, err +} - // determine the IP Family for the NULL L2 Header +func (tun *NativeTun) Write(buff []byte, offset int) (int, error) { - 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.") - } + // reserve space for header - copy(t.wBuf[4:], from) + buff = buff[offset-4:] - n, err := t.f.Write(t.wBuf) - return n - 4, err -} - -func (t *NativeTUN) Close() error { + // add packet information header - // lock to make sure no read/write is in process. + buff[0] = 0x00 + buff[1] = 0x00 + buff[2] = 0x00 - t.rMu.Lock() - defer t.rMu.Unlock() + if buff[4]>>4 == ipv6.Version { + buff[3] = unix.AF_INET6 + } else { + buff[3] = unix.AF_INET + } - t.wMu.Lock() - defer t.wMu.Unlock() + // write - return t.f.Close() + return tun.fd.Write(buff) } -func (t *NativeTUN) Name() string { - return t.name +func (tun *NativeTun) Close() error { + return tun.fd.Close() } -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 |