tun: only call .Fd() once

Doing so tends to make the tunnel blocking, so we only retrieve it once
before we call SetNonblock, and then cache the result.
This commit is contained in:
Jason A. Donenfeld 2018-10-17 21:26:53 +02:00
parent 85b2378a07
commit 2e772194cf
4 changed files with 67 additions and 61 deletions

View file

@ -37,7 +37,8 @@ type sockaddrCtl struct {
type nativeTun struct { type nativeTun struct {
name string name string
fd *os.File tunFile *os.File
fd uintptr
rwcancel *rwcancel.RWCancel rwcancel *rwcancel.RWCancel
events chan TUNEvent events chan TUNEvent
errors chan error errors chan error
@ -171,14 +172,15 @@ func CreateTUN(name string, mtu int) (TUNDevice, error) {
func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) { func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
tun := &nativeTun{ tun := &nativeTun{
fd: file, tunFile: file,
events: make(chan TUNEvent, 10), fd: file.Fd(),
errors: make(chan error, 1), events: make(chan TUNEvent, 10),
errors: make(chan error, 1),
} }
name, err := tun.Name() name, err := tun.Name()
if err != nil { if err != nil {
tun.fd.Close() tun.tunFile.Close()
return nil, err return nil, err
} }
@ -190,19 +192,19 @@ func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
return iface.Index, nil return iface.Index, nil
}() }()
if err != nil { if err != nil {
tun.fd.Close() tun.tunFile.Close()
return nil, err return nil, err
} }
tun.rwcancel, err = rwcancel.NewRWCancel(int(file.Fd())) tun.rwcancel, err = rwcancel.NewRWCancel(int(tun.fd))
if err != nil { if err != nil {
tun.fd.Close() tun.tunFile.Close()
return nil, err return nil, err
} }
tun.routeSocket, err = unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC) tun.routeSocket, err = unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC)
if err != nil { if err != nil {
tun.fd.Close() tun.tunFile.Close()
return nil, err return nil, err
} }
@ -226,7 +228,7 @@ func (tun *nativeTun) Name() (string, error) {
_, _, errno := unix.Syscall6( _, _, errno := unix.Syscall6(
unix.SYS_GETSOCKOPT, unix.SYS_GETSOCKOPT,
uintptr(tun.fd.Fd()), uintptr(tun.fd),
2, /* #define SYSPROTO_CONTROL 2 */ 2, /* #define SYSPROTO_CONTROL 2 */
2, /* #define UTUN_OPT_IFNAME 2 */ 2, /* #define UTUN_OPT_IFNAME 2 */
uintptr(unsafe.Pointer(&ifName)), uintptr(unsafe.Pointer(&ifName)),
@ -241,7 +243,7 @@ func (tun *nativeTun) Name() (string, error) {
} }
func (tun *nativeTun) File() *os.File { func (tun *nativeTun) File() *os.File {
return tun.fd return tun.tunFile
} }
func (tun *nativeTun) Events() chan TUNEvent { func (tun *nativeTun) Events() chan TUNEvent {
@ -254,7 +256,7 @@ func (tun *nativeTun) doRead(buff []byte, offset int) (int, error) {
return 0, err return 0, err
default: default:
buff := buff[offset-4:] buff := buff[offset-4:]
n, err := tun.fd.Read(buff[:]) n, err := tun.tunFile.Read(buff[:])
if n < 4 { if n < 4 {
return 0, err return 0, err
} }
@ -294,13 +296,13 @@ func (tun *nativeTun) Write(buff []byte, offset int) (int, error) {
// write // write
return tun.fd.Write(buff) return tun.tunFile.Write(buff)
} }
func (tun *nativeTun) Close() error { func (tun *nativeTun) Close() error {
var err3 error var err3 error
err1 := tun.rwcancel.Cancel() err1 := tun.rwcancel.Cancel()
err2 := tun.fd.Close() err2 := tun.tunFile.Close()
if tun.routeSocket != -1 { if tun.routeSocket != -1 {
unix.Shutdown(tun.routeSocket, unix.SHUT_RDWR) unix.Shutdown(tun.routeSocket, unix.SHUT_RDWR)
err3 = unix.Close(tun.routeSocket) err3 = unix.Close(tun.routeSocket)

View file

@ -51,7 +51,8 @@ type ifstat struct {
type nativeTun struct { type nativeTun struct {
name string name string
fd *os.File tunFile *os.File
fd uintptr
rwcancel *rwcancel.RWCancel rwcancel *rwcancel.RWCancel
events chan TUNEvent events chan TUNEvent
errors chan error errors chan error
@ -237,15 +238,15 @@ func CreateTUN(name string, mtu int) (TUNDevice, error) {
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) tunFile, err := os.OpenFile("/dev/tun", unix.O_RDWR, 0)
if err != nil { if err != nil {
return nil, err return nil, err
} }
tunfd := tunfile.Fd() tunfd := tunFile.Fd()
assignedName, err := tunName(tunfd) assignedName, err := tunName(tunfd)
if err != nil { if err != nil {
tunfile.Close() tunFile.Close()
return nil, err return nil, err
} }
@ -293,25 +294,26 @@ func CreateTUN(name string, mtu int) (TUNDevice, error) {
uintptr(unsafe.Pointer(&ifr)), uintptr(unsafe.Pointer(&ifr)),
) )
if errno != 0 { if errno != 0 {
tunfile.Close() tunFile.Close()
tunDestroy(name) tunDestroy(name)
return nil, fmt.Errorf("failed to rename %s to %s: %s", assignedName, name, errno.Error()) return nil, fmt.Errorf("failed to rename %s to %s: %s", assignedName, name, errno.Error())
} }
return CreateTUNFromFile(tunfile, mtu) return CreateTUNFromFile(tunFile, mtu)
} }
func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) { func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
tun := &nativeTun{ tun := &nativeTun{
fd: file, tunFile: file,
events: make(chan TUNEvent, 10), fd: file.Fd(),
errors: make(chan error, 1), events: make(chan TUNEvent, 10),
errors: make(chan error, 1),
} }
name, err := tun.Name() name, err := tun.Name()
if err != nil { if err != nil {
tun.fd.Close() tun.tunFile.Close()
return nil, err return nil, err
} }
@ -323,19 +325,19 @@ func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
return iface.Index, nil return iface.Index, nil
}() }()
if err != nil { if err != nil {
tun.fd.Close() tun.tunFile.Close()
return nil, err return nil, err
} }
tun.rwcancel, err = rwcancel.NewRWCancel(int(file.Fd())) tun.rwcancel, err = rwcancel.NewRWCancel(int(tun.fd))
if err != nil { if err != nil {
tun.fd.Close() tun.tunFile.Close()
return nil, err return nil, err
} }
tun.routeSocket, err = unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC) tun.routeSocket, err = unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC)
if err != nil { if err != nil {
tun.fd.Close() tun.tunFile.Close()
return nil, err return nil, err
} }
@ -351,7 +353,7 @@ func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
} }
func (tun *nativeTun) Name() (string, error) { func (tun *nativeTun) Name() (string, error) {
name, err := tunName(tun.fd.Fd()) name, err := tunName(tun.fd)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -360,7 +362,7 @@ func (tun *nativeTun) Name() (string, error) {
} }
func (tun *nativeTun) File() *os.File { func (tun *nativeTun) File() *os.File {
return tun.fd return tun.tunFile
} }
func (tun *nativeTun) Events() chan TUNEvent { func (tun *nativeTun) Events() chan TUNEvent {
@ -373,7 +375,7 @@ func (tun *nativeTun) doRead(buff []byte, offset int) (int, error) {
return 0, err return 0, err
default: default:
buff := buff[offset-4:] buff := buff[offset-4:]
n, err := tun.fd.Read(buff[:]) n, err := tun.tunFile.Read(buff[:])
if n < 4 { if n < 4 {
return 0, err return 0, err
} }
@ -413,13 +415,13 @@ func (tun *nativeTun) Write(buff []byte, offset int) (int, error) {
// write // write
return tun.fd.Write(buff) return tun.tunFile.Write(buff)
} }
func (tun *nativeTun) Close() error { func (tun *nativeTun) Close() error {
var err4 error var err4 error
err1 := tun.rwcancel.Cancel() err1 := tun.rwcancel.Cancel()
err2 := tun.fd.Close() err2 := tun.tunFile.Close()
err3 := tunDestroy(tun.name) err3 := tunDestroy(tun.name)
if tun.routeSocket != -1 { if tun.routeSocket != -1 {
unix.Shutdown(tun.routeSocket, unix.SHUT_RDWR) unix.Shutdown(tun.routeSocket, unix.SHUT_RDWR)

View file

@ -29,7 +29,8 @@ const (
) )
type nativeTun struct { type nativeTun struct {
fd *os.File tunFile *os.File
fd uintptr
fdCancel *rwcancel.RWCancel fdCancel *rwcancel.RWCancel
index int32 // if index index int32 // if index
name string // name of interface name string // name of interface
@ -43,7 +44,7 @@ type nativeTun struct {
} }
func (tun *nativeTun) File() *os.File { func (tun *nativeTun) File() *os.File {
return tun.fd return tun.tunFile
} }
func (tun *nativeTun) routineHackListener() { func (tun *nativeTun) routineHackListener() {
@ -51,7 +52,7 @@ func (tun *nativeTun) routineHackListener() {
/* This is needed for the detection to work across network namespaces /* 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. * If you are reading this and know a better method, please get in touch.
*/ */
fd := int(tun.fd.Fd()) fd := int(tun.fd)
for { for {
_, err := unix.Write(fd, nil) _, err := unix.Write(fd, nil)
switch err { switch err {
@ -266,7 +267,7 @@ func (tun *nativeTun) Name() (string, error) {
var ifr [ifReqSize]byte var ifr [ifReqSize]byte
_, _, errno := unix.Syscall( _, _, errno := unix.Syscall(
unix.SYS_IOCTL, unix.SYS_IOCTL,
tun.fd.Fd(), tun.fd,
uintptr(unix.TUNGETIFF), uintptr(unix.TUNGETIFF),
uintptr(unsafe.Pointer(&ifr[0])), uintptr(unsafe.Pointer(&ifr[0])),
) )
@ -307,7 +308,7 @@ func (tun *nativeTun) Write(buff []byte, offset int) (int, error) {
// write // write
return tun.fd.Write(buff) return tun.tunFile.Write(buff)
} }
func (tun *nativeTun) doRead(buff []byte, offset int) (int, error) { func (tun *nativeTun) doRead(buff []byte, offset int) (int, error) {
@ -316,10 +317,10 @@ func (tun *nativeTun) doRead(buff []byte, offset int) (int, error) {
return 0, err return 0, err
default: default:
if tun.nopi { if tun.nopi {
return tun.fd.Read(buff[offset:]) return tun.tunFile.Read(buff[offset:])
} else { } else {
buff := buff[offset-4:] buff := buff[offset-4:]
n, err := tun.fd.Read(buff[:]) n, err := tun.tunFile.Read(buff[:])
if n < 4 { if n < 4 {
return 0, err return 0, err
} }
@ -354,7 +355,7 @@ func (tun *nativeTun) Close() error {
} else if tun.events != nil { } else if tun.events != nil {
close(tun.events) close(tun.events)
} }
err2 := tun.fd.Close() err2 := tun.tunFile.Close()
err3 := tun.fdCancel.Cancel() err3 := tun.fdCancel.Cancel()
if err1 != nil { if err1 != nil {
@ -413,23 +414,24 @@ func CreateTUN(name string, mtu int) (TUNDevice, error) {
func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) { func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
tun := &nativeTun{ tun := &nativeTun{
fd: file, tunFile: file,
fd: file.Fd(),
events: make(chan TUNEvent, 5), events: make(chan TUNEvent, 5),
errors: make(chan error, 5), errors: make(chan error, 5),
statusListenersShutdown: make(chan struct{}), statusListenersShutdown: make(chan struct{}),
nopi: false, nopi: false,
} }
var err error var err error
tun.fdCancel, err = rwcancel.NewRWCancel(int(file.Fd())) tun.fdCancel, err = rwcancel.NewRWCancel(int(tun.fd))
if err != nil { if err != nil {
tun.fd.Close() tun.tunFile.Close()
return nil, err return nil, err
} }
_, err = tun.Name() _, err = tun.Name()
if err != nil { if err != nil {
tun.fd.Close() tun.tunFile.Close()
return nil, err return nil, err
} }
@ -442,12 +444,12 @@ func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
tun.netlinkSock, err = createNetlinkSocket() tun.netlinkSock, err = createNetlinkSocket()
if err != nil { if err != nil {
tun.fd.Close() tun.tunFile.Close()
return nil, err return nil, err
} }
tun.netlinkCancel, err = rwcancel.NewRWCancel(tun.netlinkSock) tun.netlinkCancel, err = rwcancel.NewRWCancel(tun.netlinkSock)
if err != nil { if err != nil {
tun.fd.Close() tun.tunFile.Close()
return nil, err return nil, err
} }

View file

@ -29,7 +29,7 @@ const _TUNSIFMODE = 0x8004745d
type nativeTun struct { type nativeTun struct {
name string name string
fd *os.File tunFile *os.File
rwcancel *rwcancel.RWCancel rwcancel *rwcancel.RWCancel
events chan TUNEvent events chan TUNEvent
errors chan error errors chan error
@ -144,14 +144,14 @@ func CreateTUN(name string, mtu int) (TUNDevice, error) {
func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) { func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
tun := &nativeTun{ tun := &nativeTun{
fd: file, tunFile: file,
events: make(chan TUNEvent, 10), events: make(chan TUNEvent, 10),
errors: make(chan error, 1), errors: make(chan error, 1),
} }
name, err := tun.Name() name, err := tun.Name()
if err != nil { if err != nil {
tun.fd.Close() tun.tunFile.Close()
return nil, err return nil, err
} }
@ -163,19 +163,19 @@ func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
return iface.Index, nil return iface.Index, nil
}() }()
if err != nil { if err != nil {
tun.fd.Close() tun.tunFile.Close()
return nil, err return nil, err
} }
tun.rwcancel, err = rwcancel.NewRWCancel(int(file.Fd())) tun.rwcancel, err = rwcancel.NewRWCancel(int(file.Fd()))
if err != nil { if err != nil {
tun.fd.Close() tun.tunFile.Close()
return nil, err return nil, err
} }
tun.routeSocket, err = unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC) tun.routeSocket, err = unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC)
if err != nil { if err != nil {
tun.fd.Close() tun.tunFile.Close()
return nil, err return nil, err
} }
@ -191,7 +191,7 @@ func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
} }
func (tun *nativeTun) Name() (string, error) { func (tun *nativeTun) Name() (string, error) {
gostat, err := tun.fd.Stat() gostat, err := tun.tunFile.Stat()
if err != nil { if err != nil {
tun.name = "" tun.name = ""
return "", err return "", err
@ -202,7 +202,7 @@ func (tun *nativeTun) Name() (string, error) {
} }
func (tun *nativeTun) File() *os.File { func (tun *nativeTun) File() *os.File {
return tun.fd return tun.tunFile
} }
func (tun *nativeTun) Events() chan TUNEvent { func (tun *nativeTun) Events() chan TUNEvent {
@ -215,7 +215,7 @@ func (tun *nativeTun) doRead(buff []byte, offset int) (int, error) {
return 0, err return 0, err
default: default:
buff := buff[offset-4:] buff := buff[offset-4:]
n, err := tun.fd.Read(buff[:]) n, err := tun.tunFile.Read(buff[:])
if n < 4 { if n < 4 {
return 0, err return 0, err
} }
@ -255,13 +255,13 @@ func (tun *nativeTun) Write(buff []byte, offset int) (int, error) {
// write // write
return tun.fd.Write(buff) return tun.tunFile.Write(buff)
} }
func (tun *nativeTun) Close() error { func (tun *nativeTun) Close() error {
var err3 error var err3 error
err1 := tun.rwcancel.Cancel() err1 := tun.rwcancel.Cancel()
err2 := tun.fd.Close() err2 := tun.tunFile.Close()
if tun.routeSocket != -1 { if tun.routeSocket != -1 {
unix.Shutdown(tun.routeSocket, unix.SHUT_RDWR) unix.Shutdown(tun.routeSocket, unix.SHUT_RDWR)
err3 = unix.Close(tun.routeSocket) err3 = unix.Close(tun.routeSocket)