diff options
Diffstat (limited to 'src/handshake.go')
-rw-r--r-- | src/handshake.go | 241 |
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 { - } } |