mirror of
https://github.com/yggdrasil-network/yggdrasil-go.git
synced 2024-11-23 18:15:24 +00:00
keep track of recent nonces with a heap and a map instead of a fixed-size bitmask
This commit is contained in:
parent
fdac8932a8
commit
03b8af9f1a
@ -6,6 +6,7 @@ package yggdrasil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"container/heap"
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
@ -15,42 +16,62 @@ import (
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/util"
|
||||
)
|
||||
|
||||
// Duration that we keep track of old nonces per session, to allow some out-of-order packet delivery
|
||||
const nonceWindow = time.Second
|
||||
|
||||
// A heap of nonces, used with a map[nonce]time to allow out-of-order packets a little time to arrive without rejecting them
|
||||
// Less is backwards so the oldest node is the highest priority for Pop
|
||||
type nonceHeap []crypto.BoxNonce
|
||||
|
||||
func (h nonceHeap) Len() int { return len(h) }
|
||||
func (h nonceHeap) Less(i, j int) bool { return h[i].Minus(&h[j]) > 0 }
|
||||
func (h nonceHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
|
||||
func (h *nonceHeap) Push(x interface{}) { *h = append(*h, x.(crypto.BoxNonce)) }
|
||||
func (h *nonceHeap) Pop() interface{} {
|
||||
l := len(*h)
|
||||
var n crypto.BoxNonce
|
||||
n, *h = (*h)[l-1], (*h)[:l-1]
|
||||
return n
|
||||
}
|
||||
func (h nonceHeap) peek() *crypto.BoxNonce { return &h[len(h)-1] }
|
||||
|
||||
// All the information we know about an active session.
|
||||
// This includes coords, permanent and ephemeral keys, handles and nonces, various sorts of timing information for timeout and maintenance, and some metadata for the admin API.
|
||||
type sessionInfo struct {
|
||||
mutex sync.Mutex // Protects all of the below, use it any time you read/chance the contents of a session
|
||||
core *Core //
|
||||
reconfigure chan chan error //
|
||||
theirAddr address.Address //
|
||||
theirSubnet address.Subnet //
|
||||
theirPermPub crypto.BoxPubKey //
|
||||
theirSesPub crypto.BoxPubKey //
|
||||
mySesPub crypto.BoxPubKey //
|
||||
mySesPriv crypto.BoxPrivKey //
|
||||
sharedSesKey crypto.BoxSharedKey // derived from session keys
|
||||
theirHandle crypto.Handle //
|
||||
myHandle crypto.Handle //
|
||||
theirNonce crypto.BoxNonce //
|
||||
theirNonceMask uint64 //
|
||||
myNonce crypto.BoxNonce //
|
||||
theirMTU uint16 //
|
||||
myMTU uint16 //
|
||||
wasMTUFixed bool // Was the MTU fixed by a receive error?
|
||||
timeOpened time.Time // Time the sessino was opened
|
||||
time time.Time // Time we last received a packet
|
||||
mtuTime time.Time // time myMTU was last changed
|
||||
pingTime time.Time // time the first ping was sent since the last received packet
|
||||
pingSend time.Time // time the last ping was sent
|
||||
coords []byte // coords of destination
|
||||
reset bool // reset if coords change
|
||||
tstamp int64 // ATOMIC - tstamp from their last session ping, replay attack mitigation
|
||||
bytesSent uint64 // Bytes of real traffic sent in this session
|
||||
bytesRecvd uint64 // Bytes of real traffic received in this session
|
||||
init chan struct{} // Closed when the first session pong arrives, used to signal that the session is ready for initial use
|
||||
cancel util.Cancellation // Used to terminate workers
|
||||
fromRouter chan wire_trafficPacket // Received packets go here, to be decrypted by the session
|
||||
recv chan []byte // Decrypted packets go here, picked up by the associated Conn
|
||||
send chan FlowKeyMessage // Packets with optional flow key go here, to be encrypted and sent
|
||||
mutex sync.Mutex // Protects all of the below, use it any time you read/chance the contents of a session
|
||||
core *Core //
|
||||
reconfigure chan chan error //
|
||||
theirAddr address.Address //
|
||||
theirSubnet address.Subnet //
|
||||
theirPermPub crypto.BoxPubKey //
|
||||
theirSesPub crypto.BoxPubKey //
|
||||
mySesPub crypto.BoxPubKey //
|
||||
mySesPriv crypto.BoxPrivKey //
|
||||
sharedSesKey crypto.BoxSharedKey // derived from session keys
|
||||
theirHandle crypto.Handle //
|
||||
myHandle crypto.Handle //
|
||||
theirNonce crypto.BoxNonce //
|
||||
theirNonceHeap nonceHeap // priority queue to keep track of the lowest nonce we recently accepted
|
||||
theirNonceMap map[crypto.BoxNonce]time.Time // time we added each nonce to the heap
|
||||
myNonce crypto.BoxNonce //
|
||||
theirMTU uint16 //
|
||||
myMTU uint16 //
|
||||
wasMTUFixed bool // Was the MTU fixed by a receive error?
|
||||
timeOpened time.Time // Time the sessino was opened
|
||||
time time.Time // Time we last received a packet
|
||||
mtuTime time.Time // time myMTU was last changed
|
||||
pingTime time.Time // time the first ping was sent since the last received packet
|
||||
pingSend time.Time // time the last ping was sent
|
||||
coords []byte // coords of destination
|
||||
reset bool // reset if coords change
|
||||
tstamp int64 // ATOMIC - tstamp from their last session ping, replay attack mitigation
|
||||
bytesSent uint64 // Bytes of real traffic sent in this session
|
||||
bytesRecvd uint64 // Bytes of real traffic received in this session
|
||||
init chan struct{} // Closed when the first session pong arrives, used to signal that the session is ready for initial use
|
||||
cancel util.Cancellation // Used to terminate workers
|
||||
fromRouter chan wire_trafficPacket // Received packets go here, to be decrypted by the session
|
||||
recv chan []byte // Decrypted packets go here, picked up by the associated Conn
|
||||
send chan FlowKeyMessage // Packets with optional flow key go here, to be encrypted and sent
|
||||
}
|
||||
|
||||
func (sinfo *sessionInfo) doFunc(f func()) {
|
||||
@ -87,7 +108,8 @@ func (s *sessionInfo) update(p *sessionPing) bool {
|
||||
s.theirHandle = p.Handle
|
||||
s.sharedSesKey = *crypto.GetSharedKey(&s.mySesPriv, &s.theirSesPub)
|
||||
s.theirNonce = crypto.BoxNonce{}
|
||||
s.theirNonceMask = 0
|
||||
s.theirNonceHeap = nil
|
||||
s.theirNonceMap = make(map[crypto.BoxNonce]time.Time)
|
||||
}
|
||||
if p.MTU >= 1280 || p.MTU == 0 {
|
||||
s.theirMTU = p.MTU
|
||||
@ -400,27 +422,40 @@ func (sinfo *sessionInfo) getMTU() uint16 {
|
||||
// Checks if a packet's nonce is recent enough to fall within the window of allowed packets, and not already received.
|
||||
func (sinfo *sessionInfo) nonceIsOK(theirNonce *crypto.BoxNonce) bool {
|
||||
// The bitmask is to allow for some non-duplicate out-of-order packets
|
||||
diff := theirNonce.Minus(&sinfo.theirNonce)
|
||||
if diff > 0 {
|
||||
if theirNonce.Minus(&sinfo.theirNonce) > 0 {
|
||||
// This is newer than the newest nonce we've seen
|
||||
return true
|
||||
}
|
||||
return ^sinfo.theirNonceMask&(0x01<<uint64(-diff)) != 0
|
||||
if len(sinfo.theirNonceHeap) > 0 {
|
||||
if theirNonce.Minus(sinfo.theirNonceHeap.peek()) > 0 {
|
||||
if _, isIn := sinfo.theirNonceMap[*theirNonce]; !isIn {
|
||||
// This nonce is recent enough that we keep track of older nonces, but it's not one we've seen yet
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Updates the nonce mask by (possibly) shifting the bitmask and setting the bit corresponding to this nonce to 1, and then updating the most recent nonce
|
||||
func (sinfo *sessionInfo) updateNonce(theirNonce *crypto.BoxNonce) {
|
||||
// Shift nonce mask if needed
|
||||
// Set bit
|
||||
diff := theirNonce.Minus(&sinfo.theirNonce)
|
||||
if diff > 0 {
|
||||
// This nonce is newer, so shift the window before setting the bit, and update theirNonce in the session info.
|
||||
sinfo.theirNonceMask <<= uint64(diff)
|
||||
sinfo.theirNonceMask &= 0x01
|
||||
sinfo.theirNonce = *theirNonce
|
||||
} else {
|
||||
// This nonce is older, so set the bit but do not shift the window.
|
||||
sinfo.theirNonceMask &= 0x01 << uint64(-diff)
|
||||
// Start with some cleanup
|
||||
for len(sinfo.theirNonceHeap) > 64 {
|
||||
if time.Since(sinfo.theirNonceMap[*sinfo.theirNonceHeap.peek()]) < nonceWindow {
|
||||
// This nonce is still fairly new, so keep it around
|
||||
break
|
||||
}
|
||||
// TODO? reallocate the map in some cases, to free unused map space?
|
||||
delete(sinfo.theirNonceMap, *sinfo.theirNonceHeap.peek())
|
||||
heap.Pop(&sinfo.theirNonceHeap)
|
||||
}
|
||||
if theirNonce.Minus(&sinfo.theirNonce) > 0 {
|
||||
// This nonce is the newest we've seen, so make a note of that
|
||||
sinfo.theirNonce = *theirNonce
|
||||
}
|
||||
// Add it to the heap/map so we know not to allow it again
|
||||
heap.Push(&sinfo.theirNonceHeap, *theirNonce)
|
||||
sinfo.theirNonceMap[*theirNonce] = time.Now()
|
||||
}
|
||||
|
||||
// Resets all sessions to an uninitialized state.
|
||||
|
Loading…
Reference in New Issue
Block a user