154 lines
3.2 KiB
Go
154 lines
3.2 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"sync/atomic"
|
|
"time"
|
|
)
|
|
|
|
/* Sends a keep-alive if no packets queued for peer
|
|
*
|
|
* Used by initiator of handshake and with active keep-alive
|
|
*/
|
|
func (peer *Peer) SendKeepAlive() bool {
|
|
elem := peer.device.NewOutboundElement()
|
|
elem.packet = nil
|
|
if len(peer.queue.nonce) == 0 {
|
|
select {
|
|
case peer.queue.nonce <- elem:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
/* 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()
|
|
|
|
kp := peer.keyPairs.current
|
|
if kp == nil {
|
|
return false
|
|
}
|
|
|
|
if !kp.isInitiator {
|
|
return false
|
|
}
|
|
|
|
nonce := atomic.LoadUint64(&kp.sendNonce)
|
|
if nonce > RekeyAfterMessages {
|
|
return true
|
|
}
|
|
return time.Now().Sub(kp.created) > RekeyAfterTime
|
|
}()
|
|
if send {
|
|
sendSignal(peer.signal.handshakeBegin)
|
|
}
|
|
}
|
|
|
|
/* 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 seconds
|
|
*/
|
|
func (peer *Peer) RoutineHandshakeInitiator() {
|
|
device := peer.device
|
|
logger := device.log.Debug
|
|
timeout := stoppedTimer()
|
|
|
|
var elem *QueueOutboundElement
|
|
|
|
logger.Println("Routine, handshake initator, started for peer", peer.id)
|
|
|
|
func() {
|
|
for {
|
|
var attempts uint
|
|
var deadline time.Time
|
|
|
|
// wait for signal
|
|
|
|
select {
|
|
case <-peer.signal.handshakeBegin:
|
|
case <-peer.signal.stop:
|
|
return
|
|
}
|
|
|
|
HandshakeLoop:
|
|
for {
|
|
// clear completed signal
|
|
|
|
select {
|
|
case <-peer.signal.handshakeCompleted:
|
|
case <-peer.signal.stop:
|
|
return
|
|
default:
|
|
}
|
|
|
|
// create initiation
|
|
|
|
if elem != nil {
|
|
elem.Drop()
|
|
}
|
|
elem = device.NewOutboundElement()
|
|
|
|
msg, err := device.CreateMessageInitiation(peer)
|
|
if err != nil {
|
|
device.log.Error.Println("Failed to create initiation message:", err)
|
|
break
|
|
}
|
|
|
|
// marshal & schedule for sending
|
|
|
|
writer := bytes.NewBuffer(elem.data[:0])
|
|
binary.Write(writer, binary.LittleEndian, msg)
|
|
elem.packet = writer.Bytes()
|
|
peer.mac.AddMacs(elem.packet)
|
|
addToOutboundQueue(peer.queue.outbound, elem)
|
|
|
|
if attempts == 0 {
|
|
deadline = time.Now().Add(MaxHandshakeAttemptTime)
|
|
}
|
|
|
|
// set timeout
|
|
|
|
attempts += 1
|
|
stopTimer(timeout)
|
|
timeout.Reset(RekeyTimeout)
|
|
device.log.Debug.Println("Handshake initiation attempt", attempts, "queued for peer", peer.id)
|
|
|
|
// wait for handshake or timeout
|
|
|
|
select {
|
|
case <-peer.signal.stop:
|
|
return
|
|
|
|
case <-peer.signal.handshakeCompleted:
|
|
device.log.Debug.Println("Handshake complete")
|
|
break HandshakeLoop
|
|
|
|
case <-timeout.C:
|
|
device.log.Debug.Println("Timeout")
|
|
if deadline.Before(time.Now().Add(RekeyTimeout)) {
|
|
peer.signal.flushNonceQueue <- struct{}{}
|
|
if !peer.timer.sendKeepalive.Stop() {
|
|
<-peer.timer.sendKeepalive.C
|
|
}
|
|
break HandshakeLoop
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
|
|
logger.Println("Routine, handshake initator, stopped for peer", peer.id)
|
|
}
|