Signal using select and a pipe for bringing down TUN reader
Waiting on resolution of these to fix in better way: - https://github.com/golang/go/issues/22939 - https://github.com/golang/go/issues/24331
This commit is contained in:
parent
676bb91434
commit
4973ea0c9e
113
tun_linux.go
113
tun_linux.go
|
@ -1,3 +1,5 @@
|
||||||
|
/* Copyright 2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
/* Implementation of the TUN device interface for linux
|
/* Implementation of the TUN device interface for linux
|
||||||
|
@ -13,6 +15,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
@ -23,12 +26,14 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type NativeTun struct {
|
type NativeTun struct {
|
||||||
fd *os.File
|
fd *os.File
|
||||||
index int32 // if index
|
index int32 // if index
|
||||||
name string // name of interface
|
name string // name of interface
|
||||||
errors chan error // async error handling
|
errors chan error // async error handling
|
||||||
events chan TUNEvent // device related events
|
events chan TUNEvent // device related events
|
||||||
nopi bool // the device was pased IFF_NO_PI
|
nopi bool // the device was pased IFF_NO_PI
|
||||||
|
closingReader *os.File
|
||||||
|
closingWriter *os.File
|
||||||
}
|
}
|
||||||
|
|
||||||
func toRTMGRP(sc uint) uint {
|
func toRTMGRP(sc uint) uint {
|
||||||
|
@ -282,7 +287,44 @@ func (tun *NativeTun) Write(buff []byte, offset int) (int, error) {
|
||||||
return tun.fd.Write(buff)
|
return tun.fd.Write(buff)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tun *NativeTun) Read(buff []byte, offset int) (int, error) {
|
type FdSet struct {
|
||||||
|
fdset unix.FdSet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fdset *FdSet) set(i int) {
|
||||||
|
bits := 32 << (^uint(0) >> 63)
|
||||||
|
fdset.fdset.Bits[i/bits] |= 1 << uint(i%bits)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fdset *FdSet) check(i int) bool {
|
||||||
|
bits := 32 << (^uint(0) >> 63)
|
||||||
|
return (fdset.fdset.Bits[i/bits] & (1 << uint(i%bits))) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func max(a, b int) int {
|
||||||
|
if a > b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tun *NativeTun) readyRead() bool {
|
||||||
|
readFd := int(tun.fd.Fd())
|
||||||
|
closeFd := int(tun.closingReader.Fd())
|
||||||
|
fdset := FdSet{}
|
||||||
|
fdset.set(readFd)
|
||||||
|
fdset.set(closeFd)
|
||||||
|
_, err := unix.Select(max(readFd, closeFd)+1, &fdset.fdset, nil, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if fdset.check(closeFd) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return fdset.check(readFd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tun *NativeTun) doRead(buff []byte, offset int) (int, error) {
|
||||||
select {
|
select {
|
||||||
case err := <-tun.errors:
|
case err := <-tun.errors:
|
||||||
return 0, err
|
return 0, err
|
||||||
|
@ -300,12 +342,38 @@ func (tun *NativeTun) Read(buff []byte, offset int) (int, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func unixIsEAGAIN(err error) bool {
|
||||||
|
if pe, ok := err.(*os.PathError); ok {
|
||||||
|
if errno, ok := pe.Err.(syscall.Errno); ok && errno == syscall.EAGAIN {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tun *NativeTun) Read(buff []byte, offset int) (int, error) {
|
||||||
|
for {
|
||||||
|
n, err := tun.doRead(buff, offset)
|
||||||
|
if err == nil || !unixIsEAGAIN(err) {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
if !tun.readyRead() {
|
||||||
|
return 0, errors.New("Tun device closed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (tun *NativeTun) Events() chan TUNEvent {
|
func (tun *NativeTun) Events() chan TUNEvent {
|
||||||
return tun.events
|
return tun.events
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tun *NativeTun) Close() error {
|
func (tun *NativeTun) Close() error {
|
||||||
return tun.fd.Close()
|
err := tun.fd.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tun.closingWriter.Write([]byte{0})
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateTUNFromFile(fd *os.File) (TUNDevice, error) {
|
func CreateTUNFromFile(fd *os.File) (TUNDevice, error) {
|
||||||
|
@ -317,11 +385,21 @@ func CreateTUNFromFile(fd *os.File) (TUNDevice, error) {
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
err = syscall.SetNonblock(int(fd.Fd()), true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
_, err = device.Name()
|
_, err = device.Name()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
device.closingReader, device.closingWriter, err = os.Pipe()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// start event listener
|
// start event listener
|
||||||
|
|
||||||
device.index, err = getIFIndex(device.name)
|
device.index, err = getIFIndex(device.name)
|
||||||
|
@ -341,7 +419,19 @@ func CreateTUN(name string) (TUNDevice, error) {
|
||||||
|
|
||||||
// open clone device
|
// open clone device
|
||||||
|
|
||||||
fd, err := os.OpenFile(cloneDevicePath, os.O_RDWR, 0)
|
// HACK: we open it as a raw Fd first, so that f.nonblock=false
|
||||||
|
// when we make it into a file object.
|
||||||
|
nfd, err := syscall.Open(cloneDevicePath, os.O_RDWR, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = syscall.SetNonblock(nfd, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fd := os.NewFile(uintptr(nfd), cloneDevicePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -379,6 +469,11 @@ func CreateTUN(name string) (TUNDevice, error) {
|
||||||
nopi: false,
|
nopi: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
device.closingReader, device.closingWriter, err = os.Pipe()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// start event listener
|
// start event listener
|
||||||
|
|
||||||
device.index, err = getIFIndex(device.name)
|
device.index, err = getIFIndex(device.name)
|
||||||
|
|
Loading…
Reference in a new issue