safer pathfinding behavior

This commit is contained in:
Arceliar 2020-11-07 15:19:09 -06:00
parent 994c26e5f7
commit e19e938f64
3 changed files with 59 additions and 45 deletions

View File

@ -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.

View File

@ -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

View File

@ -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...