5
0
mirror of https://github.com/cwinfo/yggdrasil-go.git synced 2024-11-29 23:41:35 +00:00

start migrating sessionInfo to be an actor

This commit is contained in:
Arceliar 2019-08-23 20:05:18 -05:00
parent 8e89816099
commit bbcbbaf3b1
5 changed files with 49 additions and 42 deletions

View File

@ -213,7 +213,7 @@ func (c *Core) GetSessions() []Session {
workerFunc := func() { workerFunc := func() {
session = Session{ session = Session{
Coords: append([]uint64{}, wire_coordsBytestoUint64s(sinfo.coords)...), Coords: append([]uint64{}, wire_coordsBytestoUint64s(sinfo.coords)...),
MTU: sinfo.getMTU(), MTU: sinfo._getMTU(),
BytesSent: sinfo.bytesSent, BytesSent: sinfo.bytesSent,
BytesRecvd: sinfo.bytesRecvd, BytesRecvd: sinfo.bytesRecvd,
Uptime: time.Now().Sub(sinfo.timeOpened), Uptime: time.Now().Sub(sinfo.timeOpened),

View File

@ -190,8 +190,8 @@ func (c *Conn) WriteNoCopy(msg FlowKeyMessage) error {
var err error var err error
sessionFunc := func() { sessionFunc := func() {
// Does the packet exceed the permitted size for the session? // Does the packet exceed the permitted size for the session?
if uint16(len(msg.Message)) > c.session.getMTU() { if uint16(len(msg.Message)) > c.session._getMTU() {
err = ConnError{errors.New("packet too big"), true, false, false, int(c.session.getMTU())} err = ConnError{errors.New("packet too big"), true, false, false, int(c.session._getMTU())}
return return
} }
// The rest of this work is session keep-alive traffic // The rest of this work is session keep-alive traffic
@ -201,10 +201,10 @@ func (c *Conn) WriteNoCopy(msg FlowKeyMessage) error {
// TODO double check that the above condition is correct // TODO double check that the above condition is correct
c.doSearch() c.doSearch()
} else { } else {
c.core.sessions.ping(c.session) c.session.ping(c.session) // TODO send from self if this becomes an actor
} }
case c.session.reset && c.session.pingTime.Before(c.session.time): case c.session.reset && c.session.pingTime.Before(c.session.time):
c.core.sessions.ping(c.session) c.session.ping(c.session) // TODO send from self if this becomes an actor
default: // Don't do anything, to keep traffic throttled default: // Don't do anything, to keep traffic throttled
} }
} }

View File

@ -98,7 +98,7 @@ func (r *router) insertPeer(from phony.IActor, info *dhtInfo) {
// Reset sessions and DHT after the switch sees our coords change // Reset sessions and DHT after the switch sees our coords change
func (r *router) reset(from phony.IActor) { func (r *router) reset(from phony.IActor) {
r.EnqueueFrom(from, func() { r.EnqueueFrom(from, func() {
r.core.sessions.reset() r.core.sessions.reset(r)
r.core.dht.reset() r.core.dht.reset()
}) })
} }
@ -111,14 +111,14 @@ func (r *router) _mainLoop() {
for { for {
select { select {
case <-ticker.C: case <-ticker.C:
r.SyncExec(func() { <-r.SyncExec(func() {
// Any periodic maintenance stuff goes here // Any periodic maintenance stuff goes here
r.core.switchTable.doMaintenance() r.core.switchTable.doMaintenance()
r.core.dht.doMaintenance() r.core.dht.doMaintenance()
r.core.sessions.cleanup() r.core.sessions.cleanup()
}) })
case e := <-r.reconfigure: case e := <-r.reconfigure:
r.SyncExec(func() { <-r.SyncExec(func() {
current := r.core.config.GetCurrent() current := r.core.config.GetCurrent()
e <- r.nodeinfo.setNodeInfo(current.NodeInfo, current.NodeInfoPrivacy) e <- r.nodeinfo.setNodeInfo(current.NodeInfo, current.NodeInfoPrivacy)
}) })
@ -252,5 +252,5 @@ func (r *router) _handleNodeInfo(bs []byte, fromKey *crypto.BoxPubKey) {
// TODO remove this, have things either be actors that send message or else call SyncExec directly // TODO remove this, have things either be actors that send message or else call SyncExec directly
func (r *router) doAdmin(f func()) { func (r *router) doAdmin(f func()) {
r.SyncExec(f) <-r.SyncExec(f)
} }

View File

@ -213,7 +213,7 @@ func (sinfo *searchInfo) checkDHTRes(res *dhtRes) bool {
} }
// FIXME (!) replay attacks could mess with coords? Give it a handle (tstamp)? // FIXME (!) replay attacks could mess with coords? Give it a handle (tstamp)?
sess.coords = res.Coords sess.coords = res.Coords
sinfo.core.sessions.ping(sess) sess.ping(&sinfo.core.router)
sinfo.callback(sess, nil) sinfo.callback(sess, nil)
// Cleanup // Cleanup
delete(sinfo.core.searches.searches, res.Dest) delete(sinfo.core.searches.searches, res.Dest)

View File

@ -14,6 +14,8 @@ import (
"github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/address"
"github.com/yggdrasil-network/yggdrasil-go/src/crypto" "github.com/yggdrasil-network/yggdrasil-go/src/crypto"
"github.com/yggdrasil-network/yggdrasil-go/src/util" "github.com/yggdrasil-network/yggdrasil-go/src/util"
"github.com/Arceliar/phony"
) )
// Duration that we keep track of old nonces per session, to allow some out-of-order packet delivery // Duration that we keep track of old nonces per session, to allow some out-of-order packet delivery
@ -37,7 +39,7 @@ func (h nonceHeap) peek() *crypto.BoxNonce { return &h[len(h)-1] }
// All the information we know about an active session. // 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. // 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 { type sessionInfo struct {
mutex sync.Mutex // Protects all of the below, use it any time you read/chance the contents of a session phony.Actor // Protects all of the below, use it any time you read/change the contents of a session
core *Core // core *Core //
reconfigure chan chan error // reconfigure chan chan error //
theirAddr address.Address // theirAddr address.Address //
@ -46,6 +48,7 @@ type sessionInfo struct {
theirSesPub crypto.BoxPubKey // theirSesPub crypto.BoxPubKey //
mySesPub crypto.BoxPubKey // mySesPub crypto.BoxPubKey //
mySesPriv crypto.BoxPrivKey // mySesPriv crypto.BoxPrivKey //
sharedPermKey crypto.BoxSharedKey // used for session pings
sharedSesKey crypto.BoxSharedKey // derived from session keys sharedSesKey crypto.BoxSharedKey // derived from session keys
theirHandle crypto.Handle // theirHandle crypto.Handle //
myHandle crypto.Handle // myHandle crypto.Handle //
@ -73,10 +76,9 @@ type sessionInfo struct {
send chan FlowKeyMessage // Packets with optional flow key go here, to be encrypted and sent send chan FlowKeyMessage // Packets with optional flow key go here, to be encrypted and sent
} }
// TODO remove this, call SyncExec directly
func (sinfo *sessionInfo) doFunc(f func()) { func (sinfo *sessionInfo) doFunc(f func()) {
sinfo.mutex.Lock() <-sinfo.SyncExec(f)
defer sinfo.mutex.Unlock()
f()
} }
// Represents a session ping/pong packet, andincludes information like public keys, a session handle, coords, a timestamp to prevent replays, and the tun/tap MTU. // Represents a session ping/pong packet, andincludes information like public keys, a session handle, coords, a timestamp to prevent replays, and the tun/tap MTU.
@ -92,7 +94,7 @@ type sessionPing struct {
// Updates session info in response to a ping, after checking that the ping is OK. // Updates session info in response to a ping, after checking that the ping is OK.
// Returns true if the session was updated, or false otherwise. // Returns true if the session was updated, or false otherwise.
func (s *sessionInfo) update(p *sessionPing) bool { func (s *sessionInfo) _update(p *sessionPing) bool {
if !(p.Tstamp > s.tstamp) { if !(p.Tstamp > s.tstamp) {
// To protect against replay attacks // To protect against replay attacks
return false return false
@ -214,6 +216,7 @@ func (ss *sessions) createSession(theirPermKey *crypto.BoxPubKey) *sessionInfo {
sinfo.core = ss.core sinfo.core = ss.core
sinfo.reconfigure = make(chan chan error, 1) sinfo.reconfigure = make(chan chan error, 1)
sinfo.theirPermPub = *theirPermKey sinfo.theirPermPub = *theirPermKey
sinfo.sharedPermKey = *ss.getSharedKey(&ss.core.boxPriv, &sinfo.theirPermPub)
pub, priv := crypto.NewBoxKeys() pub, priv := crypto.NewBoxKeys()
sinfo.mySesPub = *pub sinfo.mySesPub = *pub
sinfo.mySesPriv = *priv sinfo.mySesPriv = *priv
@ -257,7 +260,9 @@ func (ss *sessions) createSession(theirPermKey *crypto.BoxPubKey) *sessionInfo {
go func() { go func() {
// Run cleanup when the session is canceled // Run cleanup when the session is canceled
<-sinfo.cancel.Finished() <-sinfo.cancel.Finished()
sinfo.core.router.doAdmin(sinfo.close) sinfo.core.router.doAdmin(func() {
sinfo.core.sessions.removeSession(&sinfo)
})
}() }()
go sinfo.startWorkers() go sinfo.startWorkers()
return &sinfo return &sinfo
@ -292,7 +297,7 @@ func (ss *sessions) cleanup() {
} }
// Closes a session, removing it from sessions maps. // Closes a session, removing it from sessions maps.
func (sinfo *sessionInfo) close() { func (ss *sessions) removeSession(sinfo *sessionInfo) {
if s := sinfo.core.sessions.sinfos[sinfo.myHandle]; s == sinfo { if s := sinfo.core.sessions.sinfos[sinfo.myHandle]; s == sinfo {
delete(sinfo.core.sessions.sinfos, sinfo.myHandle) delete(sinfo.core.sessions.sinfos, sinfo.myHandle)
delete(sinfo.core.sessions.byTheirPerm, sinfo.theirPermPub) delete(sinfo.core.sessions.byTheirPerm, sinfo.theirPermPub)
@ -300,11 +305,11 @@ func (sinfo *sessionInfo) close() {
} }
// Returns a session ping appropriate for the given session info. // Returns a session ping appropriate for the given session info.
func (ss *sessions) getPing(sinfo *sessionInfo) sessionPing { func (sinfo *sessionInfo) _getPing() sessionPing {
loc := ss.core.switchTable.getLocator() loc := sinfo.core.switchTable.getLocator()
coords := loc.getCoords() coords := loc.getCoords()
ref := sessionPing{ ping := sessionPing{
SendPermPub: ss.core.boxPub, SendPermPub: sinfo.core.boxPub,
Handle: sinfo.myHandle, Handle: sinfo.myHandle,
SendSesPub: sinfo.mySesPub, SendSesPub: sinfo.mySesPub,
Tstamp: time.Now().Unix(), Tstamp: time.Now().Unix(),
@ -312,7 +317,7 @@ func (ss *sessions) getPing(sinfo *sessionInfo) sessionPing {
MTU: sinfo.myMTU, MTU: sinfo.myMTU,
} }
sinfo.myNonce.Increment() sinfo.myNonce.Increment()
return ref return ping
} }
// Gets the shared key for a pair of box keys. // Gets the shared key for a pair of box keys.
@ -339,27 +344,29 @@ func (ss *sessions) getSharedKey(myPriv *crypto.BoxPrivKey,
} }
// Sends a session ping by calling sendPingPong in ping mode. // Sends a session ping by calling sendPingPong in ping mode.
func (ss *sessions) ping(sinfo *sessionInfo) { func (sinfo *sessionInfo) ping(from phony.IActor) {
ss.sendPingPong(sinfo, false) sinfo.EnqueueFrom(from, func() {
sinfo._sendPingPong(false)
})
} }
// Calls getPing, sets the appropriate ping/pong flag, encodes to wire format, and send it. // Calls getPing, sets the appropriate ping/pong flag, encodes to wire format, and send it.
// Updates the time the last ping was sent in the session info. // Updates the time the last ping was sent in the session info.
func (ss *sessions) sendPingPong(sinfo *sessionInfo, isPong bool) { func (sinfo *sessionInfo) _sendPingPong(isPong bool) {
ping := ss.getPing(sinfo) ping := sinfo._getPing()
ping.IsPong = isPong ping.IsPong = isPong
bs := ping.encode() bs := ping.encode()
shared := ss.getSharedKey(&ss.core.boxPriv, &sinfo.theirPermPub) payload, nonce := crypto.BoxSeal(&sinfo.sharedPermKey, bs, nil)
payload, nonce := crypto.BoxSeal(shared, bs, nil)
p := wire_protoTrafficPacket{ p := wire_protoTrafficPacket{
Coords: sinfo.coords, Coords: sinfo.coords,
ToKey: sinfo.theirPermPub, ToKey: sinfo.theirPermPub,
FromKey: ss.core.boxPub, FromKey: sinfo.core.boxPub,
Nonce: *nonce, Nonce: *nonce,
Payload: payload, Payload: payload,
} }
packet := p.encode() packet := p.encode()
ss.core.router.out(packet) // TODO rewrite the below if/when the peer struct becomes an actor, to not go through the router first
sinfo.core.router.EnqueueFrom(sinfo, func() { sinfo.core.router.out(packet) })
if sinfo.pingTime.Before(sinfo.time) { if sinfo.pingTime.Before(sinfo.time) {
sinfo.pingTime = time.Now() sinfo.pingTime = time.Now()
} }
@ -371,9 +378,9 @@ func (ss *sessions) handlePing(ping *sessionPing) {
// Get the corresponding session (or create a new session) // Get the corresponding session (or create a new session)
sinfo, isIn := ss.getByTheirPerm(&ping.SendPermPub) sinfo, isIn := ss.getByTheirPerm(&ping.SendPermPub)
switch { switch {
case ping.IsPong: // This is a response, not an initial ping, so ignore it.
case isIn: // Session already exists case isIn: // Session already exists
case !ss.isSessionAllowed(&ping.SendPermPub, false): // Session is not allowed case !ss.isSessionAllowed(&ping.SendPermPub, false): // Session is not allowed
case ping.IsPong: // This is a response, not an initial ping, so ignore it.
default: default:
ss.listenerMutex.Lock() ss.listenerMutex.Lock()
if ss.listener != nil { if ss.listener != nil {
@ -393,13 +400,13 @@ func (ss *sessions) handlePing(ping *sessionPing) {
ss.listenerMutex.Unlock() ss.listenerMutex.Unlock()
} }
if sinfo != nil { if sinfo != nil {
sinfo.doFunc(func() { sinfo.EnqueueFrom(&ss.core.router, func() {
// Update the session // Update the session
if !sinfo.update(ping) { /*panic("Should not happen in testing")*/ if !sinfo._update(ping) { /*panic("Should not happen in testing")*/
return return
} }
if !ping.IsPong { if !ping.IsPong {
ss.sendPingPong(sinfo, true) sinfo._sendPingPong(true)
} }
}) })
} }
@ -408,7 +415,7 @@ func (ss *sessions) handlePing(ping *sessionPing) {
// Get the MTU of the session. // Get the MTU of the session.
// Will be equal to the smaller of this node's MTU or the remote node's MTU. // Will be equal to the smaller of this node's MTU or the remote node's MTU.
// If sending over links with a maximum message size (this was a thing with the old UDP code), it could be further lowered, to a minimum of 1280. // If sending over links with a maximum message size (this was a thing with the old UDP code), it could be further lowered, to a minimum of 1280.
func (sinfo *sessionInfo) getMTU() uint16 { func (sinfo *sessionInfo) _getMTU() uint16 {
if sinfo.theirMTU == 0 || sinfo.myMTU == 0 { if sinfo.theirMTU == 0 || sinfo.myMTU == 0 {
return 0 return 0
} }
@ -419,7 +426,7 @@ 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. // 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 { func (sinfo *sessionInfo) _nonceIsOK(theirNonce *crypto.BoxNonce) bool {
// The bitmask is to allow for some non-duplicate out-of-order packets // The bitmask is to allow for some non-duplicate out-of-order packets
if theirNonce.Minus(&sinfo.theirNonce) > 0 { if theirNonce.Minus(&sinfo.theirNonce) > 0 {
// This is newer than the newest nonce we've seen // This is newer than the newest nonce we've seen
@ -437,7 +444,7 @@ func (sinfo *sessionInfo) nonceIsOK(theirNonce *crypto.BoxNonce) bool {
} }
// 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 // 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) { func (sinfo *sessionInfo) _updateNonce(theirNonce *crypto.BoxNonce) {
// Start with some cleanup // Start with some cleanup
for len(sinfo.theirNonceHeap) > 64 { for len(sinfo.theirNonceHeap) > 64 {
if time.Since(sinfo.theirNonceMap[*sinfo.theirNonceHeap.peek()]) < nonceWindow { if time.Since(sinfo.theirNonceMap[*sinfo.theirNonceHeap.peek()]) < nonceWindow {
@ -459,9 +466,9 @@ func (sinfo *sessionInfo) updateNonce(theirNonce *crypto.BoxNonce) {
// Resets all sessions to an uninitialized state. // Resets all sessions to an uninitialized state.
// Called after coord changes, so attemtps to use a session will trigger a new ping and notify the remote end of the coord change. // Called after coord changes, so attemtps to use a session will trigger a new ping and notify the remote end of the coord change.
func (ss *sessions) reset() { func (ss *sessions) reset(from phony.IActor) {
for _, sinfo := range ss.sinfos { for _, sinfo := range ss.sinfos {
sinfo.doFunc(func() { sinfo.EnqueueFrom(from, func() {
sinfo.reset = true sinfo.reset = true
}) })
} }
@ -492,7 +499,7 @@ func (sinfo *sessionInfo) recvWorker() {
var err error var err error
var k crypto.BoxSharedKey var k crypto.BoxSharedKey
sessionFunc := func() { sessionFunc := func() {
if !sinfo.nonceIsOK(&p.Nonce) { if !sinfo._nonceIsOK(&p.Nonce) {
err = ConnError{errors.New("packet dropped due to invalid nonce"), false, true, false, 0} err = ConnError{errors.New("packet dropped due to invalid nonce"), false, true, false, 0}
return return
} }
@ -514,12 +521,12 @@ func (sinfo *sessionInfo) recvWorker() {
return return
} }
sessionFunc = func() { sessionFunc = func() {
if k != sinfo.sharedSesKey || !sinfo.nonceIsOK(&p.Nonce) { if k != sinfo.sharedSesKey || !sinfo._nonceIsOK(&p.Nonce) {
// The session updated in the mean time, so return an error // The session updated in the mean time, so return an error
err = ConnError{errors.New("session updated during crypto operation"), false, true, false, 0} err = ConnError{errors.New("session updated during crypto operation"), false, true, false, 0}
return return
} }
sinfo.updateNonce(&p.Nonce) sinfo._updateNonce(&p.Nonce)
sinfo.time = time.Now() sinfo.time = time.Now()
sinfo.bytesRecvd += uint64(len(bs)) sinfo.bytesRecvd += uint64(len(bs))
} }