Completed initial version of outbound flow
This commit is contained in:
259
src/handshake.go
259
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()
|
||||
|
||||
// create initiation
|
||||
|
||||
msg, err := device.CreateMessageInitiation(peer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// create "work" element
|
||||
|
||||
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
|
||||
func StoppedTimer() *time.Timer {
|
||||
timer := time.NewTimer(time.Hour)
|
||||
if !timer.Stop() {
|
||||
<-timer.C
|
||||
}
|
||||
return timer
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-peer.signal.stopInitiator:
|
||||
return
|
||||
/* 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()
|
||||
|
||||
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()
|
||||
|
||||
case <-peer.timer.handshakeTimeout.C:
|
||||
|
||||
// check if we can stop trying
|
||||
|
||||
if time.Now().Sub(begun) > MaxHandshakeAttempTime {
|
||||
peer.signal.flushNonceQueue <- true
|
||||
peer.timer.sendKeepalive.Stop()
|
||||
ongoing = false
|
||||
continue
|
||||
}
|
||||
|
||||
// otherwise, try again (exponental backoff)
|
||||
|
||||
attempts += 1
|
||||
err := queueHandshakeInitiation()
|
||||
if err != nil {
|
||||
device.log.Error.Println("Failed to create initiation message:", err)
|
||||
}
|
||||
peer.timer.handshakeTimeout.Reset((1 << attempts) * RekeyTimeout)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
/* Handles packets related to handshake
|
||||
/* 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)
|
||||
|
||||
logger.Println("Routine, handshake initator, started for peer", peer.id)
|
||||
|
||||
func() {
|
||||
for {
|
||||
var attempts uint
|
||||
var deadline time.Time
|
||||
|
||||
select {
|
||||
case <-peer.signal.handshakeBegin:
|
||||
case <-peer.signal.stop:
|
||||
return
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
logger.Println("Routine, handshake initator, stopped for peer", peer.id)
|
||||
}
|
||||
|
||||
/* 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:
|
||||
default:
|
||||
device.log.Error.Println("Invalid message type in handshake queue")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
return kp.isInitiator && time.Now().Sub(kp.created) > RekeyAfterTime
|
||||
}()
|
||||
|
||||
if send {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user