mirror of
				https://github.com/yggdrasil-network/yggdrasil-go.git
				synced 2025-11-04 03:05:07 +03:00 
			
		
		
		
	safer pathfinding behavior
This commit is contained in:
		
							parent
							
								
									994c26e5f7
								
							
						
					
					
						commit
						e19e938f64
					
				
					 3 changed files with 59 additions and 45 deletions
				
			
		| 
						 | 
				
			
			@ -196,9 +196,9 @@ func (r *router) _handleProto(packet []byte) {
 | 
			
		|||
	}
 | 
			
		||||
	switch bsType {
 | 
			
		||||
	case wire_SessionPing:
 | 
			
		||||
		r._handlePing(bs, &p.FromKey)
 | 
			
		||||
		r._handlePing(bs, &p.FromKey, p.RPath)
 | 
			
		||||
	case wire_SessionPong:
 | 
			
		||||
		r._handlePong(bs, &p.FromKey)
 | 
			
		||||
		r._handlePong(bs, &p.FromKey, p.RPath)
 | 
			
		||||
	case wire_NodeInfoRequest:
 | 
			
		||||
		fallthrough
 | 
			
		||||
	case wire_NodeInfoResponse:
 | 
			
		||||
| 
						 | 
				
			
			@ -212,18 +212,18 @@ func (r *router) _handleProto(packet []byte) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// Decodes session pings from wire format and passes them to sessions.handlePing where they either create or update a session.
 | 
			
		||||
func (r *router) _handlePing(bs []byte, fromKey *crypto.BoxPubKey) {
 | 
			
		||||
func (r *router) _handlePing(bs []byte, fromKey *crypto.BoxPubKey, rpath []byte) {
 | 
			
		||||
	ping := sessionPing{}
 | 
			
		||||
	if !ping.decode(bs) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	ping.SendPermPub = *fromKey
 | 
			
		||||
	r.sessions.handlePing(&ping)
 | 
			
		||||
	r.sessions.handlePing(&ping, rpath)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Handles session pongs (which are really pings with an extra flag to prevent acknowledgement).
 | 
			
		||||
func (r *router) _handlePong(bs []byte, fromKey *crypto.BoxPubKey) {
 | 
			
		||||
	r._handlePing(bs, fromKey)
 | 
			
		||||
func (r *router) _handlePong(bs []byte, fromKey *crypto.BoxPubKey, rpath []byte) {
 | 
			
		||||
	r._handlePing(bs, fromKey, rpath)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Decodes dht requests and passes them to dht.handleReq to trigger a lookup/response.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -51,7 +51,6 @@ type sessionInfo struct {
 | 
			
		|||
	callbacks     []chan func()       // Finished work from crypto workers
 | 
			
		||||
	table         *lookupTable        // table.self is a locator where we get our coords
 | 
			
		||||
	path          []byte              // Path from self to destination
 | 
			
		||||
	rpath         []byte              // Path from destination to self
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Represents a session ping/pong packet, and includes information like public keys, a session handle, coords, a timestamp to prevent replays, and the tun/tap MTU.
 | 
			
		||||
| 
						 | 
				
			
			@ -67,41 +66,46 @@ 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 {
 | 
			
		||||
	if !(p.Tstamp > s.tstamp) {
 | 
			
		||||
func (sinfo *sessionInfo) _update(p *sessionPing, rpath []byte) bool {
 | 
			
		||||
	if !(p.Tstamp > sinfo.tstamp) {
 | 
			
		||||
		// To protect against replay attacks
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	if p.SendPermPub != s.theirPermPub {
 | 
			
		||||
	if p.SendPermPub != sinfo.theirPermPub {
 | 
			
		||||
		// Should only happen if two sessions got the same handle
 | 
			
		||||
		// That shouldn't be allowed anyway, but if it happens then let one time out
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	if p.SendSesPub != s.theirSesPub {
 | 
			
		||||
		s.theirSesPub = p.SendSesPub
 | 
			
		||||
		s.theirHandle = p.Handle
 | 
			
		||||
		s.sharedSesKey = *crypto.GetSharedKey(&s.mySesPriv, &s.theirSesPub)
 | 
			
		||||
		s.theirNonce = crypto.BoxNonce{}
 | 
			
		||||
	if p.SendSesPub != sinfo.theirSesPub {
 | 
			
		||||
		sinfo.path = nil
 | 
			
		||||
		sinfo.theirSesPub = p.SendSesPub
 | 
			
		||||
		sinfo.theirHandle = p.Handle
 | 
			
		||||
		sinfo.sharedSesKey = *crypto.GetSharedKey(&sinfo.mySesPriv, &sinfo.theirSesPub)
 | 
			
		||||
		sinfo.theirNonce = crypto.BoxNonce{}
 | 
			
		||||
	}
 | 
			
		||||
	if p.MTU >= 1280 || p.MTU == 0 {
 | 
			
		||||
		s.theirMTU = p.MTU
 | 
			
		||||
		if s.conn != nil {
 | 
			
		||||
			s.conn.setMTU(s, s._getMTU())
 | 
			
		||||
		sinfo.theirMTU = p.MTU
 | 
			
		||||
		if sinfo.conn != nil {
 | 
			
		||||
			sinfo.conn.setMTU(sinfo, sinfo._getMTU())
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if !bytes.Equal(s.coords, p.Coords) {
 | 
			
		||||
	if !bytes.Equal(sinfo.coords, p.Coords) {
 | 
			
		||||
		// allocate enough space for additional coords
 | 
			
		||||
		s.coords = append(make([]byte, 0, len(p.Coords)+11), p.Coords...)
 | 
			
		||||
		sinfo.coords = append(make([]byte, 0, len(p.Coords)+11), p.Coords...)
 | 
			
		||||
	}
 | 
			
		||||
	s.time = time.Now()
 | 
			
		||||
	s.tstamp = p.Tstamp
 | 
			
		||||
	s.reset = false
 | 
			
		||||
	sinfo.time = time.Now()
 | 
			
		||||
	sinfo.tstamp = p.Tstamp
 | 
			
		||||
	if p.IsPong && sinfo.path == nil {
 | 
			
		||||
		path := switch_reverseCoordBytes(rpath)
 | 
			
		||||
		sinfo.path = append(sinfo.path[:0], path...)
 | 
			
		||||
	}
 | 
			
		||||
	sinfo.reset = false
 | 
			
		||||
	defer func() { recover() }() // Recover if the below panics
 | 
			
		||||
	select {
 | 
			
		||||
	case <-s.init:
 | 
			
		||||
	case <-sinfo.init:
 | 
			
		||||
	default:
 | 
			
		||||
		// Unblock anything waiting for the session to initialize
 | 
			
		||||
		close(s.init)
 | 
			
		||||
		close(sinfo.init)
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -306,13 +310,13 @@ func (ss *sessions) getSharedKey(myPriv *crypto.BoxPrivKey,
 | 
			
		|||
// Sends a session ping by calling sendPingPong in ping mode.
 | 
			
		||||
func (sinfo *sessionInfo) ping(from phony.Actor) {
 | 
			
		||||
	sinfo.Act(from, func() {
 | 
			
		||||
		sinfo._sendPingPong(false)
 | 
			
		||||
		sinfo._sendPingPong(false, nil)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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 (sinfo *sessionInfo) _sendPingPong(isPong bool) {
 | 
			
		||||
func (sinfo *sessionInfo) _sendPingPong(isPong bool, path []byte) {
 | 
			
		||||
	ping := sinfo._getPing()
 | 
			
		||||
	ping.IsPong = isPong
 | 
			
		||||
	bs := ping.encode()
 | 
			
		||||
| 
						 | 
				
			
			@ -324,16 +328,21 @@ func (sinfo *sessionInfo) _sendPingPong(isPong bool) {
 | 
			
		|||
		Nonce:   *nonce,
 | 
			
		||||
		Payload: payload,
 | 
			
		||||
	}
 | 
			
		||||
	if path != nil {
 | 
			
		||||
		p.Coords = append([]byte{0}, path...)
 | 
			
		||||
		p.Offset += 1
 | 
			
		||||
	}
 | 
			
		||||
	packet := p.encode()
 | 
			
		||||
	// TODO rewrite the below if/when the peer struct becomes an actor, to not go through the router first
 | 
			
		||||
	sinfo.sessions.router.Act(sinfo, func() { sinfo.sessions.router.out(packet) })
 | 
			
		||||
	if sinfo.pingTime.Before(sinfo.time) {
 | 
			
		||||
	if !isPong && sinfo.pingTime.Before(sinfo.time) {
 | 
			
		||||
		sinfo.pingTime = time.Now()
 | 
			
		||||
	}
 | 
			
		||||
	// Sending a ping may happen when we don't know if our path info is good anymore...
 | 
			
		||||
	// Reset paths just to be safe...
 | 
			
		||||
	sinfo.path = nil
 | 
			
		||||
	sinfo.rpath = nil
 | 
			
		||||
	if !isPong {
 | 
			
		||||
		// Sending a ping may happen when we don't know if our path info is good anymore...
 | 
			
		||||
		// Reset paths just to be safe...
 | 
			
		||||
		sinfo.path = nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (sinfo *sessionInfo) setConn(from phony.Actor, conn *Conn) {
 | 
			
		||||
| 
						 | 
				
			
			@ -345,7 +354,7 @@ func (sinfo *sessionInfo) setConn(from phony.Actor, conn *Conn) {
 | 
			
		|||
 | 
			
		||||
// Handles a session ping, creating a session if needed and calling update, then possibly responding with a pong if the ping was in ping mode and the update was successful.
 | 
			
		||||
// If the session has a packet cached (common when first setting up a session), it will be sent.
 | 
			
		||||
func (ss *sessions) handlePing(ping *sessionPing) {
 | 
			
		||||
func (ss *sessions) handlePing(ping *sessionPing, rpath []byte) {
 | 
			
		||||
	// Get the corresponding session (or create a new session)
 | 
			
		||||
	sinfo, isIn := ss.getByTheirPerm(&ping.SendPermPub)
 | 
			
		||||
	switch {
 | 
			
		||||
| 
						 | 
				
			
			@ -374,11 +383,11 @@ func (ss *sessions) handlePing(ping *sessionPing) {
 | 
			
		|||
	if sinfo != nil {
 | 
			
		||||
		sinfo.Act(ss.router, func() {
 | 
			
		||||
			// Update the session
 | 
			
		||||
			if !sinfo._update(ping) { /*panic("Should not happen in testing")*/
 | 
			
		||||
			if !sinfo._update(ping, rpath) { /*panic("Should not happen in testing")*/
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			if !ping.IsPong {
 | 
			
		||||
				sinfo._sendPingPong(true)
 | 
			
		||||
				sinfo._sendPingPong(true, switch_reverseCoordBytes(rpath))
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -474,16 +483,9 @@ func (sinfo *sessionInfo) _recvPacket(p *wire_trafficPacket) {
 | 
			
		|||
			sinfo._updateNonce(&p.Nonce)
 | 
			
		||||
			sinfo.bytesRecvd += uint64(len(bs))
 | 
			
		||||
			sinfo.conn.recvMsg(sinfo, bs)
 | 
			
		||||
			a := switch_getPorts(p.RPath)
 | 
			
		||||
			for i := len(a)/2 - 1; i >= 0; i-- {
 | 
			
		||||
				opp := len(a) - 1 - i
 | 
			
		||||
				a[i], a[opp] = a[opp], a[i]
 | 
			
		||||
			if sinfo.path == nil {
 | 
			
		||||
				sinfo._sendPingPong(false, nil)
 | 
			
		||||
			}
 | 
			
		||||
			sinfo.path = sinfo.path[:0]
 | 
			
		||||
			for _, sPort := range a {
 | 
			
		||||
				sinfo.path = wire_put_uint64(uint64(sPort), sinfo.path)
 | 
			
		||||
			}
 | 
			
		||||
			//sinfo.rpath = append(sinfo.rpath[:0], p.Path...)
 | 
			
		||||
		}
 | 
			
		||||
		ch <- callback
 | 
			
		||||
		sinfo.checkCallbacks()
 | 
			
		||||
| 
						 | 
				
			
			@ -516,7 +518,6 @@ func (sinfo *sessionInfo) _send(msg FlowKeyMessage) {
 | 
			
		|||
		Coords: coords,
 | 
			
		||||
		Handle: sinfo.theirHandle,
 | 
			
		||||
		Nonce:  sinfo.myNonce,
 | 
			
		||||
		RPath:  sinfo.rpath,
 | 
			
		||||
	}
 | 
			
		||||
	sinfo.myNonce.Increment()
 | 
			
		||||
	k := sinfo.sharedSesKey
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -655,6 +655,19 @@ func switch_getPorts(coords []byte) []switchPort {
 | 
			
		|||
	return ports
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func switch_reverseCoordBytes(coords []byte) []byte {
 | 
			
		||||
	a := switch_getPorts(coords)
 | 
			
		||||
	for i := len(a)/2 - 1; i >= 0; i-- {
 | 
			
		||||
		opp := len(a) - 1 - i
 | 
			
		||||
		a[i], a[opp] = a[opp], a[i]
 | 
			
		||||
	}
 | 
			
		||||
	var reversed []byte
 | 
			
		||||
	for _, sPort := range a {
 | 
			
		||||
		reversed = wire_put_uint64(uint64(sPort), reversed)
 | 
			
		||||
	}
 | 
			
		||||
	return reversed
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *lookupTable) isDescendant(ports []switchPort) bool {
 | 
			
		||||
	// Note that this returns true for anyone in the subtree that starts at us
 | 
			
		||||
	// That includes ourself, so we are our own descendant by this logic...
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue