aboutsummaryrefslogtreecommitdiffstats
path: root/src/handshake.go
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/handshake.go241
1 files changed, 146 insertions, 95 deletions
diff --git a/src/handshake.go b/src/handshake.go
index 238c339..8f8e2f9 100644
--- a/src/handshake.go
+++ b/src/handshake.go
@@ -24,91 +24,163 @@ func (peer *Peer) SendKeepAlive() bool {
return true
}
-func (peer *Peer) RoutineHandshakeInitiator() {
- var ongoing bool
- var begun time.Time
- var attempts uint
- var timeout time.Timer
-
- device := peer.device
- work := new(QueueOutboundElement)
- buffer := make([]byte, 0, 1024)
-
- queueHandshakeInitiation := func() error {
- work.mutex.Lock()
- defer work.mutex.Unlock()
+func StoppedTimer() *time.Timer {
+ timer := time.NewTimer(time.Hour)
+ if !timer.Stop() {
+ <-timer.C
+ }
+ return timer
+}
- // create initiation
+/* Called when a new authenticated message has been send
+ *
+ * TODO: This might be done in a faster way
+ */
+func (peer *Peer) KeepKeyFreshSending() {
+ send := func() bool {
+ peer.keyPairs.mutex.RLock()
+ defer peer.keyPairs.mutex.RUnlock()
- msg, err := device.CreateMessageInitiation(peer)
- if err != nil {
- return err
+ kp := peer.keyPairs.current
+ if kp == nil {
+ return false
}
- // create "work" element
+ if !kp.isInitiator {
+ return false
+ }
- writer := bytes.NewBuffer(buffer[:0])
- binary.Write(writer, binary.LittleEndian, &msg)
- work.packet = writer.Bytes()
- peer.mac.AddMacs(work.packet)
- peer.InsertOutbound(work)
- return nil
+ nonce := atomic.LoadUint64(&kp.sendNonce)
+ if nonce > RekeyAfterMessages {
+ return true
+ }
+ return time.Now().Sub(kp.created) > RekeyAfterTime
+ }()
+ if send {
+ sendSignal(peer.signal.handshakeBegin)
}
+}
- for {
- select {
- case <-peer.signal.stopInitiator:
- return
-
- case <-peer.signal.newHandshake:
- if ongoing {
- continue
- }
-
- // create handshake
-
- err := queueHandshakeInitiation()
- if err != nil {
- device.log.Error.Println("Failed to create initiation message:", err)
- }
-
- // log when we began
-
- begun = time.Now()
- ongoing = true
- attempts = 0
- timeout.Reset(RekeyTimeout)
-
- case <-peer.timer.sendKeepalive.C:
-
- // active keep-alives
-
- peer.SendKeepAlive()
+/* This is the state machine for handshake initiation
+ *
+ * Associated with this routine is the signal "handshakeBegin"
+ * The routine will read from the "handshakeBegin" channel
+ * at most every RekeyTimeout or with exponential backoff
+ *
+ * Implements exponential backoff for retries
+ */
+func (peer *Peer) RoutineHandshakeInitiator() {
+ work := new(QueueOutboundElement)
+ device := peer.device
+ buffer := make([]byte, 1024)
+ logger := device.log.Debug
+ timeout := time.NewTimer(time.Hour)
- case <-peer.timer.handshakeTimeout.C:
+ logger.Println("Routine, handshake initator, started for peer", peer.id)
- // check if we can stop trying
+ func() {
+ for {
+ var attempts uint
+ var deadline time.Time
- if time.Now().Sub(begun) > MaxHandshakeAttempTime {
- peer.signal.flushNonceQueue <- true
- peer.timer.sendKeepalive.Stop()
- ongoing = false
- continue
+ select {
+ case <-peer.signal.handshakeBegin:
+ case <-peer.signal.stop:
+ return
}
- // otherwise, try again (exponental backoff)
-
- attempts += 1
- err := queueHandshakeInitiation()
- if err != nil {
- device.log.Error.Println("Failed to create initiation message:", err)
+ HandshakeLoop:
+ for run := true; run; {
+ // clear completed signal
+
+ select {
+ case <-peer.signal.handshakeCompleted:
+ case <-peer.signal.stop:
+ return
+ default:
+ }
+
+ // queue handshake
+
+ err := func() error {
+ work.mutex.Lock()
+ defer work.mutex.Unlock()
+
+ // create initiation
+
+ msg, err := device.CreateMessageInitiation(peer)
+ if err != nil {
+ return err
+ }
+
+ // marshal
+
+ writer := bytes.NewBuffer(buffer[:0])
+ binary.Write(writer, binary.LittleEndian, msg)
+ work.packet = writer.Bytes()
+ peer.mac.AddMacs(work.packet)
+ peer.InsertOutbound(work)
+ return nil
+ }()
+ if err != nil {
+ device.log.Error.Println("Failed to create initiation message:", err)
+ break
+ }
+ if attempts == 0 {
+ deadline = time.Now().Add(MaxHandshakeAttemptTime)
+ }
+
+ // set timeout
+
+ if !timeout.Stop() {
+ select {
+ case <-timeout.C:
+ default:
+ }
+ }
+ timeout.Reset((1 << attempts) * RekeyTimeout)
+ attempts += 1
+ device.log.Debug.Println("Handshake initiation attempt", attempts, "queued for peer", peer.id)
+ time.Sleep(RekeyTimeout)
+
+ // wait for handshake or timeout
+
+ select {
+ case <-peer.signal.stop:
+ return
+
+ case <-peer.signal.handshakeCompleted:
+ break HandshakeLoop
+
+ default:
+ select {
+
+ case <-peer.signal.stop:
+ return
+
+ case <-peer.signal.handshakeCompleted:
+ break HandshakeLoop
+
+ case <-timeout.C:
+ nextTimeout := (1 << attempts) * RekeyTimeout
+ if deadline.Before(time.Now().Add(nextTimeout)) {
+ // we do not have time for another attempt
+ peer.signal.flushNonceQueue <- struct{}{}
+ if !peer.timer.sendKeepalive.Stop() {
+ <-peer.timer.sendKeepalive.C
+ }
+ break HandshakeLoop
+ }
+ }
+ }
}
- peer.timer.handshakeTimeout.Reset((1 << attempts) * RekeyTimeout)
}
- }
+ }()
+
+ logger.Println("Routine, handshake initator, stopped for peer", peer.id)
}
-/* Handles packets related to handshake
+/* Handles incomming packets related to handshake
*
*
*/
@@ -140,33 +212,12 @@ func (device *Device) HandshakeWorker(queue chan struct {
// check for cookie
case MessageCookieReplyType:
+ if len(elem.msg) != MessageCookieReplySize {
+ continue
+ }
- case MessageTransportType:
- }
-
- }
-}
-
-func (device *Device) KeepKeyFresh(peer *Peer) {
-
- send := func() bool {
- peer.keyPairs.mutex.RLock()
- defer peer.keyPairs.mutex.RUnlock()
-
- kp := peer.keyPairs.current
- if kp == nil {
- return false
- }
-
- nonce := atomic.LoadUint64(&kp.sendNonce)
- if nonce > RekeyAfterMessage {
- return true
+ default:
+ device.log.Error.Println("Invalid message type in handshake queue")
}
-
- return kp.isInitiator && time.Now().Sub(kp.created) > RekeyAfterTime
- }()
-
- if send {
-
}
}