2017-12-28 22:16:20 -06:00
package yggdrasil
// This is the session manager
// It's responsible for keeping track of open sessions to other nodes
// The session information consists of crypto keys and coords
2018-07-30 13:44:46 +00:00
import (
"bytes"
2018-10-07 17:13:41 +01:00
"encoding/hex"
2019-04-18 23:38:23 +01:00
"sync"
2019-04-20 11:53:38 +01:00
"sync/atomic"
2018-07-30 13:44:46 +00:00
"time"
2018-12-14 20:49:18 -06:00
"github.com/yggdrasil-network/yggdrasil-go/src/address"
"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
2018-07-30 13:44:46 +00:00
)
2017-12-28 22:16:20 -06:00
2018-06-10 18:03:28 -05:00
// 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.
2017-12-28 22:16:20 -06:00
type sessionInfo struct {
2019-04-21 20:38:14 -05:00
core * Core //
reconfigure chan chan error //
theirAddr address . Address //
theirSubnet address . Subnet //
theirPermPub crypto . BoxPubKey //
theirSesPub crypto . BoxPubKey //
mySesPub crypto . BoxPubKey //
mySesPriv crypto . BoxPrivKey //
sharedSesKey crypto . BoxSharedKey // derived from session keys
theirHandle crypto . Handle //
myHandle crypto . Handle //
theirNonce crypto . BoxNonce //
theirNonceMask uint64 //
myNonce crypto . BoxNonce //
theirMTU uint16 //
myMTU uint16 //
wasMTUFixed bool // Was the MTU fixed by a receive error?
time time . Time // Time we last received a packet
mtuTime time . Time // time myMTU was last changed
pingTime time . Time // time the first ping was sent since the last received packet
pingSend time . Time // time the last ping was sent
coords [ ] byte // coords of destination
packet [ ] byte // a buffered packet, sent immediately on ping/pong
init bool // Reset if coords change
tstamp int64 // ATOMIC - tstamp from their last session ping, replay attack mitigation
bytesSent uint64 // Bytes of real traffic sent in this session
bytesRecvd uint64 // Bytes of real traffic received in this session
worker chan func ( ) // Channel to send work to the session worker
recv chan * wire_trafficPacket // Received packets go here, picked up by the associated Conn
}
func ( sinfo * sessionInfo ) doWorker ( f func ( ) ) {
done := make ( chan struct { } )
sinfo . worker <- func ( ) {
f ( )
close ( done )
}
<- done
}
func ( sinfo * sessionInfo ) workerMain ( ) {
for f := range sinfo . worker {
f ( )
}
2017-12-28 22:16:20 -06:00
}
2018-06-10 18:03:28 -05:00
// 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.
2017-12-28 22:16:20 -06:00
type sessionPing struct {
2018-12-14 20:49:18 -06:00
SendPermPub crypto . BoxPubKey // Sender's permanent key
Handle crypto . Handle // Random number to ID session
SendSesPub crypto . BoxPubKey // Session key to use
2019-04-21 11:50:41 +01:00
Coords [ ] byte //
Tstamp int64 // unix time, but the only real requirement is that it increases
IsPong bool //
MTU uint16 //
2017-12-28 22:16:20 -06:00
}
2018-06-10 18:03:28 -05:00
// 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.
2017-12-28 22:16:20 -06:00
func ( s * sessionInfo ) update ( p * sessionPing ) bool {
2019-04-20 11:53:38 +01:00
if ! ( p . Tstamp > atomic . LoadInt64 ( & s . tstamp ) ) {
2018-01-26 17:30:51 -06:00
// To protect against replay attacks
2018-01-04 22:37:51 +00:00
return false
}
2018-06-02 22:19:42 +01:00
if p . SendPermPub != s . theirPermPub {
2018-01-26 17:30:51 -06:00
// 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
2018-01-04 22:37:51 +00:00
return false
2018-01-26 17:30:51 -06:00
}
2018-06-02 22:19:42 +01:00
if p . SendSesPub != s . theirSesPub {
s . theirSesPub = p . SendSesPub
s . theirHandle = p . Handle
2018-12-14 20:49:18 -06:00
s . sharedSesKey = * crypto . GetSharedKey ( & s . mySesPriv , & s . theirSesPub )
s . theirNonce = crypto . BoxNonce { }
2019-04-19 20:10:41 +01:00
s . theirNonceMask = 0
2018-01-04 22:37:51 +00:00
}
2018-06-02 22:19:42 +01:00
if p . MTU >= 1280 || p . MTU == 0 {
s . theirMTU = p . MTU
2018-02-11 23:09:05 +00:00
}
2018-07-30 13:44:46 +00:00
if ! bytes . Equal ( s . coords , p . Coords ) {
// allocate enough space for additional coords
s . coords = append ( make ( [ ] byte , 0 , len ( p . Coords ) + 11 ) , p . Coords ... )
}
2019-04-21 20:38:14 -05:00
s . time = time . Now ( )
s . tstamp = p . Tstamp
s . init = true
2018-01-04 22:37:51 +00:00
return true
2017-12-28 22:16:20 -06:00
}
2018-06-10 18:03:28 -05:00
// Returns true if the session has been idle for longer than the allowed timeout.
2017-12-28 22:16:20 -06:00
func ( s * sessionInfo ) timedout ( ) bool {
2019-04-21 20:38:14 -05:00
var timedout bool
s . doWorker ( func ( ) {
timedout = time . Since ( s . time ) > time . Minute
} )
return timedout
2017-12-28 22:16:20 -06:00
}
2018-06-10 18:03:28 -05:00
// Struct of all active sessions.
// Sessions are indexed by handle.
// Additionally, stores maps of address/subnet onto keys, and keys onto handles.
2017-12-28 22:16:20 -06:00
type sessions struct {
2019-04-19 22:57:52 +01:00
core * Core
listener * Listener
listenerMutex sync . Mutex
reconfigure chan chan error
lastCleanup time . Time
permShared map [ crypto . BoxPubKey ] * crypto . BoxSharedKey // Maps known permanent keys to their shared key, used by DHT a lot
sinfos map [ crypto . Handle ] * sessionInfo // Maps (secret) handle onto session info
conns map [ crypto . Handle ] * Conn // Maps (secret) handle onto connections
byMySes map [ crypto . BoxPubKey ] * crypto . Handle // Maps mySesPub onto handle
byTheirPerm map [ crypto . BoxPubKey ] * crypto . Handle // Maps theirPermPub onto handle
addrToPerm map [ address . Address ] * crypto . BoxPubKey
subnetToPerm map [ address . Subnet ] * crypto . BoxPubKey
2017-12-28 22:16:20 -06:00
}
2018-06-10 18:03:28 -05:00
// Initializes the session struct.
2017-12-28 22:16:20 -06:00
func ( ss * sessions ) init ( core * Core ) {
2018-01-04 22:37:51 +00:00
ss . core = core
2018-12-30 12:04:42 +00:00
ss . reconfigure = make ( chan chan error , 1 )
2018-12-29 18:51:51 +00:00
go func ( ) {
for {
2019-01-15 08:51:19 +00:00
e := <- ss . reconfigure
responses := make ( map [ crypto . Handle ] chan error )
for index , session := range ss . sinfos {
responses [ index ] = make ( chan error )
session . reconfigure <- responses [ index ]
}
for _ , response := range responses {
if err := <- response ; err != nil {
e <- err
continue
2018-12-29 18:51:51 +00:00
}
}
2019-01-15 08:51:19 +00:00
e <- nil
2018-12-29 18:51:51 +00:00
}
} ( )
2018-12-14 20:49:18 -06:00
ss . permShared = make ( map [ crypto . BoxPubKey ] * crypto . BoxSharedKey )
ss . sinfos = make ( map [ crypto . Handle ] * sessionInfo )
ss . byMySes = make ( map [ crypto . BoxPubKey ] * crypto . Handle )
ss . byTheirPerm = make ( map [ crypto . BoxPubKey ] * crypto . Handle )
ss . addrToPerm = make ( map [ address . Address ] * crypto . BoxPubKey )
ss . subnetToPerm = make ( map [ address . Subnet ] * crypto . BoxPubKey )
2018-06-21 20:31:30 -05:00
ss . lastCleanup = time . Now ( )
2017-12-28 22:16:20 -06:00
}
2019-01-16 20:38:51 +00:00
// Determines whether the session firewall is enabled.
func ( ss * sessions ) isSessionFirewallEnabled ( ) bool {
2019-03-28 00:30:25 +00:00
ss . core . config . Mutex . RLock ( )
defer ss . core . config . Mutex . RUnlock ( )
2018-10-07 17:13:41 +01:00
2019-03-28 00:30:25 +00:00
return ss . core . config . Current . SessionFirewall . Enable
2018-10-07 17:13:41 +01:00
}
// Determines whether the session with a given publickey is allowed based on
// session firewall rules.
2018-12-14 20:49:18 -06:00
func ( ss * sessions ) isSessionAllowed ( pubkey * crypto . BoxPubKey , initiator bool ) bool {
2019-03-28 00:30:25 +00:00
ss . core . config . Mutex . RLock ( )
defer ss . core . config . Mutex . RUnlock ( )
2019-01-14 18:24:35 +00:00
2018-10-07 17:13:41 +01:00
// Allow by default if the session firewall is disabled
2019-01-16 20:38:51 +00:00
if ! ss . isSessionFirewallEnabled ( ) {
2018-10-07 17:13:41 +01:00
return true
}
// Prepare for checking whitelist/blacklist
2018-12-14 20:49:18 -06:00
var box crypto . BoxPubKey
2018-10-07 17:13:41 +01:00
// Reject blacklisted nodes
2019-03-28 00:30:25 +00:00
for _ , b := range ss . core . config . Current . SessionFirewall . BlacklistEncryptionPublicKeys {
2018-10-07 17:13:41 +01:00
key , err := hex . DecodeString ( b )
if err == nil {
2018-12-14 20:49:18 -06:00
copy ( box [ : crypto . BoxPubKeyLen ] , key )
2018-10-07 17:13:41 +01:00
if box == * pubkey {
return false
}
}
}
// Allow whitelisted nodes
2019-03-28 00:30:25 +00:00
for _ , b := range ss . core . config . Current . SessionFirewall . WhitelistEncryptionPublicKeys {
2018-10-07 17:13:41 +01:00
key , err := hex . DecodeString ( b )
if err == nil {
2018-12-14 20:49:18 -06:00
copy ( box [ : crypto . BoxPubKeyLen ] , key )
2018-10-07 17:13:41 +01:00
if box == * pubkey {
return true
}
}
}
2018-10-08 19:51:51 +01:00
// Allow outbound sessions if appropriate
2019-03-28 00:30:25 +00:00
if ss . core . config . Current . SessionFirewall . AlwaysAllowOutbound {
2018-10-08 19:51:51 +01:00
if initiator {
return true
}
}
2018-10-07 17:13:41 +01:00
// Look and see if the pubkey is that of a direct peer
var isDirectPeer bool
for _ , peer := range ss . core . peers . ports . Load ( ) . ( map [ switchPort ] * peer ) {
if peer . box == * pubkey {
isDirectPeer = true
break
}
}
// Allow direct peers if appropriate
2019-03-28 00:30:25 +00:00
if ss . core . config . Current . SessionFirewall . AllowFromDirect && isDirectPeer {
2018-10-07 17:13:41 +01:00
return true
}
// Allow remote nodes if appropriate
2019-03-28 00:30:25 +00:00
if ss . core . config . Current . SessionFirewall . AllowFromRemote && ! isDirectPeer {
2018-10-07 17:13:41 +01:00
return true
}
// Finally, default-deny if not matching any of the above rules
return false
}
2018-06-10 18:03:28 -05:00
// Gets the session corresponding to a given handle.
2018-12-14 20:49:18 -06:00
func ( ss * sessions ) getSessionForHandle ( handle * crypto . Handle ) ( * sessionInfo , bool ) {
2018-01-04 22:37:51 +00:00
sinfo , isIn := ss . sinfos [ * handle ]
if isIn && sinfo . timedout ( ) {
// We have a session, but it has timed out
return nil , false
}
return sinfo , isIn
2017-12-28 22:16:20 -06:00
}
2018-06-10 18:03:28 -05:00
// Gets a session corresponding to an ephemeral session key used by this node.
2018-12-14 20:49:18 -06:00
func ( ss * sessions ) getByMySes ( key * crypto . BoxPubKey ) ( * sessionInfo , bool ) {
2018-01-04 22:37:51 +00:00
h , isIn := ss . byMySes [ * key ]
if ! isIn {
return nil , false
}
sinfo , isIn := ss . getSessionForHandle ( h )
return sinfo , isIn
2017-12-28 22:16:20 -06:00
}
2018-06-10 18:03:28 -05:00
// Gets a session corresponding to a permanent key used by the remote node.
2018-12-14 20:49:18 -06:00
func ( ss * sessions ) getByTheirPerm ( key * crypto . BoxPubKey ) ( * sessionInfo , bool ) {
2018-01-04 22:37:51 +00:00
h , isIn := ss . byTheirPerm [ * key ]
if ! isIn {
return nil , false
}
sinfo , isIn := ss . getSessionForHandle ( h )
return sinfo , isIn
2017-12-28 22:16:20 -06:00
}
2018-06-10 18:03:28 -05:00
// Gets a session corresponding to an IPv6 address used by the remote node.
2018-12-14 20:49:18 -06:00
func ( ss * sessions ) getByTheirAddr ( addr * address . Address ) ( * sessionInfo , bool ) {
2018-01-04 22:37:51 +00:00
p , isIn := ss . addrToPerm [ * addr ]
if ! isIn {
return nil , false
}
sinfo , isIn := ss . getByTheirPerm ( p )
return sinfo , isIn
2017-12-28 22:16:20 -06:00
}
2018-06-10 18:03:28 -05:00
// Gets a session corresponding to an IPv6 /64 subnet used by the remote node/network.
2018-12-14 20:49:18 -06:00
func ( ss * sessions ) getByTheirSubnet ( snet * address . Subnet ) ( * sessionInfo , bool ) {
2018-01-04 22:37:51 +00:00
p , isIn := ss . subnetToPerm [ * snet ]
if ! isIn {
return nil , false
}
sinfo , isIn := ss . getByTheirPerm ( p )
return sinfo , isIn
2017-12-28 22:16:20 -06:00
}
2018-06-10 18:03:28 -05:00
// Creates a new session and lazily cleans up old/timedout existing sessions.
// This includse initializing session info to sane defaults (e.g. lowest supported MTU).
2018-12-14 20:49:18 -06:00
func ( ss * sessions ) createSession ( theirPermKey * crypto . BoxPubKey ) * sessionInfo {
2019-01-14 18:24:35 +00:00
if ! ss . isSessionAllowed ( theirPermKey , true ) {
return nil
2018-10-08 19:51:51 +01:00
}
2018-01-04 22:37:51 +00:00
sinfo := sessionInfo { }
sinfo . core = ss . core
2018-12-30 12:04:42 +00:00
sinfo . reconfigure = make ( chan chan error , 1 )
2018-01-04 22:37:51 +00:00
sinfo . theirPermPub = * theirPermKey
2018-12-14 20:49:18 -06:00
pub , priv := crypto . NewBoxKeys ( )
2018-01-04 22:37:51 +00:00
sinfo . mySesPub = * pub
sinfo . mySesPriv = * priv
2018-12-14 20:49:18 -06:00
sinfo . myNonce = * crypto . NewBoxNonce ( )
2018-02-11 23:09:05 +00:00
sinfo . theirMTU = 1280
2019-04-20 16:32:27 +01:00
sinfo . myMTU = 1280
2018-04-22 15:31:30 -05:00
now := time . Now ( )
2019-04-21 20:38:14 -05:00
sinfo . time = now
sinfo . mtuTime = now
sinfo . pingTime = now
sinfo . pingSend = now
2018-01-04 22:37:51 +00:00
higher := false
for idx := range ss . core . boxPub {
if ss . core . boxPub [ idx ] > sinfo . theirPermPub [ idx ] {
higher = true
break
} else if ss . core . boxPub [ idx ] < sinfo . theirPermPub [ idx ] {
break
}
}
if higher {
// higher => odd nonce
sinfo . myNonce [ len ( sinfo . myNonce ) - 1 ] |= 0x01
} else {
// lower => even nonce
sinfo . myNonce [ len ( sinfo . myNonce ) - 1 ] &= 0xfe
}
2018-12-14 20:49:18 -06:00
sinfo . myHandle = * crypto . NewHandle ( )
sinfo . theirAddr = * address . AddrForNodeID ( crypto . GetNodeID ( & sinfo . theirPermPub ) )
sinfo . theirSubnet = * address . SubnetForNodeID ( crypto . GetNodeID ( & sinfo . theirPermPub ) )
2019-04-21 20:38:14 -05:00
sinfo . worker = make ( chan func ( ) , 1 )
2019-04-22 11:20:35 +01:00
sinfo . recv = make ( chan * wire_trafficPacket , 32 )
2018-01-04 22:37:51 +00:00
ss . sinfos [ sinfo . myHandle ] = & sinfo
ss . byMySes [ sinfo . mySesPub ] = & sinfo . myHandle
ss . byTheirPerm [ sinfo . theirPermPub ] = & sinfo . myHandle
ss . addrToPerm [ sinfo . theirAddr ] = & sinfo . theirPermPub
ss . subnetToPerm [ sinfo . theirSubnet ] = & sinfo . theirPermPub
2019-04-21 20:38:14 -05:00
go sinfo . workerMain ( )
2018-01-04 22:37:51 +00:00
return & sinfo
2017-12-28 22:16:20 -06:00
}
2018-06-21 20:31:30 -05:00
func ( ss * sessions ) cleanup ( ) {
// Time thresholds almost certainly could use some adjusting
2018-11-25 12:25:38 -06:00
for k := range ss . permShared {
// Delete a key, to make sure this eventually shrinks to 0
delete ( ss . permShared , k )
break
}
2018-06-21 20:31:30 -05:00
if time . Since ( ss . lastCleanup ) < time . Minute {
return
}
for _ , s := range ss . sinfos {
if s . timedout ( ) {
s . close ( )
}
}
2018-12-14 20:49:18 -06:00
permShared := make ( map [ crypto . BoxPubKey ] * crypto . BoxSharedKey , len ( ss . permShared ) )
2018-11-25 12:25:38 -06:00
for k , v := range ss . permShared {
permShared [ k ] = v
}
ss . permShared = permShared
2018-12-14 20:49:18 -06:00
sinfos := make ( map [ crypto . Handle ] * sessionInfo , len ( ss . sinfos ) )
2018-11-25 12:25:38 -06:00
for k , v := range ss . sinfos {
sinfos [ k ] = v
}
ss . sinfos = sinfos
2018-12-14 20:49:18 -06:00
byMySes := make ( map [ crypto . BoxPubKey ] * crypto . Handle , len ( ss . byMySes ) )
2018-11-25 12:25:38 -06:00
for k , v := range ss . byMySes {
byMySes [ k ] = v
}
ss . byMySes = byMySes
2018-12-14 20:49:18 -06:00
byTheirPerm := make ( map [ crypto . BoxPubKey ] * crypto . Handle , len ( ss . byTheirPerm ) )
2018-11-25 12:25:38 -06:00
for k , v := range ss . byTheirPerm {
byTheirPerm [ k ] = v
}
ss . byTheirPerm = byTheirPerm
2018-12-14 20:49:18 -06:00
addrToPerm := make ( map [ address . Address ] * crypto . BoxPubKey , len ( ss . addrToPerm ) )
2018-11-25 12:25:38 -06:00
for k , v := range ss . addrToPerm {
addrToPerm [ k ] = v
}
ss . addrToPerm = addrToPerm
2018-12-14 20:49:18 -06:00
subnetToPerm := make ( map [ address . Subnet ] * crypto . BoxPubKey , len ( ss . subnetToPerm ) )
2018-11-25 12:25:38 -06:00
for k , v := range ss . subnetToPerm {
subnetToPerm [ k ] = v
}
ss . subnetToPerm = subnetToPerm
2018-06-21 20:31:30 -05:00
ss . lastCleanup = time . Now ( )
}
2018-06-10 18:03:28 -05:00
// Closes a session, removing it from sessions maps and killing the worker goroutine.
2017-12-28 22:16:20 -06:00
func ( sinfo * sessionInfo ) close ( ) {
2018-01-04 22:37:51 +00:00
delete ( sinfo . core . sessions . sinfos , sinfo . myHandle )
delete ( sinfo . core . sessions . byMySes , sinfo . mySesPub )
delete ( sinfo . core . sessions . byTheirPerm , sinfo . theirPermPub )
delete ( sinfo . core . sessions . addrToPerm , sinfo . theirAddr )
delete ( sinfo . core . sessions . subnetToPerm , sinfo . theirSubnet )
2019-04-21 20:38:14 -05:00
close ( sinfo . worker )
2017-12-28 22:16:20 -06:00
}
2018-06-10 18:03:28 -05:00
// Returns a session ping appropriate for the given session info.
2017-12-28 22:16:20 -06:00
func ( ss * sessions ) getPing ( sinfo * sessionInfo ) sessionPing {
2018-01-04 22:37:51 +00:00
loc := ss . core . switchTable . getLocator ( )
coords := loc . getCoords ( )
ref := sessionPing {
2018-06-02 22:19:42 +01:00
SendPermPub : ss . core . boxPub ,
Handle : sinfo . myHandle ,
SendSesPub : sinfo . mySesPub ,
Tstamp : time . Now ( ) . Unix ( ) ,
Coords : coords ,
MTU : sinfo . myMTU ,
2018-01-04 22:37:51 +00:00
}
2018-12-14 20:49:18 -06:00
sinfo . myNonce . Increment ( )
2018-01-04 22:37:51 +00:00
return ref
2017-12-28 22:16:20 -06:00
}
2018-06-10 18:03:28 -05:00
// Gets the shared key for a pair of box keys.
// Used to cache recently used shared keys for protocol traffic.
// This comes up with dht req/res and session ping/pong traffic.
2018-12-14 20:49:18 -06:00
func ( ss * sessions ) getSharedKey ( myPriv * crypto . BoxPrivKey ,
theirPub * crypto . BoxPubKey ) * crypto . BoxSharedKey {
2018-01-04 22:37:51 +00:00
if skey , isIn := ss . permShared [ * theirPub ] ; isIn {
return skey
}
// First do some cleanup
2018-10-20 14:48:07 -05:00
const maxKeys = 1024
2018-01-04 22:37:51 +00:00
for key := range ss . permShared {
// Remove a random key until the store is small enough
if len ( ss . permShared ) < maxKeys {
break
}
delete ( ss . permShared , key )
}
2018-12-14 20:49:18 -06:00
ss . permShared [ * theirPub ] = crypto . GetSharedKey ( myPriv , theirPub )
2018-01-04 22:37:51 +00:00
return ss . permShared [ * theirPub ]
2017-12-28 22:16:20 -06:00
}
2018-06-10 18:03:28 -05:00
// Sends a session ping by calling sendPingPong in ping mode.
2017-12-28 22:16:20 -06:00
func ( ss * sessions ) ping ( sinfo * sessionInfo ) {
2018-01-04 22:37:51 +00:00
ss . sendPingPong ( sinfo , false )
2017-12-28 22:16:20 -06:00
}
2018-06-10 18:03:28 -05:00
// 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.
2017-12-28 22:16:20 -06:00
func ( ss * sessions ) sendPingPong ( sinfo * sessionInfo , isPong bool ) {
2018-01-04 22:37:51 +00:00
ping := ss . getPing ( sinfo )
2018-06-02 22:19:42 +01:00
ping . IsPong = isPong
2018-01-04 22:37:51 +00:00
bs := ping . encode ( )
shared := ss . getSharedKey ( & ss . core . boxPriv , & sinfo . theirPermPub )
2018-12-14 20:49:18 -06:00
payload , nonce := crypto . BoxSeal ( shared , bs , nil )
2018-01-04 22:37:51 +00:00
p := wire_protoTrafficPacket {
2018-06-02 21:21:05 +01:00
Coords : sinfo . coords ,
ToKey : sinfo . theirPermPub ,
FromKey : ss . core . boxPub ,
Nonce : * nonce ,
Payload : payload ,
2018-01-04 22:37:51 +00:00
}
packet := p . encode ( )
ss . core . router . out ( packet )
2018-04-22 15:31:30 -05:00
if ! isPong {
2019-04-21 20:38:14 -05:00
sinfo . pingSend = time . Now ( )
2018-04-22 15:31:30 -05:00
}
2017-12-28 22:16:20 -06:00
}
2018-06-10 18:03:28 -05:00
// 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.
2017-12-28 22:16:20 -06:00
func ( ss * sessions ) handlePing ( ping * sessionPing ) {
2018-01-04 22:37:51 +00:00
// Get the corresponding session (or create a new session)
2018-06-02 22:19:42 +01:00
sinfo , isIn := ss . getByTheirPerm ( & ping . SendPermPub )
2018-10-07 17:13:41 +01:00
// Check the session firewall
2019-01-16 20:38:51 +00:00
if ! isIn && ss . isSessionFirewallEnabled ( ) {
2018-10-08 19:51:51 +01:00
if ! ss . isSessionAllowed ( & ping . SendPermPub , false ) {
2018-10-07 17:13:41 +01:00
return
}
}
2018-01-04 22:37:51 +00:00
if ! isIn || sinfo . timedout ( ) {
if isIn {
sinfo . close ( )
}
2018-06-02 22:19:42 +01:00
ss . createSession ( & ping . SendPermPub )
sinfo , isIn = ss . getByTheirPerm ( & ping . SendPermPub )
2018-01-04 22:37:51 +00:00
if ! isIn {
panic ( "This should not happen" )
}
2019-04-19 22:57:52 +01:00
ss . listenerMutex . Lock ( )
if ss . listener != nil {
conn := & Conn {
2019-04-20 16:32:27 +01:00
core : ss . core ,
session : sinfo ,
mutex : & sync . RWMutex { } ,
nodeID : crypto . GetNodeID ( & sinfo . theirPermPub ) ,
nodeMask : & crypto . NodeID { } ,
2019-04-22 11:20:35 +01:00
recv : sinfo . recv ,
2019-04-19 22:57:52 +01:00
}
for i := range conn . nodeMask {
conn . nodeMask [ i ] = 0xFF
}
ss . listener . conn <- conn
} else {
ss . core . log . Debugln ( "Received new session but there is no listener, ignoring" )
}
ss . listenerMutex . Unlock ( )
2018-01-04 22:37:51 +00:00
}
2019-04-21 20:38:14 -05:00
sinfo . doWorker ( func ( ) {
// Update the session
if ! sinfo . update ( ping ) { /*panic("Should not happen in testing")*/
return
}
if ! ping . IsPong {
ss . sendPingPong ( sinfo , true )
}
if sinfo . packet != nil {
/ * FIXME this needs to live in the net . Conn or something , needs work in Write
// send
var bs [ ] byte
bs , sinfo . packet = sinfo . packet , nil
ss . core . router . sendPacket ( bs ) // FIXME this needs to live in the net.Conn or something, needs work in Write
* /
sinfo . packet = nil
}
} )
2017-12-28 22:16:20 -06:00
}
2018-06-10 18:03:28 -05:00
// 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.
2018-02-11 23:09:05 +00:00
func ( sinfo * sessionInfo ) getMTU ( ) uint16 {
2018-05-18 18:56:33 +01:00
if sinfo . theirMTU == 0 || sinfo . myMTU == 0 {
return 0
}
2018-02-11 23:09:05 +00:00
if sinfo . theirMTU < sinfo . myMTU {
return sinfo . theirMTU
}
return sinfo . myMTU
}
2018-06-10 18:03:28 -05:00
// Checks if a packet's nonce is recent enough to fall within the window of allowed packets, and not already received.
2018-12-14 20:49:18 -06:00
func ( sinfo * sessionInfo ) nonceIsOK ( theirNonce * crypto . BoxNonce ) bool {
2018-01-04 22:37:51 +00:00
// The bitmask is to allow for some non-duplicate out-of-order packets
2018-12-14 20:49:18 -06:00
diff := theirNonce . Minus ( & sinfo . theirNonce )
2018-01-04 22:37:51 +00:00
if diff > 0 {
return true
}
2019-04-19 20:10:41 +01:00
return ^ sinfo . theirNonceMask & ( 0x01 << uint64 ( - diff ) ) != 0
2017-12-28 22:16:20 -06:00
}
2018-06-10 18:03:28 -05:00
// 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
2018-12-14 20:49:18 -06:00
func ( sinfo * sessionInfo ) updateNonce ( theirNonce * crypto . BoxNonce ) {
2018-01-04 22:37:51 +00:00
// Shift nonce mask if needed
// Set bit
2018-12-14 20:49:18 -06:00
diff := theirNonce . Minus ( & sinfo . theirNonce )
2018-01-04 22:37:51 +00:00
if diff > 0 {
2018-06-10 18:03:28 -05:00
// This nonce is newer, so shift the window before setting the bit, and update theirNonce in the session info.
2019-04-19 20:10:41 +01:00
sinfo . theirNonceMask <<= uint64 ( diff )
sinfo . theirNonceMask &= 0x01
2018-06-10 18:03:28 -05:00
sinfo . theirNonce = * theirNonce
2018-01-04 22:37:51 +00:00
} else {
2018-06-10 18:03:28 -05:00
// This nonce is older, so set the bit but do not shift the window.
2019-04-19 20:10:41 +01:00
sinfo . theirNonceMask &= 0x01 << uint64 ( - diff )
2018-01-04 22:37:51 +00:00
}
2017-12-28 22:16:20 -06:00
}
2018-06-10 18:03:28 -05:00
// 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.
2017-12-28 22:16:20 -06:00
func ( ss * sessions ) resetInits ( ) {
2018-01-04 22:37:51 +00:00
for _ , sinfo := range ss . sinfos {
2019-04-21 20:38:14 -05:00
sinfo . doWorker ( func ( ) {
sinfo . init = false
} )
2018-01-04 22:37:51 +00:00
}
2017-12-28 22:16:20 -06:00
}