diff options
Diffstat (limited to 'tun_freebsd.go')
-rw-r--r-- | tun_freebsd.go | 247 |
1 files changed, 138 insertions, 109 deletions
diff --git a/tun_freebsd.go b/tun_freebsd.go index e83b8ef..e2ec511 100644 --- a/tun_freebsd.go +++ b/tun_freebsd.go @@ -12,11 +12,8 @@ import ( "fmt" "golang.org/x/net/ipv6" "golang.org/x/sys/unix" - "io/ioutil" "net" "os" - "path/filepath" - "time" "unsafe" ) @@ -29,38 +26,93 @@ const _TUNSIFPID = 0x2000745f // Iface status string max len const _IFSTATMAX = 800 +const SIZEOF_UINTPTR = 4 << (^uintptr(0) >> 32 & 1) + +// structure for iface requests with a pointer +type ifreq_ptr struct { + Name [unix.IFNAMSIZ]byte + Data uintptr + Pad0 [24 - SIZEOF_UINTPTR]byte +} + // Structure for iface mtu get/set ioctls type ifreq_mtu struct { - Name [_IFNAMESIZ]byte + Name [unix.IFNAMSIZ]byte MTU uint32 Pad0 [12]byte } // Structure for interface status request ioctl type ifstat struct { - IfsName [_IFNAMESIZ]byte + IfsName [unix.IFNAMSIZ]byte Ascii [_IFSTATMAX]byte } -// 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 { - name string - fd *os.File - rwcancel *rwcancel.RWCancel - mtu int - events chan TUNEvent - errors chan error - statusListenersShutdown chan struct{} + name string + fd *os.File + rwcancel *rwcancel.RWCancel + events chan TUNEvent + errors chan error + routeSocket int +} + +func (tun *NativeTun) RoutineRouteListener(tunIfindex int) { + var ( + statusUp bool + statusMTU int + ) + + defer close(tun.events) + + data := make([]byte, os.Getpagesize()) + for { + n, err := unix.Read(tun.routeSocket, data) + if err != nil { + tun.errors <- err + return + } + + if n < 14 { + continue + } + + if data[3 /* type */] != unix.RTM_IFINFO { + continue + } + ifindex := int(*(*uint16)(unsafe.Pointer(&data[12 /* ifindex */]))) + if ifindex != tunIfindex { + continue + } + + iface, err := net.InterfaceByIndex(ifindex) + if err != nil { + tun.errors <- err + return + } + + // Up / Down event + up := (iface.Flags & net.FlagUp) != 0 + if up != statusUp && up { + tun.events <- TUNEventUp + } + if up != statusUp && !up { + tun.events <- TUNEventDown + } + statusUp = up + + // MTU changes + if iface.MTU != statusMTU { + tun.events <- TUNEventMTUUpdate + } + statusMTU = iface.MTU + } } -// Figure out the interface name for an open tun device file descriptor -func TunIfaceName(f *os.File) (string, error) { +func tunName(fd uintptr) (string, error) { //Terrible hack to make up for freebsd not having a TUNGIFNAME - fd := f.Fd() //First, make sure the tun pid matches this proc's pid - _, _, errno := unix.Syscall( unix.SYS_IOCTL, uintptr(fd), @@ -69,7 +121,7 @@ func TunIfaceName(f *os.File) (string, error) { ) if errno != 0 { - return "", fmt.Errorf("Failed to set tun device PID: %s", errno.Error()) + return "", fmt.Errorf("failed to set tun device PID: %s", errno.Error()) } // Open iface control socket @@ -80,12 +132,12 @@ func TunIfaceName(f *os.File) (string, error) { 0, ) - defer unix.Close(confd) - if err != nil { return "", err } + defer unix.Close(confd) + procPid := os.Getpid() //Try to find interface with matching PID @@ -97,8 +149,7 @@ func TunIfaceName(f *os.File) (string, error) { // Structs for getting data in and out of SIOCGIFSTATUS ioctl var ifstatus ifstat - ifname := iface.Name - copy(ifstatus.IfsName[:], ifname) + copy(ifstatus.IfsName[:], iface.Name) // Make the syscall to get the status string _, _, errno := unix.Syscall( @@ -117,30 +168,26 @@ func TunIfaceName(f *os.File) (string, error) { if i < 1 { continue } - if i != -1 { - nullStr = nullStr[:i] - } - statStr := string(nullStr) + statStr := string(nullStr[:i]) var pidNum int = 0 // Finally get the owning PID // Format string taken from sys/net/if_tun.c _, err := fmt.Sscanf(statStr, "\tOpened by PID %d\n", &pidNum) if err != nil { - return "", err + continue } if pidNum == procPid { - return ifname, nil + return iface.Name, nil } - } return "", nil } // Destroy a named system interface -func DestroyIface(name string) error { +func tunDestroy(name string) error { // open control socket var fd int @@ -168,21 +215,21 @@ func DestroyIface(name string) error { ) if errno != 0 { - return fmt.Errorf("Failed to destroy interface %s: %s", name, errno.Error()) + return fmt.Errorf("failed to destroy interface %s: %s", name, errno.Error()) } return nil } func CreateTUN(name string) (TUNDevice, error) { - if len(name) > _IFNAMESIZ-1 { - return nil, errors.New("Interface name too long") + if len(name) > unix.IFNAMSIZ-1 { + return nil, errors.New("interface name too long") } // See if interface already exists iface, _ := net.InterfaceByName(name) if iface != nil { - return nil, fmt.Errorf("Interface %s already exists", name) + return nil, fmt.Errorf("interface %s already exists", name) } tunfile, err := os.OpenFile("/dev/tun", unix.O_RDWR, 0) @@ -190,16 +237,13 @@ func CreateTUN(name string) (TUNDevice, error) { if err != nil { return nil, err } - - nameif, err := TunIfaceName(tunfile) - + tunfd := tunfile.Fd() + assignedName, err := tunName(tunfd) if err != nil { tunfile.Close() return nil, err } - tunfd := tunfile.Fd() - // Enable ifhead mode, otherwise tun will complain if it gets a non-AF_INET packet ifheadmode := 1 _, _, errno := unix.Syscall( @@ -210,11 +254,10 @@ func CreateTUN(name string) (TUNDevice, error) { ) if errno != 0 { - return nil, fmt.Errorf("Error %s", errno.Error()) + return nil, fmt.Errorf("error %s", errno.Error()) } - /* Set TUN iface to broadcast mode. TUN inferfaces on freebsd come up - * point to point by default */ + // Set TUN iface to broadcast mode. TUN inferfaces on freebsd come up in point to point by default ifmodemode := unix.IFF_BROADCAST _, _, errno = unix.Syscall( unix.SYS_IOCTL, @@ -224,12 +267,13 @@ func CreateTUN(name string) (TUNDevice, error) { ) if errno != 0 { - return nil, fmt.Errorf("Error %s", errno.Error()) + return nil, fmt.Errorf("error %s", errno.Error()) } // Rename tun interface + // Open control socket - ctfd, err := unix.Socket( + confd, err := unix.Socket( unix.AF_INET, unix.SOCK_DGRAM, 0, @@ -239,27 +283,27 @@ func CreateTUN(name string) (TUNDevice, error) { return nil, err } - defer unix.Close(ctfd) + defer unix.Close(confd) // set up struct for iface rename - var newnp [_IFNAMESIZ]byte + var newnp [unix.IFNAMSIZ]byte copy(newnp[:], name) var ifr ifreq_ptr - copy(ifr.Name[:], nameif) + copy(ifr.Name[:], assignedName) ifr.Data = uintptr(unsafe.Pointer(&newnp[0])) //do actual ioctl to rename iface _, _, errno = unix.Syscall( unix.SYS_IOCTL, - uintptr(ctfd), + uintptr(confd), uintptr(unix.SIOCSIFNAME), uintptr(unsafe.Pointer(&ifr)), ) if errno != 0 { tunfile.Close() - DestroyIface(name) - return nil, fmt.Errorf("Failed to rename %s to %s: %s", nameif, name, errno.Error()) + tunDestroy(name) + return nil, fmt.Errorf("failed to rename %s to %s: %s", assignedName, name, errno.Error()) } tun, err := CreateTUNFromFile(tunfile) @@ -267,29 +311,34 @@ func CreateTUN(name string) (TUNDevice, error) { if err != nil { return nil, err } - - if err == nil && name == "tun" { - fname := os.Getenv("WG_FREEBSD_TUN_NAME_FILE") - if fname != "" { - os.MkdirAll(filepath.Dir(fname), 0700) - ioutil.WriteFile(fname, []byte(tun.(*NativeTun).name+"\n"), 0400) - } - } - return tun, err } func CreateTUNFromFile(file *os.File) (TUNDevice, error) { tun := &NativeTun{ - fd: file, - mtu: 1500, - events: make(chan TUNEvent, 10), - errors: make(chan error, 1), - statusListenersShutdown: make(chan struct{}), + fd: file, + events: make(chan TUNEvent, 10), + errors: make(chan error, 1), } - _, err := tun.Name() + name, err := tun.Name() + if err != nil { + tun.fd.Close() + return nil, err + } + + tunIfindex, err := func() (int, error) { + iface, err := net.InterfaceByName(name) + if err != nil { + return -1, err + } + return iface.Index, nil + }() + if err != nil { + tun.fd.Close() + return nil, err + } if err != nil { tun.fd.Close() @@ -302,43 +351,13 @@ func CreateTUNFromFile(file *os.File) (TUNDevice, error) { return nil, err } - // TODO: Fix this very naive implementation - go func(tun *NativeTun) { - var ( - statusUp bool - statusMTU int - ) + tun.routeSocket, err = unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC) + if err != nil { + tun.fd.Close() + return nil, err + } - for { - intr, err := net.InterfaceByName(tun.name) - if err != nil { - tun.errors <- err - return - } - - // Up / Down event - up := (intr.Flags & net.FlagUp) != 0 - if up != statusUp && up { - tun.events <- TUNEventUp - } - if up != statusUp && !up { - tun.events <- TUNEventDown - } - statusUp = up - - // MTU changes - if intr.MTU != statusMTU { - tun.events <- TUNEventMTUUpdate - } - statusMTU = intr.MTU - - select { - case <-time.After(time.Second / 10): - case <-tun.statusListenersShutdown: - return - } - } - }(tun) + go tun.RoutineRouteListener(tunIfindex) // set default MTU err = tun.setMTU(DefaultMTU) @@ -351,7 +370,7 @@ func CreateTUNFromFile(file *os.File) (TUNDevice, error) { } func (tun *NativeTun) Name() (string, error) { - name, err := TunIfaceName(tun.fd) + name, err := tunName(tun.fd.Fd()) if err != nil { return "", err } @@ -417,18 +436,28 @@ func (tun *NativeTun) Write(buff []byte, offset int) (int, error) { } func (tun *NativeTun) Close() error { - close(tun.statusListenersShutdown) + var err4 error err1 := tun.rwcancel.Cancel() err2 := tun.fd.Close() - err3 := DestroyIface(tun.name) - close(tun.events) + err3 := tunDestroy(tun.name) + if tun.routeSocket != -1 { + // Surprisingly, on FreeBSD, simply closing a route socket is enough to unblock it. + // We don't even need to call shutdown, or use a rwcancel. TODO: CONFIRM THIS CLAIM. IT WAS TRUE ON DARWIN BUT... + err4 = unix.Close(tun.routeSocket) + tun.routeSocket = -1 + } else if tun.events != nil { + close(tun.events) + } if err1 != nil { return err1 } if err2 != nil { return err2 } - return err3 + if err3 != nil { + return err3 + } + return err4 } func (tun *NativeTun) setMTU(n int) error { @@ -462,7 +491,7 @@ func (tun *NativeTun) setMTU(n int) error { ) if errno != 0 { - return fmt.Errorf("Failed to set MTU on %s", tun.name) + return fmt.Errorf("failed to set MTU on %s", tun.name) } return nil @@ -494,7 +523,7 @@ func (tun *NativeTun) MTU() (int, error) { uintptr(unsafe.Pointer(&ifr)), ) if errno != 0 { - return 0, fmt.Errorf("Failed to get MTU on %s", tun.name) + return 0, fmt.Errorf("failed to get MTU on %s", tun.name) } // convert result to signed 32-bit int |