package main /* Implementation of the TUN device interface for linux */ import ( "encoding/binary" "errors" "golang.org/x/sys/unix" "net" "os" "strings" "unsafe" ) const CloneDevicePath = "/dev/net/tun" type NativeTun struct { fd *os.File name string events chan TUNEvent } func (tun *NativeTun) isUp() (bool, error) { inter, err := net.InterfaceByName(tun.name) return inter.Flags&net.FlagUp != 0, err } func (tun *NativeTun) Name() string { return tun.name } func (tun *NativeTun) setMTU(n int) error { // open datagram socket 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 [64]byte copy(ifr[:], tun.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 errors.New("Failed to set MTU of TUN device") } return nil } func (tun *NativeTun) 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[:], tun.name) _, _, errno := unix.Syscall( unix.SYS_IOCTL, uintptr(fd), uintptr(unix.SIOCGIFMTU), uintptr(unsafe.Pointer(&ifr[0])), ) if errno != 0 { return 0, errors.New("Failed to get MTU of TUN device") } // 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 } func (tun *NativeTun) Write(d []byte) (int, error) { return tun.fd.Write(d) } func (tun *NativeTun) Read(d []byte) (int, error) { return tun.fd.Read(d) } func (tun *NativeTun) Events() chan TUNEvent { return tun.events } func (tun *NativeTun) Close() error { return nil } func CreateTUN(name string) (TUNDevice, error) { // open clone device fd, err := os.OpenFile(CloneDevicePath, os.O_RDWR, 0) if err != nil { return nil, err } // create new device var ifr [64]byte var flags uint16 = unix.IFF_TUN | unix.IFF_NO_PI nameBytes := []byte(name) if len(nameBytes) >= unix.IFNAMSIZ { return nil, errors.New("Name size too long") } copy(ifr[:], nameBytes) binary.LittleEndian.PutUint16(ifr[16:], flags) _, _, errno := unix.Syscall( unix.SYS_IOCTL, uintptr(fd.Fd()), uintptr(unix.TUNSETIFF), uintptr(unsafe.Pointer(&ifr[0])), ) if errno != 0 { return nil, errors.New("Failed to create tun, ioctl call failed") } // read (new) name of interface newName := string(ifr[:]) newName = newName[:strings.Index(newName, "\000")] device := &NativeTun{ fd: fd, name: newName, events: make(chan TUNEvent, 5), } // TODO: Wait for device to be upped device.events <- TUNEventUp // set default MTU err = device.setMTU(DefaultMTU) return device, err }