Merge pull request #461 from yggdrasil-network/connreader

Try to fix TUN/TAP conn reader leakage
This commit is contained in:
Neil Alexander 2019-07-17 10:15:44 +01:00 committed by GitHub
commit 2532cd77e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -11,6 +11,8 @@ import (
"golang.org/x/net/ipv6" "golang.org/x/net/ipv6"
) )
const tunConnTimeout = 2 * time.Minute
type tunConn struct { type tunConn struct {
tun *TunAdapter tun *TunAdapter
conn *yggdrasil.Conn conn *yggdrasil.Conn
@ -49,24 +51,37 @@ func (s *tunConn) reader() error {
} }
default: default:
} }
s.tun.log.Debugln("Starting conn reader for", s)
var n int var n int
var err error var err error
read := make(chan bool) read := make(chan bool)
b := make([]byte, 65535) b := make([]byte, 65535)
for { go func() {
go func() { s.tun.log.Debugln("Starting conn reader helper for", s)
// TODO don't start a new goroutine for every packet read, this is probably a big part of the slowdowns we saw when refactoring for {
s.conn.SetReadDeadline(time.Now().Add(tunConnTimeout))
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)
if e, eok := err.(yggdrasil.ConnError); eok && !e.Temporary() { if e, eok := err.(yggdrasil.ConnError); eok {
close(s.stop) switch {
} else { case e.Temporary():
read <- false read <- false
continue
case e.Timeout():
s.tun.log.Debugln("Conn reader for helper", s, "timed out")
fallthrough
default:
s.tun.log.Debugln("Stopping conn reader helper for", s)
s.close()
return
}
} }
return read <- false
} }
read <- true read <- true
}() }
}()
for {
select { select {
case r := <-read: case r := <-read:
if r && n > 0 { if r && n > 0 {
@ -93,6 +108,7 @@ func (s *tunConn) writer() error {
} }
default: default:
} }
s.tun.log.Debugln("Starting conn writer for", s)
for { for {
select { select {
case <-s.stop: case <-s.stop:
@ -134,8 +150,7 @@ func (s *tunConn) stillAlive() {
} }
func (s *tunConn) checkForTimeouts() error { func (s *tunConn) checkForTimeouts() error {
const timeout = 2 * time.Minute timer := time.NewTimer(tunConnTimeout)
timer := time.NewTimer(timeout)
defer util.TimerStop(timer) defer util.TimerStop(timer)
defer s.close() defer s.close()
for { for {
@ -145,7 +160,7 @@ func (s *tunConn) checkForTimeouts() error {
return errors.New("connection closed") return errors.New("connection closed")
} }
util.TimerStop(timer) util.TimerStop(timer)
timer.Reset(timeout) timer.Reset(tunConnTimeout)
case <-timer.C: case <-timer.C:
return errors.New("timed out") return errors.New("timed out")
} }