From 0a0ed7a8714d52e8bf2478e92e424850d6a9aadf Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Mon, 23 Feb 2015 15:29:11 +0000 Subject: [PATCH 1/3] Tear down connections on prolonged loss of UDP heartbeat --- router/connection.go | 28 +++++++++++++++++++++++----- router/consts.go | 38 ++++++++++++++++++++------------------ 2 files changed, 43 insertions(+), 23 deletions(-) diff --git a/router/connection.go b/router/connection.go index f1ae3a04..961dd9cf 100644 --- a/router/connection.go +++ b/router/connection.go @@ -45,6 +45,7 @@ type LocalConnection struct { effectivePMTU int SessionKey *[32]byte establishedTimeout *time.Timer + heartbeatTimeout *time.Timer heartbeatFrame *ForwardedFrame heartbeat *time.Ticker fragTest *time.Ticker @@ -301,10 +302,12 @@ func (conn *LocalConnection) queryLoop(queryChan <-chan *ConnectionInteraction) case CSendProtocolMsg: err = conn.handleSendProtocolMsg(query.payload.(ProtocolMsg)) } - case <-conn.establishedTimeout.C: + case <-timerChan(conn.establishedTimeout): if !conn.established { err = fmt.Errorf("failed to establish UDP connectivity") } + case <-timerChan(conn.heartbeatTimeout): + err = fmt.Errorf("timed out waiting for UDP heartbeat") case <-tickerChan(conn.heartbeat): conn.Forward(true, conn.heartbeatFrame, nil) case <-tickerChan(conn.fragTest): @@ -329,6 +332,9 @@ func (conn *LocalConnection) handleReceivedHeartbeat(remoteUDPAddr *net.UDPAddr) conn.remoteUDPAddr = remoteUDPAddr conn.receivedHeartbeat = true conn.Unlock() + if conn.established { + conn.heartbeatTimeout.Reset(HeartbeatTimeout) + } if !old { if err := conn.handleSendSimpleProtocolMsg(ProtocolConnectionEstablished); err != nil { return err @@ -362,6 +368,7 @@ func (conn *LocalConnection) handleSetEstablished() error { dstPeer: conn.remote, frame: PMTUDiscovery}, nil) + conn.heartbeatTimeout = time.NewTimer(HeartbeatTimeout) conn.heartbeat = time.NewTicker(SlowHeartbeat) conn.fragTest = time.NewTicker(FragTestInterval) // avoid initial waits for timers to fire @@ -391,10 +398,8 @@ func (conn *LocalConnection) handleShutdown() { conn.Router.Ourself.DeleteConnection(conn) } - if conn.establishedTimeout != nil { - conn.establishedTimeout.Stop() - } - + stopTimer(conn.establishedTimeout) + stopTimer(conn.heartbeatTimeout) stopTicker(conn.heartbeat) stopTicker(conn.fragTest) @@ -498,3 +503,16 @@ func stopTicker(ticker *time.Ticker) { ticker.Stop() } } + +func timerChan(timer *time.Timer) <-chan time.Time { + if timer != nil { + return timer.C + } + return nil +} + +func stopTimer(timer *time.Timer) { + if timer != nil { + timer.Stop() + } +} diff --git a/router/consts.go b/router/consts.go index 11075638..da38e8ba 100644 --- a/router/consts.go +++ b/router/consts.go @@ -6,24 +6,26 @@ import ( ) const ( - EthernetOverhead = 14 - UDPOverhead = 28 // 20 bytes for IPv4, 8 bytes for UDP - Port = 6783 - HttpPort = Port + 1 - DefaultPMTU = 65535 - MaxUDPPacketSize = 65536 - ChannelSize = 16 - UDPNonceSendAt = 8192 - FragTestSize = 60001 - PMTUDiscoverySize = 60000 - FastHeartbeat = 500 * time.Millisecond - SlowHeartbeat = 10 * time.Second - FragTestInterval = 5 * time.Minute - EstablishedTimeout = 30 * time.Second - ReadTimeout = 1 * time.Minute - PMTUVerifyAttempts = 8 - PMTUVerifyTimeout = 10 * time.Millisecond // gets doubled with every attempt - MaxDuration = time.Duration(math.MaxInt64) + EthernetOverhead = 14 + UDPOverhead = 28 // 20 bytes for IPv4, 8 bytes for UDP + Port = 6783 + HttpPort = Port + 1 + DefaultPMTU = 65535 + MaxUDPPacketSize = 65536 + ChannelSize = 16 + UDPNonceSendAt = 8192 + FragTestSize = 60001 + PMTUDiscoverySize = 60000 + FastHeartbeat = 500 * time.Millisecond + SlowHeartbeat = 10 * time.Second + FragTestInterval = 5 * time.Minute + EstablishedTimeout = 30 * time.Second + ReadTimeout = 1 * time.Minute + PMTUVerifyAttempts = 8 + PMTUVerifyTimeout = 10 * time.Millisecond // gets doubled with every attempt + MaxDuration = time.Duration(math.MaxInt64) + MaxMissedHeartbeats = 3 + HeartbeatTimeout = MaxMissedHeartbeats * SlowHeartbeat ) var ( From 7618ae033cebe186d4ade40ea96b163ecea1959c Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Tue, 24 Feb 2015 18:31:18 +0000 Subject: [PATCH 2/3] Simplify to single heartbeat timer --- router/connection.go | 74 ++++++++++++++++---------------------------- 1 file changed, 27 insertions(+), 47 deletions(-) diff --git a/router/connection.go b/router/connection.go index 961dd9cf..360ec6d2 100644 --- a/router/connection.go +++ b/router/connection.go @@ -37,27 +37,26 @@ type RemoteConnection struct { type LocalConnection struct { sync.RWMutex RemoteConnection - TCPConn *net.TCPConn - tcpSender TCPSender - remoteUDPAddr *net.UDPAddr - receivedHeartbeat bool - stackFrag bool - effectivePMTU int - SessionKey *[32]byte - establishedTimeout *time.Timer - heartbeatTimeout *time.Timer - heartbeatFrame *ForwardedFrame - heartbeat *time.Ticker - fragTest *time.Ticker - forwardChan chan<- *ForwardedFrame - forwardChanDF chan<- *ForwardedFrame - stopForward chan<- interface{} - stopForwardDF chan<- interface{} - verifyPMTU chan<- int - Decryptor Decryptor - Router *Router - uid uint64 - queryChan chan<- *ConnectionInteraction + TCPConn *net.TCPConn + tcpSender TCPSender + remoteUDPAddr *net.UDPAddr + receivedHeartbeat bool + stackFrag bool + effectivePMTU int + SessionKey *[32]byte + heartbeatTimeout *time.Timer + heartbeatFrame *ForwardedFrame + heartbeat *time.Ticker + fragTest *time.Ticker + forwardChan chan<- *ForwardedFrame + forwardChanDF chan<- *ForwardedFrame + stopForward chan<- interface{} + stopForwardDF chan<- interface{} + verifyPMTU chan<- int + Decryptor Decryptor + Router *Router + uid uint64 + queryChan chan<- *ConnectionInteraction } type ConnectionInteraction struct { @@ -262,7 +261,7 @@ func (conn *LocalConnection) run(queryChan <-chan *ConnectionInteraction, accept } } - conn.establishedTimeout = time.NewTimer(EstablishedTimeout) + conn.heartbeatTimeout = time.NewTimer(HeartbeatTimeout) if err := conn.queryLoop(queryChan); err != nil { conn.log("connection shutting down due to error:", err) @@ -297,16 +296,11 @@ func (conn *LocalConnection) queryLoop(queryChan <-chan *ConnectionInteraction) case CReceivedHeartbeat: err = conn.handleReceivedHeartbeat(query.payload.(*net.UDPAddr)) case CSetEstablished: - conn.establishedTimeout.Stop() err = conn.handleSetEstablished() case CSendProtocolMsg: err = conn.handleSendProtocolMsg(query.payload.(ProtocolMsg)) } - case <-timerChan(conn.establishedTimeout): - if !conn.established { - err = fmt.Errorf("failed to establish UDP connectivity") - } - case <-timerChan(conn.heartbeatTimeout): + case <-conn.heartbeatTimeout.C: err = fmt.Errorf("timed out waiting for UDP heartbeat") case <-tickerChan(conn.heartbeat): conn.Forward(true, conn.heartbeatFrame, nil) @@ -332,9 +326,7 @@ func (conn *LocalConnection) handleReceivedHeartbeat(remoteUDPAddr *net.UDPAddr) conn.remoteUDPAddr = remoteUDPAddr conn.receivedHeartbeat = true conn.Unlock() - if conn.established { - conn.heartbeatTimeout.Reset(HeartbeatTimeout) - } + conn.heartbeatTimeout.Reset(HeartbeatTimeout) if !old { if err := conn.handleSendSimpleProtocolMsg(ProtocolConnectionEstablished); err != nil { return err @@ -368,7 +360,6 @@ func (conn *LocalConnection) handleSetEstablished() error { dstPeer: conn.remote, frame: PMTUDiscovery}, nil) - conn.heartbeatTimeout = time.NewTimer(HeartbeatTimeout) conn.heartbeat = time.NewTicker(SlowHeartbeat) conn.fragTest = time.NewTicker(FragTestInterval) // avoid initial waits for timers to fire @@ -398,8 +389,10 @@ func (conn *LocalConnection) handleShutdown() { conn.Router.Ourself.DeleteConnection(conn) } - stopTimer(conn.establishedTimeout) - stopTimer(conn.heartbeatTimeout) + if conn.heartbeatTimeout != nil { + conn.heartbeatTimeout.Stop() + } + stopTicker(conn.heartbeat) stopTicker(conn.fragTest) @@ -503,16 +496,3 @@ func stopTicker(ticker *time.Ticker) { ticker.Stop() } } - -func timerChan(timer *time.Timer) <-chan time.Time { - if timer != nil { - return timer.C - } - return nil -} - -func stopTimer(timer *time.Timer) { - if timer != nil { - timer.Stop() - } -} From cd168f54b2594d63798bc357f5d3835cfec6bf72 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Tue, 24 Feb 2015 19:42:18 +0000 Subject: [PATCH 3/3] Removed obsolete constant --- router/consts.go | 1 - 1 file changed, 1 deletion(-) diff --git a/router/consts.go b/router/consts.go index da38e8ba..b2709d22 100644 --- a/router/consts.go +++ b/router/consts.go @@ -19,7 +19,6 @@ const ( FastHeartbeat = 500 * time.Millisecond SlowHeartbeat = 10 * time.Second FragTestInterval = 5 * time.Minute - EstablishedTimeout = 30 * time.Second ReadTimeout = 1 * time.Minute PMTUVerifyAttempts = 8 PMTUVerifyTimeout = 10 * time.Millisecond // gets doubled with every attempt