diff options
author | Mathias Hall-Andersen <mathias@hall-andersen.dk> | 2018-02-04 16:08:26 +0100 |
---|---|---|
committer | Mathias Hall-Andersen <mathias@hall-andersen.dk> | 2018-02-04 16:08:26 +0100 |
commit | a0f54cbe5ac2cd8b8296c2c57c30029dd349cff0 (patch) | |
tree | 64574090d79ff3899c5c18e5268e450028e4656b /src/tun_linux.go | |
parent | 5871ec04deb8f4715cab37146940baa35c08cbee (diff) |
Align with go library layout
Diffstat (limited to 'src/tun_linux.go')
-rw-r--r-- | src/tun_linux.go | 377 |
1 files changed, 0 insertions, 377 deletions
diff --git a/src/tun_linux.go b/src/tun_linux.go deleted file mode 100644 index 9756169..0000000 --- a/src/tun_linux.go +++ /dev/null @@ -1,377 +0,0 @@ -package main - -/* Implementation of the TUN device interface for linux - */ - -import ( - "encoding/binary" - "errors" - "fmt" - "golang.org/x/net/ipv6" - "golang.org/x/sys/unix" - "net" - "os" - "strings" - "time" - "unsafe" -) - -// #include <string.h> -// #include <unistd.h> -// #include <net/if.h> -// #include <netinet/in.h> -// #include <linux/netlink.h> -// #include <linux/rtnetlink.h> -// -// /* Creates a netlink socket -// * listening to the RTMGRP_LINK multicast group -// */ -// -// int bind_rtmgrp() { -// int nl_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); -// if (nl_sock < 0) -// return -1; -// -// struct sockaddr_nl addr; -// memset ((void *) &addr, 0, sizeof (addr)); -// addr.nl_family = AF_NETLINK; -// addr.nl_pid = getpid (); -// addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR; -// -// if (bind(nl_sock, (struct sockaddr *) &addr, sizeof (addr)) < 0) -// return -1; -// -// return nl_sock; -// } -import "C" - -const ( - CloneDevicePath = "/dev/net/tun" - IFReqSize = unix.IFNAMSIZ + 64 -) - -type NativeTun struct { - fd *os.File - index int32 // if index - name string // name of interface - errors chan error // async error handling - events chan TUNEvent // device related events -} - -func (tun *NativeTun) File() *os.File { - return tun.fd -} - -func (tun *NativeTun) RoutineHackListener() { - /* This is needed for the detection to work across network namespaces - * If you are reading this and know a better method, please get in touch. - */ - fd := int(tun.fd.Fd()) - for { - _, err := unix.Write(fd, nil) - switch err { - case unix.EINVAL: - tun.events <- TUNEventUp - case unix.EIO: - tun.events <- TUNEventDown - default: - } - time.Sleep(time.Second / 10) - } -} - -func (tun *NativeTun) RoutineNetlinkListener() { - - sock := int(C.bind_rtmgrp()) - if sock < 0 { - tun.errors <- errors.New("Failed to create netlink event listener") - return - } - - for msg := make([]byte, 1<<16); ; { - - msgn, _, _, _, err := unix.Recvmsg(sock, msg[:], nil, 0) - if err != nil { - tun.errors <- fmt.Errorf("Failed to receive netlink message: %s", err.Error()) - return - } - - for remain := msg[:msgn]; len(remain) >= unix.SizeofNlMsghdr; { - - hdr := *(*unix.NlMsghdr)(unsafe.Pointer(&remain[0])) - - if int(hdr.Len) > len(remain) { - break - } - - switch hdr.Type { - case unix.NLMSG_DONE: - remain = []byte{} - - case unix.RTM_NEWLINK: - info := *(*unix.IfInfomsg)(unsafe.Pointer(&remain[unix.SizeofNlMsghdr])) - remain = remain[hdr.Len:] - - if info.Index != tun.index { - // not our interface - continue - } - - if info.Flags&unix.IFF_RUNNING != 0 { - tun.events <- TUNEventUp - } - - if info.Flags&unix.IFF_RUNNING == 0 { - tun.events <- TUNEventDown - } - - tun.events <- TUNEventMTUUpdate - - default: - remain = remain[hdr.Len:] - } - } - } -} - -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 getDummySock() (int, error) { - return unix.Socket( - unix.AF_INET, - unix.SOCK_DGRAM, - 0, - ) -} - -func getIFIndex(name string) (int32, error) { - fd, err := getDummySock() - if err != nil { - return 0, err - } - - defer unix.Close(fd) - - var ifr [IFReqSize]byte - copy(ifr[:], name) - _, _, errno := unix.Syscall( - unix.SYS_IOCTL, - uintptr(fd), - uintptr(unix.SIOCGIFINDEX), - uintptr(unsafe.Pointer(&ifr[0])), - ) - - if errno != 0 { - return 0, errno - } - - index := binary.LittleEndian.Uint32(ifr[unix.IFNAMSIZ:]) - return toInt32(index), nil -} - -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 [IFReqSize]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 [IFReqSize]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(toInt32(val)), nil - } - return int(val), nil -} - -func (tun *NativeTun) Write(buff []byte, offset int) (int, error) { - - // reserve space for header - - buff = buff[offset-4:] - - // add packet information header - - buff[0] = 0x00 - buff[1] = 0x00 - - if buff[4] == ipv6.Version<<4 { - buff[2] = 0x86 - buff[3] = 0xdd - } else { - buff[2] = 0x08 - buff[3] = 0x00 - } - - // write - - return tun.fd.Write(buff) -} - -func (tun *NativeTun) Read(buff []byte, offset int) (int, error) { - select { - case err := <-tun.errors: - return 0, err - default: - buff := buff[offset-4:] - n, err := tun.fd.Read(buff[:]) - if n < 4 { - return 0, err - } - return n - 4, err - } -} - -func (tun *NativeTun) Events() chan TUNEvent { - return tun.events -} - -func (tun *NativeTun) Close() error { - return nil -} - -func CreateTUNFromFile(name string, fd *os.File) (TUNDevice, error) { - device := &NativeTun{ - fd: fd, - name: name, - events: make(chan TUNEvent, 5), - errors: make(chan error, 5), - } - - // start event listener - - var err error - device.index, err = getIFIndex(device.name) - if err != nil { - return nil, err - } - - go device.RoutineNetlinkListener() - // go device.RoutineHackListener() // cross namespace - - // set default MTU - - return device, device.setMTU(DefaultMTU) -} - -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 [IFReqSize]byte - var flags uint16 = unix.IFF_TUN // | unix.IFF_NO_PI - nameBytes := []byte(name) - if len(nameBytes) >= unix.IFNAMSIZ { - return nil, errors.New("Interface name 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, errno - } - - // 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), - errors: make(chan error, 5), - } - - // start event listener - - device.index, err = getIFIndex(device.name) - if err != nil { - return nil, err - } - - go device.RoutineNetlinkListener() - // go device.RoutineHackListener() // cross namespace - - // set default MTU - - return device, device.setMTU(DefaultMTU) -} |