have the tunConn close things after a 2 minute timeout

This commit is contained in:
Arceliar 2019-05-28 18:35:52 -05:00
parent 5ea864869a
commit b2513fce56
2 changed files with 53 additions and 13 deletions

View File

@ -2,6 +2,7 @@ package tuntap
import ( import (
"errors" "errors"
"time"
"github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/address"
"github.com/yggdrasil-network/yggdrasil-go/src/util" "github.com/yggdrasil-network/yggdrasil-go/src/util"
@ -14,19 +15,28 @@ type tunConn struct {
addr address.Address addr address.Address
snet address.Subnet snet address.Subnet
send chan []byte send chan []byte
stop chan interface{} stop chan struct{}
alive chan struct{}
} }
func (s *tunConn) close() { func (s *tunConn) close() {
s.tun.mutex.Lock() s.tun.mutex.Lock()
defer s.tun.mutex.Unlock()
s._close_nomutex() s._close_nomutex()
s.tun.mutex.Unlock()
} }
func (s *tunConn) _close_nomutex() { func (s *tunConn) _close_nomutex() {
s.conn.Close()
delete(s.tun.addrToConn, s.addr) delete(s.tun.addrToConn, s.addr)
delete(s.tun.subnetToConn, s.snet) delete(s.tun.subnetToConn, s.snet)
close(s.stop) func() {
defer func() { recover() }()
close(s.stop) // Closes reader/writer goroutines
}()
func() {
defer func() { recover() }()
close(s.alive) // Closes timeout goroutine
}()
} }
func (s *tunConn) reader() error { func (s *tunConn) reader() error {
@ -43,7 +53,7 @@ func (s *tunConn) reader() error {
b := make([]byte, 65535) b := make([]byte, 65535)
for { for {
go func() { go func() {
// TODO read timeout and close // TODO don't start a new goroutine for every packet read, this is probably a big part of the slowdowns we saw when refactoring
if n, err = s.conn.Read(b); err != nil { if n, err = s.conn.Read(b); err != nil {
s.tun.log.Errorln(s.conn.String(), "TUN/TAP conn read error:", err) s.tun.log.Errorln(s.conn.String(), "TUN/TAP conn read error:", err)
return return
@ -60,6 +70,7 @@ func (s *tunConn) reader() error {
util.PutBytes(bs) util.PutBytes(bs)
} }
} }
s.stillAlive() // TODO? Only stay alive if we read >0 bytes?
case <-s.stop: case <-s.stop:
s.tun.log.Debugln("Stopping conn reader for", s) s.tun.log.Debugln("Stopping conn reader for", s)
return nil return nil
@ -89,6 +100,33 @@ func (s *tunConn) writer() error {
s.tun.log.Errorln(s.conn.String(), "TUN/TAP conn write error:", err) s.tun.log.Errorln(s.conn.String(), "TUN/TAP conn write error:", err)
} }
util.PutBytes(b) util.PutBytes(b)
s.stillAlive()
}
}
}
func (s *tunConn) stillAlive() {
select {
case s.alive <- struct{}{}:
default:
}
}
func (s *tunConn) checkForTimeouts() error {
const timeout = 2 * time.Minute
timer := time.NewTimer(timeout)
defer util.TimerStop(timer)
defer s.close()
for {
select {
case _, ok := <-s.alive:
if !ok {
return errors.New("connection closed")
}
util.TimerStop(timer)
timer.Reset(timeout)
case <-timer.C:
return errors.New("timed out")
} }
} }
} }

View File

@ -232,7 +232,8 @@ func (tun *TunAdapter) wrap(conn *yggdrasil.Conn) (c *tunConn, err error) {
tun: tun, tun: tun,
conn: conn, conn: conn,
send: make(chan []byte, 32), // TODO: is this a sensible value? send: make(chan []byte, 32), // TODO: is this a sensible value?
stop: make(chan interface{}), stop: make(chan struct{}),
alive: make(chan struct{}, 1),
} }
// Get the remote address and subnet of the other side // Get the remote address and subnet of the other side
remoteNodeID := conn.RemoteAddr() remoteNodeID := conn.RemoteAddr()
@ -259,6 +260,7 @@ func (tun *TunAdapter) wrap(conn *yggdrasil.Conn) (c *tunConn, err error) {
// Start the connection goroutines // Start the connection goroutines
go s.reader() go s.reader()
go s.writer() go s.writer()
go s.checkForTimeouts()
// Return // Return
return c, err return c, err
} }