mirror of
				https://github.com/yggdrasil-network/yggdrasil-go.git
				synced 2025-11-04 11:15:07 +03:00 
			
		
		
		
	start migrating sessionInfo to be an actor
This commit is contained in:
		
							parent
							
								
									8e89816099
								
							
						
					
					
						commit
						bbcbbaf3b1
					
				
					 5 changed files with 49 additions and 42 deletions
				
			
		| 
						 | 
				
			
			@ -213,7 +213,7 @@ func (c *Core) GetSessions() []Session {
 | 
			
		|||
			workerFunc := func() {
 | 
			
		||||
				session = Session{
 | 
			
		||||
					Coords:      append([]uint64{}, wire_coordsBytestoUint64s(sinfo.coords)...),
 | 
			
		||||
					MTU:         sinfo.getMTU(),
 | 
			
		||||
					MTU:         sinfo._getMTU(),
 | 
			
		||||
					BytesSent:   sinfo.bytesSent,
 | 
			
		||||
					BytesRecvd:  sinfo.bytesRecvd,
 | 
			
		||||
					Uptime:      time.Now().Sub(sinfo.timeOpened),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -190,8 +190,8 @@ func (c *Conn) WriteNoCopy(msg FlowKeyMessage) error {
 | 
			
		|||
	var err error
 | 
			
		||||
	sessionFunc := func() {
 | 
			
		||||
		// Does the packet exceed the permitted size for the session?
 | 
			
		||||
		if uint16(len(msg.Message)) > c.session.getMTU() {
 | 
			
		||||
			err = ConnError{errors.New("packet too big"), true, false, false, int(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())}
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		// 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
 | 
			
		||||
				c.doSearch()
 | 
			
		||||
			} 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):
 | 
			
		||||
			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
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -98,7 +98,7 @@ func (r *router) insertPeer(from phony.IActor, info *dhtInfo) {
 | 
			
		|||
// Reset sessions and DHT after the switch sees our coords change
 | 
			
		||||
func (r *router) reset(from phony.IActor) {
 | 
			
		||||
	r.EnqueueFrom(from, func() {
 | 
			
		||||
		r.core.sessions.reset()
 | 
			
		||||
		r.core.sessions.reset(r)
 | 
			
		||||
		r.core.dht.reset()
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -111,14 +111,14 @@ func (r *router) _mainLoop() {
 | 
			
		|||
	for {
 | 
			
		||||
		select {
 | 
			
		||||
		case <-ticker.C:
 | 
			
		||||
			r.SyncExec(func() {
 | 
			
		||||
			<-r.SyncExec(func() {
 | 
			
		||||
				// Any periodic maintenance stuff goes here
 | 
			
		||||
				r.core.switchTable.doMaintenance()
 | 
			
		||||
				r.core.dht.doMaintenance()
 | 
			
		||||
				r.core.sessions.cleanup()
 | 
			
		||||
			})
 | 
			
		||||
		case e := <-r.reconfigure:
 | 
			
		||||
			r.SyncExec(func() {
 | 
			
		||||
			<-r.SyncExec(func() {
 | 
			
		||||
				current := r.core.config.GetCurrent()
 | 
			
		||||
				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
 | 
			
		||||
func (r *router) doAdmin(f func()) {
 | 
			
		||||
	r.SyncExec(f)
 | 
			
		||||
	<-r.SyncExec(f)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -213,7 +213,7 @@ func (sinfo *searchInfo) checkDHTRes(res *dhtRes) bool {
 | 
			
		|||
	}
 | 
			
		||||
	// FIXME (!) replay attacks could mess with coords? Give it a handle (tstamp)?
 | 
			
		||||
	sess.coords = res.Coords
 | 
			
		||||
	sinfo.core.sessions.ping(sess)
 | 
			
		||||
	sess.ping(&sinfo.core.router)
 | 
			
		||||
	sinfo.callback(sess, nil)
 | 
			
		||||
	// Cleanup
 | 
			
		||||
	delete(sinfo.core.searches.searches, res.Dest)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,6 +14,8 @@ import (
 | 
			
		|||
	"github.com/yggdrasil-network/yggdrasil-go/src/address"
 | 
			
		||||
	"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
 | 
			
		||||
	"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
 | 
			
		||||
| 
						 | 
				
			
			@ -37,7 +39,7 @@ 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
 | 
			
		||||
	phony.Actor                                  // Protects all of the below, use it any time you read/change the contents of a session
 | 
			
		||||
	core           *Core                         //
 | 
			
		||||
	reconfigure    chan chan error               //
 | 
			
		||||
	theirAddr      address.Address               //
 | 
			
		||||
| 
						 | 
				
			
			@ -46,6 +48,7 @@ type sessionInfo struct {
 | 
			
		|||
	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                 //
 | 
			
		||||
| 
						 | 
				
			
			@ -73,10 +76,9 @@ type sessionInfo struct {
 | 
			
		|||
	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()) {
 | 
			
		||||
	sinfo.mutex.Lock()
 | 
			
		||||
	defer sinfo.mutex.Unlock()
 | 
			
		||||
	f()
 | 
			
		||||
	<-sinfo.SyncExec(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.
 | 
			
		||||
| 
						 | 
				
			
			@ -92,7 +94,7 @@ type sessionPing struct {
 | 
			
		|||
 | 
			
		||||
// 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.
 | 
			
		||||
func (s *sessionInfo) update(p *sessionPing) bool {
 | 
			
		||||
func (s *sessionInfo) _update(p *sessionPing) bool {
 | 
			
		||||
	if !(p.Tstamp > s.tstamp) {
 | 
			
		||||
		// To protect against replay attacks
 | 
			
		||||
		return false
 | 
			
		||||
| 
						 | 
				
			
			@ -214,6 +216,7 @@ func (ss *sessions) createSession(theirPermKey *crypto.BoxPubKey) *sessionInfo {
 | 
			
		|||
	sinfo.core = ss.core
 | 
			
		||||
	sinfo.reconfigure = make(chan chan error, 1)
 | 
			
		||||
	sinfo.theirPermPub = *theirPermKey
 | 
			
		||||
	sinfo.sharedPermKey = *ss.getSharedKey(&ss.core.boxPriv, &sinfo.theirPermPub)
 | 
			
		||||
	pub, priv := crypto.NewBoxKeys()
 | 
			
		||||
	sinfo.mySesPub = *pub
 | 
			
		||||
	sinfo.mySesPriv = *priv
 | 
			
		||||
| 
						 | 
				
			
			@ -257,7 +260,9 @@ func (ss *sessions) createSession(theirPermKey *crypto.BoxPubKey) *sessionInfo {
 | 
			
		|||
	go func() {
 | 
			
		||||
		// Run cleanup when the session is canceled
 | 
			
		||||
		<-sinfo.cancel.Finished()
 | 
			
		||||
		sinfo.core.router.doAdmin(sinfo.close)
 | 
			
		||||
		sinfo.core.router.doAdmin(func() {
 | 
			
		||||
			sinfo.core.sessions.removeSession(&sinfo)
 | 
			
		||||
		})
 | 
			
		||||
	}()
 | 
			
		||||
	go sinfo.startWorkers()
 | 
			
		||||
	return &sinfo
 | 
			
		||||
| 
						 | 
				
			
			@ -292,7 +297,7 @@ func (ss *sessions) cleanup() {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// 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 {
 | 
			
		||||
		delete(sinfo.core.sessions.sinfos, sinfo.myHandle)
 | 
			
		||||
		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.
 | 
			
		||||
func (ss *sessions) getPing(sinfo *sessionInfo) sessionPing {
 | 
			
		||||
	loc := ss.core.switchTable.getLocator()
 | 
			
		||||
func (sinfo *sessionInfo) _getPing() sessionPing {
 | 
			
		||||
	loc := sinfo.core.switchTable.getLocator()
 | 
			
		||||
	coords := loc.getCoords()
 | 
			
		||||
	ref := sessionPing{
 | 
			
		||||
		SendPermPub: ss.core.boxPub,
 | 
			
		||||
	ping := sessionPing{
 | 
			
		||||
		SendPermPub: sinfo.core.boxPub,
 | 
			
		||||
		Handle:      sinfo.myHandle,
 | 
			
		||||
		SendSesPub:  sinfo.mySesPub,
 | 
			
		||||
		Tstamp:      time.Now().Unix(),
 | 
			
		||||
| 
						 | 
				
			
			@ -312,7 +317,7 @@ func (ss *sessions) getPing(sinfo *sessionInfo) sessionPing {
 | 
			
		|||
		MTU:         sinfo.myMTU,
 | 
			
		||||
	}
 | 
			
		||||
	sinfo.myNonce.Increment()
 | 
			
		||||
	return ref
 | 
			
		||||
	return ping
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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.
 | 
			
		||||
func (ss *sessions) ping(sinfo *sessionInfo) {
 | 
			
		||||
	ss.sendPingPong(sinfo, false)
 | 
			
		||||
func (sinfo *sessionInfo) ping(from phony.IActor) {
 | 
			
		||||
	sinfo.EnqueueFrom(from, func() {
 | 
			
		||||
		sinfo._sendPingPong(false)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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.
 | 
			
		||||
func (ss *sessions) sendPingPong(sinfo *sessionInfo, isPong bool) {
 | 
			
		||||
	ping := ss.getPing(sinfo)
 | 
			
		||||
func (sinfo *sessionInfo) _sendPingPong(isPong bool) {
 | 
			
		||||
	ping := sinfo._getPing()
 | 
			
		||||
	ping.IsPong = isPong
 | 
			
		||||
	bs := ping.encode()
 | 
			
		||||
	shared := ss.getSharedKey(&ss.core.boxPriv, &sinfo.theirPermPub)
 | 
			
		||||
	payload, nonce := crypto.BoxSeal(shared, bs, nil)
 | 
			
		||||
	payload, nonce := crypto.BoxSeal(&sinfo.sharedPermKey, bs, nil)
 | 
			
		||||
	p := wire_protoTrafficPacket{
 | 
			
		||||
		Coords:  sinfo.coords,
 | 
			
		||||
		ToKey:   sinfo.theirPermPub,
 | 
			
		||||
		FromKey: ss.core.boxPub,
 | 
			
		||||
		FromKey: sinfo.core.boxPub,
 | 
			
		||||
		Nonce:   *nonce,
 | 
			
		||||
		Payload: payload,
 | 
			
		||||
	}
 | 
			
		||||
	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) {
 | 
			
		||||
		sinfo.pingTime = time.Now()
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -371,9 +378,9 @@ func (ss *sessions) handlePing(ping *sessionPing) {
 | 
			
		|||
	// Get the corresponding session (or create a new session)
 | 
			
		||||
	sinfo, isIn := ss.getByTheirPerm(&ping.SendPermPub)
 | 
			
		||||
	switch {
 | 
			
		||||
	case ping.IsPong: // This is a response, not an initial ping, so ignore it.
 | 
			
		||||
	case isIn: // Session already exists
 | 
			
		||||
	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:
 | 
			
		||||
		ss.listenerMutex.Lock()
 | 
			
		||||
		if ss.listener != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -393,13 +400,13 @@ func (ss *sessions) handlePing(ping *sessionPing) {
 | 
			
		|||
		ss.listenerMutex.Unlock()
 | 
			
		||||
	}
 | 
			
		||||
	if sinfo != nil {
 | 
			
		||||
		sinfo.doFunc(func() {
 | 
			
		||||
		sinfo.EnqueueFrom(&ss.core.router, func() {
 | 
			
		||||
			// Update the session
 | 
			
		||||
			if !sinfo.update(ping) { /*panic("Should not happen in testing")*/
 | 
			
		||||
			if !sinfo._update(ping) { /*panic("Should not happen in testing")*/
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			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.
 | 
			
		||||
// 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.
 | 
			
		||||
func (sinfo *sessionInfo) getMTU() uint16 {
 | 
			
		||||
func (sinfo *sessionInfo) _getMTU() uint16 {
 | 
			
		||||
	if sinfo.theirMTU == 0 || sinfo.myMTU == 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.
 | 
			
		||||
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
 | 
			
		||||
	if theirNonce.Minus(&sinfo.theirNonce) > 0 {
 | 
			
		||||
		// 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
 | 
			
		||||
func (sinfo *sessionInfo) updateNonce(theirNonce *crypto.BoxNonce) {
 | 
			
		||||
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 {
 | 
			
		||||
| 
						 | 
				
			
			@ -459,9 +466,9 @@ func (sinfo *sessionInfo) updateNonce(theirNonce *crypto.BoxNonce) {
 | 
			
		|||
 | 
			
		||||
// 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.
 | 
			
		||||
func (ss *sessions) reset() {
 | 
			
		||||
func (ss *sessions) reset(from phony.IActor) {
 | 
			
		||||
	for _, sinfo := range ss.sinfos {
 | 
			
		||||
		sinfo.doFunc(func() {
 | 
			
		||||
		sinfo.EnqueueFrom(from, func() {
 | 
			
		||||
			sinfo.reset = true
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -492,7 +499,7 @@ func (sinfo *sessionInfo) recvWorker() {
 | 
			
		|||
		var err error
 | 
			
		||||
		var k crypto.BoxSharedKey
 | 
			
		||||
		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}
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -514,12 +521,12 @@ func (sinfo *sessionInfo) recvWorker() {
 | 
			
		|||
					return
 | 
			
		||||
				}
 | 
			
		||||
				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
 | 
			
		||||
						err = ConnError{errors.New("session updated during crypto operation"), false, true, false, 0}
 | 
			
		||||
						return
 | 
			
		||||
					}
 | 
			
		||||
					sinfo.updateNonce(&p.Nonce)
 | 
			
		||||
					sinfo._updateNonce(&p.Nonce)
 | 
			
		||||
					sinfo.time = time.Now()
 | 
			
		||||
					sinfo.bytesRecvd += uint64(len(bs))
 | 
			
		||||
				}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue