nextKeepalive time.Time
}
signal struct {
- newKeyPair chan struct{} // (size 1) : a new key pair was generated
- handshakeBegin chan struct{} // (size 1) : request that a new handshake be started ("queue handshake")
- handshakeCompleted chan struct{} // (size 1) : handshake completed
- handshakeReset chan struct{} // (size 1) : reset handshake negotiation state
- flushNonceQueue chan struct{} // (size 1) : empty queued packets
- messageSend chan struct{} // (size 1) : a message was send to the peer
- messageReceived chan struct{} // (size 1) : an authenticated message was received
- stop chan struct{} // (size 0) : close to stop all goroutines for peer
+ newKeyPair Signal // size 1, new key pair was generated
+ handshakeCompleted Signal // size 1, handshake completed
+ handshakeBegin Signal // size 1, begin new handshake begin
+ flushNonceQueue Signal // size 1, empty queued packets
+ messageSend Signal // size 1, message was send to peer
+ messageReceived Signal // size 1, authenticated message recv
+ stop Signal // size 0, stop all goroutines
}
timer struct {
// state related to WireGuard timers
- keepalivePersistent *time.Timer // set for persistent keepalives
- keepalivePassive *time.Timer // set upon recieving messages
- newHandshake *time.Timer // begin a new handshake (after Keepalive + RekeyTimeout)
- zeroAllKeys *time.Timer // zero all key material (after RejectAfterTime*3)
- handshakeDeadline *time.Timer // Current handshake must be completed
+ keepalivePersistent Timer // set for persistent keepalives
+ keepalivePassive Timer // set upon recieving messages
+ newHandshake Timer // begin a new handshake (stale)
+ zeroAllKeys Timer // zero all key material
+ handshakeDeadline Timer // complete handshake timeout
+ handshakeTimeout Timer // current handshake message timeout
- pendingKeepalivePassive bool
- pendingNewHandshake bool
- pendingZeroAllKeys bool
-
- needAnotherKeepalive bool
sendLastMinuteHandshake bool
+ needAnotherKeepalive bool
}
queue struct {
nonce chan *QueueOutboundElement // nonce / pre-handshake queue
peer.mac.Init(pk)
peer.device = device
- peer.timer.keepalivePersistent = NewStoppedTimer()
- peer.timer.keepalivePassive = NewStoppedTimer()
- peer.timer.newHandshake = NewStoppedTimer()
- peer.timer.zeroAllKeys = NewStoppedTimer()
+ peer.timer.keepalivePersistent = NewTimer()
+ peer.timer.keepalivePassive = NewTimer()
+ peer.timer.newHandshake = NewTimer()
+ peer.timer.zeroAllKeys = NewTimer()
+ peer.timer.handshakeDeadline = NewTimer()
+ peer.timer.handshakeTimeout = NewTimer()
// assign id for debugging
handshake := &peer.handshake
handshake.mutex.Lock()
handshake.remoteStatic = pk
- handshake.precomputedStaticStatic = device.privateKey.sharedSecret(handshake.remoteStatic)
+ handshake.precomputedStaticStatic =
+ device.privateKey.sharedSecret(handshake.remoteStatic)
handshake.mutex.Unlock()
// reset endpoint
// prepare signaling & routines
- peer.signal.stop = make(chan struct{})
- peer.signal.newKeyPair = make(chan struct{}, 1)
- peer.signal.handshakeBegin = make(chan struct{}, 1)
- peer.signal.handshakeReset = make(chan struct{}, 1)
- peer.signal.handshakeCompleted = make(chan struct{}, 1)
- peer.signal.flushNonceQueue = make(chan struct{}, 1)
+ peer.signal.stop = NewSignal()
+ peer.signal.newKeyPair = NewSignal()
+ peer.signal.handshakeBegin = NewSignal()
+ peer.signal.handshakeCompleted = NewSignal()
+ peer.signal.flushNonceQueue = NewSignal()
go peer.RoutineNonce()
go peer.RoutineTimerHandler()
- go peer.RoutineHandshakeInitiator()
go peer.RoutineSequentialSender()
go peer.RoutineSequentialReceiver()
}
func (peer *Peer) Close() {
- close(peer.signal.stop)
+ peer.signal.stop.Broadcast()
}
}\r
nonce := atomic.LoadUint64(&kp.sendNonce)\r
if nonce > RekeyAfterMessages {\r
- signalSend(peer.signal.handshakeBegin)\r
+ peer.signal.handshakeBegin.Send()\r
}\r
if kp.isInitiator && time.Now().Sub(kp.created) > RekeyAfterTime {\r
- signalSend(peer.signal.handshakeBegin)\r
+ peer.signal.handshakeBegin.Send()\r
}\r
}\r
\r
send := nonce > RekeyAfterMessages || time.Now().Sub(kp.created) > RekeyAfterTimeReceiving\r
if send {\r
// do a last minute attempt at initiating a new handshake\r
- signalSend(peer.signal.handshakeBegin)\r
+ peer.signal.handshakeBegin.Send()\r
peer.timer.sendLastMinuteHandshake = true\r
}\r
}\r
* Sent non-empty (authenticated) transport message\r
*/\r
func (peer *Peer) TimerDataSent() {\r
- timerStop(peer.timer.keepalivePassive)\r
- if !peer.timer.pendingNewHandshake {\r
- peer.timer.pendingNewHandshake = true\r
+ peer.timer.keepalivePassive.Stop()\r
+ if peer.timer.newHandshake.Pending() {\r
peer.timer.newHandshake.Reset(NewHandshakeTime)\r
}\r
}\r
\r
/* Event:\r
* Received non-empty (authenticated) transport message\r
+ *\r
+ * Action:\r
+ * Set a timer to confirm the message using a keep-alive (if not already set)\r
*/\r
func (peer *Peer) TimerDataReceived() {\r
- if peer.timer.pendingKeepalivePassive {\r
+ if !peer.timer.keepalivePassive.Start(KeepaliveTimeout) {\r
peer.timer.needAnotherKeepalive = true\r
- return\r
}\r
- peer.timer.pendingKeepalivePassive = false\r
- peer.timer.keepalivePassive.Reset(KeepaliveTimeout)\r
}\r
\r
/* Event:\r
* Any (authenticated) packet received\r
*/\r
func (peer *Peer) TimerAnyAuthenticatedPacketReceived() {\r
- timerStop(peer.timer.newHandshake)\r
+ peer.timer.newHandshake.Stop()\r
}\r
\r
/* Event:\r
* Any authenticated packet send / received.\r
+ *\r
+ * Action:\r
+ * Push persistent keep-alive into the future\r
*/\r
func (peer *Peer) TimerAnyAuthenticatedPacketTraversal() {\r
interval := atomic.LoadUint64(&peer.persistentKeepaliveInterval)\r
&peer.stats.lastHandshakeNano,\r
time.Now().UnixNano(),\r
)\r
- signalSend(peer.signal.handshakeCompleted)\r
+ peer.signal.handshakeCompleted.Send()\r
peer.device.log.Info.Println("Negotiated new handshake for", peer.String())\r
}\r
\r
* CreateMessageInitiation\r
* CreateMessageResponse\r
*\r
- * Schedules the deletion of all key material\r
+ * Action:\r
+ * Schedule the deletion of all key material\r
* upon failure to complete a handshake\r
*/\r
func (peer *Peer) TimerEphemeralKeyCreated() {\r
func (peer *Peer) RoutineTimerHandler() {\r
device := peer.device\r
\r
+ logInfo := device.log.Info\r
logDebug := device.log.Debug\r
logDebug.Println("Routine, timer handler, started for peer", peer.String())\r
\r
for {\r
select {\r
\r
- case <-peer.signal.stop:\r
- return\r
+ /* timers */\r
\r
- // keep-alives\r
+ // keep-alive\r
\r
- case <-peer.timer.keepalivePersistent.C:\r
+ case <-peer.timer.keepalivePersistent.Wait():\r
\r
interval := atomic.LoadUint64(&peer.persistentKeepaliveInterval)\r
if interval > 0 {\r
peer.SendKeepAlive()\r
}\r
\r
- case <-peer.timer.keepalivePassive.C:\r
+ case <-peer.timer.keepalivePassive.Wait():\r
\r
logDebug.Println("Sending keep-alive to", peer.String())\r
\r
peer.timer.needAnotherKeepalive = false\r
}\r
\r
- // unresponsive session\r
+ // clear key material timer\r
\r
- case <-peer.timer.newHandshake.C:\r
-\r
- logDebug.Println("Retrying handshake with", peer.String(), "due to lack of reply")\r
-\r
- signalSend(peer.signal.handshakeBegin)\r
-\r
- // clear key material\r
-\r
- case <-peer.timer.zeroAllKeys.C:\r
+ case <-peer.timer.zeroAllKeys.Wait():\r
\r
logDebug.Println("Clearing all key material for", peer.String())\r
\r
setZero(hs.chainKey[:])\r
setZero(hs.hash[:])\r
hs.mutex.Unlock()\r
- }\r
- }\r
-}\r
\r
-/* This is the state machine for handshake initiation\r
- *\r
- * Associated with this routine is the signal "handshakeBegin"\r
- * The routine will read from the "handshakeBegin" channel\r
- * at most every RekeyTimeout seconds\r
- */\r
-func (peer *Peer) RoutineHandshakeInitiator() {\r
- device := peer.device\r
+ // handshake timers\r
\r
- logInfo := device.log.Info\r
- logError := device.log.Error\r
- logDebug := device.log.Debug\r
- logDebug.Println("Routine, handshake initiator, started for", peer.String())\r
+ case <-peer.timer.newHandshake.Wait():\r
+ logInfo.Println("Retrying handshake with", peer.String())\r
+ peer.signal.handshakeBegin.Send()\r
\r
- var temp [256]byte\r
+ case <-peer.timer.handshakeTimeout.Wait():\r
\r
- for {\r
+ // clear source (in case this is causing problems)\r
\r
- // wait for signal\r
+ peer.mutex.Lock()\r
+ if peer.endpoint != nil {\r
+ peer.endpoint.ClearSrc()\r
+ }\r
+ peer.mutex.Unlock()\r
\r
- select {\r
- case <-peer.signal.handshakeBegin:\r
- case <-peer.signal.stop:\r
- return\r
- }\r
+ // send new handshake\r
\r
- // set deadline\r
+ err := peer.sendNewHandshake()\r
+ if err != nil {\r
+ logInfo.Println(\r
+ "Failed to send handshake to peer:", peer.String())\r
+ }\r
\r
- BeginHandshakes:\r
+ case <-peer.timer.handshakeDeadline.Wait():\r
\r
- signalClear(peer.signal.handshakeReset)\r
- deadline := time.NewTimer(RekeyAttemptTime)\r
+ // clear all queued packets and stop keep-alive\r
\r
- AttemptHandshakes:\r
+ logInfo.Println(\r
+ "Handshake negotiation timed out for:", peer.String())\r
\r
- for attempts := uint(1); ; attempts++ {\r
+ peer.signal.flushNonceQueue.Send()\r
+ peer.timer.keepalivePersistent.Stop()\r
+ peer.signal.handshakeBegin.Enable()\r
\r
- // check if deadline reached\r
+ /* signals */\r
\r
- select {\r
- case <-deadline.C:\r
- logInfo.Println("Handshake negotiation timed out for:", peer.String())\r
- signalSend(peer.signal.flushNonceQueue)\r
- timerStop(peer.timer.keepalivePersistent)\r
- break\r
- case <-peer.signal.stop:\r
- return\r
- default:\r
- }\r
+ case <-peer.signal.stop.Wait():\r
+ return\r
\r
- signalClear(peer.signal.handshakeCompleted)\r
+ case <-peer.signal.handshakeBegin.Wait():\r
\r
- // create initiation message\r
+ peer.signal.handshakeBegin.Disable()\r
\r
- msg, err := peer.device.CreateMessageInitiation(peer)\r
+ err := peer.sendNewHandshake()\r
if err != nil {\r
- logError.Println("Failed to create handshake initiation message:", err)\r
- break AttemptHandshakes\r
+ logInfo.Println(\r
+ "Failed to send handshake to peer:", peer.String())\r
}\r
\r
- // marshal handshake message\r
-\r
- writer := bytes.NewBuffer(temp[:0])\r
- binary.Write(writer, binary.LittleEndian, msg)\r
- packet := writer.Bytes()\r
- peer.mac.AddMacs(packet)\r
-\r
- // send to endpoint\r
-\r
- err = peer.SendBuffer(packet)\r
- jitter := time.Millisecond * time.Duration(rand.Uint32()%334)\r
- timeout := time.NewTimer(RekeyTimeout + jitter)\r
- if err == nil {\r
- peer.TimerAnyAuthenticatedPacketTraversal()\r
- logDebug.Println(\r
- "Handshake initiation attempt",\r
- attempts, "sent to", peer.String(),\r
- )\r
- } else {\r
- logError.Println(\r
- "Failed to send handshake initiation message to",\r
- peer.String(), ":", err,\r
- )\r
- }\r
+ peer.timer.handshakeDeadline.Reset(RekeyAttemptTime)\r
\r
- // wait for handshake or timeout\r
+ case <-peer.signal.handshakeCompleted.Wait():\r
\r
- select {\r
+ logInfo.Println(\r
+ "Handshake completed for:", peer.String())\r
\r
- case <-peer.signal.stop:\r
- return\r
+ peer.timer.handshakeTimeout.Stop()\r
+ peer.timer.handshakeDeadline.Stop()\r
+ peer.signal.handshakeBegin.Enable()\r
+ }\r
+ }\r
+}\r
\r
- case <-peer.signal.handshakeCompleted:\r
- <-timeout.C\r
- peer.timer.sendLastMinuteHandshake = false\r
- break AttemptHandshakes\r
+/* Sends a new handshake initiation message to the peer (endpoint)\r
+ */\r
+func (peer *Peer) sendNewHandshake() error {\r
\r
- case <-peer.signal.handshakeReset:\r
- <-timeout.C\r
- goto BeginHandshakes\r
+ // temporarily disable the handshake complete signal\r
\r
- case <-timeout.C:\r
+ peer.signal.handshakeCompleted.Disable()\r
\r
- // clear source address of peer\r
+ // create initiation message\r
\r
- peer.mutex.Lock()\r
- if peer.endpoint != nil {\r
- peer.endpoint.ClearSrc()\r
- }\r
- peer.mutex.Unlock()\r
- }\r
- }\r
+ msg, err := peer.device.CreateMessageInitiation(peer)\r
+ if err != nil {\r
+ return err\r
+ }\r
+\r
+ // marshal handshake message\r
\r
- // clear signal set in the meantime\r
+ var buff [MessageInitiationSize]byte\r
+ writer := bytes.NewBuffer(buff[:0])\r
+ binary.Write(writer, binary.LittleEndian, msg)\r
+ packet := writer.Bytes()\r
+ peer.mac.AddMacs(packet)\r
\r
- signalClear(peer.signal.handshakeBegin)\r
+ // send to endpoint\r
+\r
+ err = peer.SendBuffer(packet)\r
+ if err == nil {\r
+ peer.TimerAnyAuthenticatedPacketTraversal()\r
+ peer.signal.handshakeCompleted.Enable()\r
}\r
+\r
+ // set timeout\r
+\r
+ jitter := time.Millisecond * time.Duration(rand.Uint32()%334)\r
+ peer.timer.handshakeTimeout.Reset(RekeyTimeout + jitter)\r
+\r
+ return err\r
}\r