From a64f7320d8ce8cd8c3f079e9bf7d7d92b7cb00b0 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sat, 31 Aug 2019 16:27:36 -0500 Subject: [PATCH 1/5] update phony, add mobile versions of util bytes functions that don't try to store anything --- go.mod | 2 +- go.sum | 4 ++-- src/util/bytes_mobile.go | 13 +++++++++++++ src/util/bytes_other.go | 18 ++++++++++++++++++ src/util/util.go | 14 -------------- 5 files changed, 34 insertions(+), 17 deletions(-) create mode 100644 src/util/bytes_mobile.go create mode 100644 src/util/bytes_other.go diff --git a/go.mod b/go.mod index 3b0af5a4..7a96c97e 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,7 @@ module github.com/yggdrasil-network/yggdrasil-go require ( - github.com/Arceliar/phony v0.0.0-20190831050304-94a6d3da9ba4 + github.com/Arceliar/phony v0.0.0-20190831212216-7018ff05d824 github.com/gologme/log v0.0.0-20181207131047-4e5d8ccb38e8 github.com/hashicorp/go-syslog v1.0.0 github.com/hjson/hjson-go v0.0.0-20181010104306-a25ecf6bd222 diff --git a/go.sum b/go.sum index df7ff630..b0b891e9 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Arceliar/phony v0.0.0-20190831050304-94a6d3da9ba4 h1:OePImnPRBqS6JiHuVVq4Rfvt2yyhqMpWCB63PrwGrJE= -github.com/Arceliar/phony v0.0.0-20190831050304-94a6d3da9ba4/go.mod h1:6Lkn+/zJilRMsKmbmG1RPoamiArC6HS73xbwRyp3UyI= +github.com/Arceliar/phony v0.0.0-20190831212216-7018ff05d824 h1:JY7qh6yR87H8xgPUTYrrqa5cajb7zynKsbAdjhsVkyU= +github.com/Arceliar/phony v0.0.0-20190831212216-7018ff05d824/go.mod h1:6Lkn+/zJilRMsKmbmG1RPoamiArC6HS73xbwRyp3UyI= github.com/gologme/log v0.0.0-20181207131047-4e5d8ccb38e8 h1:WD8iJ37bRNwvETMfVTusVSAi0WdXTpfNVGY2aHycNKY= github.com/gologme/log v0.0.0-20181207131047-4e5d8ccb38e8/go.mod h1:gq31gQ8wEHkR+WekdWsqDuf8pXTUZA9BnnzTuPz1Y9U= github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= diff --git a/src/util/bytes_mobile.go b/src/util/bytes_mobile.go new file mode 100644 index 00000000..ceab763c --- /dev/null +++ b/src/util/bytes_mobile.go @@ -0,0 +1,13 @@ +//+build mobile + +package util + +// On mobile, just return a nil slice. +func GetBytes() []byte { + return nil +} + +// On mobile, don't do anything. +func PutBytes(bs []byte) { + return +} diff --git a/src/util/bytes_other.go b/src/util/bytes_other.go new file mode 100644 index 00000000..41b8bec0 --- /dev/null +++ b/src/util/bytes_other.go @@ -0,0 +1,18 @@ +//+build !mobile + +package util + +import "sync" + +// This is used to buffer recently used slices of bytes, to prevent allocations in the hot loops. +var byteStore = sync.Pool{New: func() interface{} { return []byte(nil) }} + +// Gets an empty slice from the byte store. +func GetBytes() []byte { + return byteStore.Get().([]byte)[:0] +} + +// Puts a slice in the store. +func PutBytes(bs []byte) { + byteStore.Put(bs) +} diff --git a/src/util/util.go b/src/util/util.go index a588a35c..97250122 100644 --- a/src/util/util.go +++ b/src/util/util.go @@ -6,7 +6,6 @@ import ( "runtime" "strconv" "strings" - "sync" "time" ) @@ -25,19 +24,6 @@ func UnlockThread() { runtime.UnlockOSThread() } -// This is used to buffer recently used slices of bytes, to prevent allocations in the hot loops. -var byteStore = sync.Pool{New: func() interface{} { return []byte(nil) }} - -// Gets an empty slice from the byte store. -func GetBytes() []byte { - return byteStore.Get().([]byte)[:0] -} - -// Puts a slice in the store. -func PutBytes(bs []byte) { - byteStore.Put(bs) -} - // Gets a slice of the appropriate length, reusing existing slice capacity when possible func ResizeBytes(bs []byte, length int) []byte { if cap(bs) >= length { From 0806f3e6eacc965a8019dbe04f93046eecd5809d Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sat, 31 Aug 2019 16:49:13 -0500 Subject: [PATCH 2/5] upgrade phony --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 7a96c97e..d2ca6b7c 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,7 @@ module github.com/yggdrasil-network/yggdrasil-go require ( - github.com/Arceliar/phony v0.0.0-20190831212216-7018ff05d824 + github.com/Arceliar/phony v0.0.0-20190831214819-9b642ea019ad github.com/gologme/log v0.0.0-20181207131047-4e5d8ccb38e8 github.com/hashicorp/go-syslog v1.0.0 github.com/hjson/hjson-go v0.0.0-20181010104306-a25ecf6bd222 diff --git a/go.sum b/go.sum index b0b891e9..f0fbacaf 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Arceliar/phony v0.0.0-20190831212216-7018ff05d824 h1:JY7qh6yR87H8xgPUTYrrqa5cajb7zynKsbAdjhsVkyU= -github.com/Arceliar/phony v0.0.0-20190831212216-7018ff05d824/go.mod h1:6Lkn+/zJilRMsKmbmG1RPoamiArC6HS73xbwRyp3UyI= +github.com/Arceliar/phony v0.0.0-20190831214819-9b642ea019ad h1:670inqspOp+tAnSvkOBgrKGOIT4605Jt+6KGi2j2/S8= +github.com/Arceliar/phony v0.0.0-20190831214819-9b642ea019ad/go.mod h1:6Lkn+/zJilRMsKmbmG1RPoamiArC6HS73xbwRyp3UyI= github.com/gologme/log v0.0.0-20181207131047-4e5d8ccb38e8 h1:WD8iJ37bRNwvETMfVTusVSAi0WdXTpfNVGY2aHycNKY= github.com/gologme/log v0.0.0-20181207131047-4e5d8ccb38e8/go.mod h1:gq31gQ8wEHkR+WekdWsqDuf8pXTUZA9BnnzTuPz1Y9U= github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= From cabdc27a54cee361ef6635295dd9632086df017b Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sat, 31 Aug 2019 17:39:05 -0500 Subject: [PATCH 3/5] change how nonce is tracked, so we allow packets if we've recently received a highest nonce ever, but don't bother tracking all received nonce values, this means duplicate packets are possible but only for a small window of time (and significantly reduces memory usage per session) --- src/yggdrasil/session.go | 107 ++++++++++++--------------------------- 1 file changed, 33 insertions(+), 74 deletions(-) diff --git a/src/yggdrasil/session.go b/src/yggdrasil/session.go index d209a0de..8a6d16fc 100644 --- a/src/yggdrasil/session.go +++ b/src/yggdrasil/session.go @@ -6,7 +6,6 @@ package yggdrasil import ( "bytes" - "container/heap" "sync" "time" @@ -20,57 +19,40 @@ import ( // 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 -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[0] } - // 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 { - phony.Inbox // Protects all of the below, use it any time you read/change the contents of a session - sessions *sessions // - theirAddr address.Address // - theirSubnet address.Subnet // - theirPermPub crypto.BoxPubKey // - theirSesPub crypto.BoxPubKey // - mySesPub crypto.BoxPubKey // - mySesPriv crypto.BoxPrivKey // - sharedPermKey crypto.BoxSharedKey // used for session pings - 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 - conn *Conn // The associated Conn object - callbacks []chan func() // Finished work from crypto workers + phony.Inbox // Protects all of the below, use it any time you read/change the contents of a session + sessions *sessions // + theirAddr address.Address // + theirSubnet address.Subnet // + theirPermPub crypto.BoxPubKey // + theirSesPub crypto.BoxPubKey // + mySesPub crypto.BoxPubKey // + mySesPriv crypto.BoxPrivKey // + sharedPermKey crypto.BoxSharedKey // used for session pings + sharedSesKey crypto.BoxSharedKey // derived from session keys + theirHandle crypto.Handle // + myHandle crypto.Handle // + theirNonce crypto.BoxNonce // + 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 + conn *Conn // The associated Conn object + callbacks []chan func() // Finished work from crypto workers } func (sinfo *sessionInfo) reconfigure() { @@ -105,8 +87,6 @@ func (s *sessionInfo) _update(p *sessionPing) bool { s.theirHandle = p.Handle s.sharedSesKey = *crypto.GetSharedKey(&s.mySesPriv, &s.theirSesPub) s.theirNonce = crypto.BoxNonce{} - s.theirNonceHeap = nil - s.theirNonceMap = make(map[crypto.BoxNonce]time.Time) } if p.MTU >= 1280 || p.MTU == 0 { s.theirMTU = p.MTU @@ -420,36 +400,16 @@ func (sinfo *sessionInfo) _nonceIsOK(theirNonce *crypto.BoxNonce) bool { // This is newer than the newest nonce we've seen return true } - 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 + return time.Since(sinfo.time) < nonceWindow } // 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) { - // 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 + sinfo.time = time.Now() } - // 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. @@ -515,7 +475,6 @@ func (sinfo *sessionInfo) _recvPacket(p *wire_trafficPacket) { return } sinfo._updateNonce(&p.Nonce) - sinfo.time = time.Now() sinfo.bytesRecvd += uint64(len(bs)) sinfo.conn.recvMsg(sinfo, bs) } From 3a493fe8945227b20ecbb101af014c847d15d912 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sun, 1 Sep 2019 11:08:25 -0500 Subject: [PATCH 4/5] gc more often on mobile --- src/util/bytes_mobile.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/util/bytes_mobile.go b/src/util/bytes_mobile.go index ceab763c..09e78050 100644 --- a/src/util/bytes_mobile.go +++ b/src/util/bytes_mobile.go @@ -2,6 +2,12 @@ package util +import "runtime/debug" + +func init() { + debug.SetGCPercent(25) +} + // On mobile, just return a nil slice. func GetBytes() []byte { return nil From e0ea845cdc26f47eeb2ac41e233d76b744f69918 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 1 Sep 2019 17:50:15 +0100 Subject: [PATCH 5/5] Update build --- build | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/build b/build index c1e5f863..7ca5f4fa 100755 --- a/build +++ b/build @@ -34,13 +34,15 @@ if [ $IOS ]; then gomobile bind -target ios -tags mobile -ldflags="$LDFLAGS $STRIP" -gcflags="$GCFLAGS" \ github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil \ github.com/yggdrasil-network/yggdrasil-go/src/config \ - github.com/yggdrasil-network/yggdrasil-extras/src/mobile + github.com/yggdrasil-network/yggdrasil-extras/src/mobile \ + github.com/yggdrasil-network/yggdrasil-extras/src/dummy elif [ $ANDROID ]; then echo "Building aar for Android" gomobile bind -target android -tags mobile -ldflags="$LDFLAGS $STRIP" -gcflags="$GCFLAGS" \ github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil \ github.com/yggdrasil-network/yggdrasil-go/src/config \ - github.com/yggdrasil-network/yggdrasil-extras/src/mobile + github.com/yggdrasil-network/yggdrasil-extras/src/mobile \ + github.com/yggdrasil-network/yggdrasil-extras/src/dummy else for CMD in `ls cmd/` ; do echo "Building: $CMD"