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 { switch bsType {
case wire_SessionPing: case wire_SessionPing:
r._handlePing(bs, &p.FromKey) r._handlePing(bs, &p.FromKey, p.RPath)
case wire_SessionPong: case wire_SessionPong:
r._handlePong(bs, &p.FromKey) r._handlePong(bs, &p.FromKey, p.RPath)
case wire_NodeInfoRequest: case wire_NodeInfoRequest:
fallthrough fallthrough
case wire_NodeInfoResponse: 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. // 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{} ping := sessionPing{}
if !ping.decode(bs) { if !ping.decode(bs) {
return return
} }
ping.SendPermPub = *fromKey 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). // Handles session pongs (which are really pings with an extra flag to prevent acknowledgement).
func (r *router) _handlePong(bs []byte, fromKey *crypto.BoxPubKey) { func (r *router) _handlePong(bs []byte, fromKey *crypto.BoxPubKey, rpath []byte) {
r._handlePing(bs, fromKey) r._handlePing(bs, fromKey, rpath)
} }
// Decodes dht requests and passes them to dht.handleReq to trigger a lookup/response. // 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 callbacks []chan func() // Finished work from crypto workers
table *lookupTable // table.self is a locator where we get our coords table *lookupTable // table.self is a locator where we get our coords
path []byte // Path from self to destination 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. // 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. // 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 (sinfo *sessionInfo) _update(p *sessionPing, rpath []byte) bool {
if !(p.Tstamp > s.tstamp) { if !(p.Tstamp > sinfo.tstamp) {
// To protect against replay attacks // To protect against replay attacks
return false return false
} }
if p.SendPermPub != s.theirPermPub { if p.SendPermPub != sinfo.theirPermPub {
// Should only happen if two sessions got the same handle // 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 // That shouldn't be allowed anyway, but if it happens then let one time out
return false return false
} }
if p.SendSesPub != s.theirSesPub { if p.SendSesPub != sinfo.theirSesPub {
s.theirSesPub = p.SendSesPub sinfo.path = nil
s.theirHandle = p.Handle sinfo.theirSesPub = p.SendSesPub
s.sharedSesKey = *crypto.GetSharedKey(&s.mySesPriv, &s.theirSesPub) sinfo.theirHandle = p.Handle
s.theirNonce = crypto.BoxNonce{} sinfo.sharedSesKey = *crypto.GetSharedKey(&sinfo.mySesPriv, &sinfo.theirSesPub)
sinfo.theirNonce = crypto.BoxNonce{}
} }
if p.MTU >= 1280 || p.MTU == 0 { if p.MTU >= 1280 || p.MTU == 0 {
s.theirMTU = p.MTU sinfo.theirMTU = p.MTU
if s.conn != nil { if sinfo.conn != nil {
s.conn.setMTU(s, s._getMTU()) 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 // 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() sinfo.time = time.Now()
s.tstamp = p.Tstamp sinfo.tstamp = p.Tstamp
s.reset = false 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 defer func() { recover() }() // Recover if the below panics
select { select {
case <-s.init: case <-sinfo.init:
default: default:
// Unblock anything waiting for the session to initialize // Unblock anything waiting for the session to initialize
close(s.init) close(sinfo.init)
} }
return true return true
} }
@ -306,13 +310,13 @@ 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 (sinfo *sessionInfo) ping(from phony.Actor) { func (sinfo *sessionInfo) ping(from phony.Actor) {
sinfo.Act(from, func() { 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. // 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 (sinfo *sessionInfo) _sendPingPong(isPong bool) { func (sinfo *sessionInfo) _sendPingPong(isPong bool, path []byte) {
ping := sinfo._getPing() ping := sinfo._getPing()
ping.IsPong = isPong ping.IsPong = isPong
bs := ping.encode() bs := ping.encode()
@ -324,16 +328,21 @@ func (sinfo *sessionInfo) _sendPingPong(isPong bool) {
Nonce: *nonce, Nonce: *nonce,
Payload: payload, Payload: payload,
} }
if path != nil {
p.Coords = append([]byte{0}, path...)
p.Offset += 1
}
packet := p.encode() packet := p.encode()
// TODO rewrite the below if/when the peer struct becomes an actor, to not go through the router first // 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) }) 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() sinfo.pingTime = time.Now()
} }
if !isPong {
// Sending a ping may happen when we don't know if our path info is good anymore... // Sending a ping may happen when we don't know if our path info is good anymore...
// Reset paths just to be safe... // Reset paths just to be safe...
sinfo.path = nil sinfo.path = nil
sinfo.rpath = nil }
} }
func (sinfo *sessionInfo) setConn(from phony.Actor, conn *Conn) { 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. // 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. // 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) // Get the corresponding session (or create a new session)
sinfo, isIn := ss.getByTheirPerm(&ping.SendPermPub) sinfo, isIn := ss.getByTheirPerm(&ping.SendPermPub)
switch { switch {
@ -374,11 +383,11 @@ func (ss *sessions) handlePing(ping *sessionPing) {
if sinfo != nil { if sinfo != nil {
sinfo.Act(ss.router, func() { sinfo.Act(ss.router, func() {
// Update the session // 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 return
} }
if !ping.IsPong { 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._updateNonce(&p.Nonce)
sinfo.bytesRecvd += uint64(len(bs)) sinfo.bytesRecvd += uint64(len(bs))
sinfo.conn.recvMsg(sinfo, bs) sinfo.conn.recvMsg(sinfo, bs)
a := switch_getPorts(p.RPath) if sinfo.path == nil {
for i := len(a)/2 - 1; i >= 0; i-- { sinfo._sendPingPong(false, nil)
opp := len(a) - 1 - i
a[i], a[opp] = a[opp], a[i]
} }
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 ch <- callback
sinfo.checkCallbacks() sinfo.checkCallbacks()
@ -516,7 +518,6 @@ func (sinfo *sessionInfo) _send(msg FlowKeyMessage) {
Coords: coords, Coords: coords,
Handle: sinfo.theirHandle, Handle: sinfo.theirHandle,
Nonce: sinfo.myNonce, Nonce: sinfo.myNonce,
RPath: sinfo.rpath,
} }
sinfo.myNonce.Increment() sinfo.myNonce.Increment()
k := sinfo.sharedSesKey k := sinfo.sharedSesKey

View File

@ -655,6 +655,19 @@ func switch_getPorts(coords []byte) []switchPort {
return ports 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 { func (t *lookupTable) isDescendant(ports []switchPort) bool {
// Note that this returns true for anyone in the subtree that starts at us // 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... // That includes ourself, so we are our own descendant by this logic...