diff --git a/src/yggdrasil/router.go b/src/yggdrasil/router.go index d387346..f89b26f 100644 --- a/src/yggdrasil/router.go +++ b/src/yggdrasil/router.go @@ -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. diff --git a/src/yggdrasil/session.go b/src/yggdrasil/session.go index 4a06bee..181a199 100644 --- a/src/yggdrasil/session.go +++ b/src/yggdrasil/session.go @@ -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 diff --git a/src/yggdrasil/switch.go b/src/yggdrasil/switch.go index c291d82..a245560 100644 --- a/src/yggdrasil/switch.go +++ b/src/yggdrasil/switch.go @@ -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...