2017-12-28 22:16:20 -06:00
package yggdrasil
// TODO cleanup, this file is kind of a mess
2018-01-26 17:30:51 -06:00
// Commented code should be removed
// Live code should be better commented
2017-12-28 22:16:20 -06:00
2018-06-12 17:50:08 -05:00
import (
2019-01-16 20:26:39 +00:00
"encoding/hex"
2018-06-12 17:50:08 -05:00
"time"
2018-12-14 20:49:18 -06:00
"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
2019-08-24 12:46:24 -05:00
"github.com/Arceliar/phony"
2018-06-12 17:50:08 -05:00
)
2017-12-28 22:16:20 -06:00
2018-06-10 18:03:28 -05:00
// The peers struct represents peers with an active connection.
2019-01-09 11:42:07 +02:00
// Incoming packets are passed to the corresponding peer, which handles them somehow.
2018-06-10 18:03:28 -05:00
// In most cases, this involves passing the packet to the handler for outgoing traffic to another peer.
2019-11-29 11:45:02 +02:00
// In other cases, its link protocol traffic is used to build the spanning tree, in which case this checks signatures and passes the message along to the switch.
2017-12-28 22:16:20 -06:00
type peers struct {
2020-03-29 00:23:38 -05:00
phony . Inbox
2019-08-25 12:10:59 -05:00
core * Core
2020-03-29 00:23:38 -05:00
ports map [ switchPort ] * peer // use CoW semantics, share updated version with each peer
table * lookupTable // Sent from switch, share updated version with each peer
2017-12-28 22:16:20 -06:00
}
2018-06-10 18:03:28 -05:00
// Initializes the peers struct.
2017-12-28 22:16:20 -06:00
func ( ps * peers ) init ( c * Core ) {
2018-01-04 22:37:51 +00:00
ps . core = c
2020-03-29 00:23:38 -05:00
ps . ports = make ( map [ switchPort ] * peer )
ps . table = new ( lookupTable )
2019-08-25 12:10:59 -05:00
}
2019-08-28 19:31:04 +01:00
func ( ps * peers ) reconfigure ( ) {
2019-08-25 12:10:59 -05:00
// This is where reconfiguration would go, if we had anything to do
2018-05-06 16:32:34 -05:00
}
2019-01-16 20:26:39 +00:00
// Returns true if an incoming peer connection to a key is allowed, either
// because the key is in the whitelist or because the whitelist is empty.
2018-12-14 20:49:18 -06:00
func ( ps * peers ) isAllowedEncryptionPublicKey ( box * crypto . BoxPubKey ) bool {
2019-01-16 20:26:39 +00:00
boxstr := hex . EncodeToString ( box [ : ] )
2019-03-28 00:30:25 +00:00
ps . core . config . Mutex . RLock ( )
defer ps . core . config . Mutex . RUnlock ( )
for _ , v := range ps . core . config . Current . AllowedEncryptionPublicKeys {
2019-01-16 20:26:39 +00:00
if v == boxstr {
return true
}
}
2019-03-28 00:30:25 +00:00
return len ( ps . core . config . Current . AllowedEncryptionPublicKeys ) == 0
2017-12-28 22:16:20 -06:00
}
2018-06-10 18:03:28 -05:00
// Adds a key to the whitelist.
2019-01-16 20:26:39 +00:00
func ( ps * peers ) addAllowedEncryptionPublicKey ( box string ) {
2019-03-28 00:30:25 +00:00
ps . core . config . Mutex . RLock ( )
defer ps . core . config . Mutex . RUnlock ( )
ps . core . config . Current . AllowedEncryptionPublicKeys =
append ( ps . core . config . Current . AllowedEncryptionPublicKeys , box )
2018-05-06 19:01:52 -05:00
}
2018-06-10 18:03:28 -05:00
// Removes a key from the whitelist.
2019-01-16 20:26:39 +00:00
func ( ps * peers ) removeAllowedEncryptionPublicKey ( box string ) {
2019-03-28 00:30:25 +00:00
ps . core . config . Mutex . RLock ( )
defer ps . core . config . Mutex . RUnlock ( )
for k , v := range ps . core . config . Current . AllowedEncryptionPublicKeys {
2019-01-16 20:26:39 +00:00
if v == box {
2019-03-28 00:30:25 +00:00
ps . core . config . Current . AllowedEncryptionPublicKeys =
append ( ps . core . config . Current . AllowedEncryptionPublicKeys [ : k ] ,
ps . core . config . Current . AllowedEncryptionPublicKeys [ k + 1 : ] ... )
2019-01-16 20:26:39 +00:00
}
}
2018-05-06 19:01:52 -05:00
}
2018-06-10 18:03:28 -05:00
// Gets the whitelist of allowed keys for incoming connections.
2019-01-16 20:26:39 +00:00
func ( ps * peers ) getAllowedEncryptionPublicKeys ( ) [ ] string {
2019-03-28 00:30:25 +00:00
ps . core . config . Mutex . RLock ( )
defer ps . core . config . Mutex . RUnlock ( )
return ps . core . config . Current . AllowedEncryptionPublicKeys
2018-05-06 19:01:52 -05:00
}
2019-11-29 11:45:02 +02:00
// Information known about a peer, including their box/sig keys, precomputed shared keys (static and ephemeral) and a handler for their outgoing traffic
2017-12-28 22:16:20 -06:00
type peer struct {
2019-08-25 10:36:09 -05:00
phony . Inbox
2018-10-21 22:58:27 +01:00
core * Core
2020-05-23 10:28:57 -05:00
intf linkInterface
2018-10-21 22:58:27 +01:00
port switchPort
2018-12-15 18:11:02 -06:00
box crypto . BoxPubKey
sig crypto . SigPubKey
shared crypto . BoxSharedKey
linkShared crypto . BoxSharedKey
2018-10-21 22:58:27 +01:00
endpoint string
2020-05-16 17:07:47 -05:00
firstSeen time . Time // To track uptime for getPeers
dinfo * dhtInfo // used to keep the DHT working
2019-08-24 14:24:42 -05:00
// The below aren't actually useful internally, they're just gathered for getPeers statistics
bytesSent uint64
bytesRecvd uint64
2020-03-29 00:23:38 -05:00
ports map [ switchPort ] * peer
table * lookupTable
2020-04-03 00:32:26 -05:00
queue packetQueue
2020-05-17 12:58:57 -05:00
max uint64
2020-05-16 17:07:47 -05:00
seq uint64 // this and idle are used to detect when to drop packets from queue
2020-04-03 00:32:26 -05:00
idle bool
2020-05-17 12:09:40 -05:00
drop bool // set to true if we're dropping packets from the queue
2020-03-29 00:23:38 -05:00
}
func ( ps * peers ) updateTables ( from phony . Actor , table * lookupTable ) {
ps . Act ( from , func ( ) {
ps . table = table
ps . _updatePeers ( )
} )
}
func ( ps * peers ) _updatePeers ( ) {
ports := ps . ports
table := ps . table
for _ , peer := range ps . ports {
p := peer // peer is mutated during iteration
p . Act ( ps , func ( ) {
p . ports = ports
p . table = table
} )
}
2017-12-28 22:16:20 -06:00
}
2018-01-04 22:37:51 +00:00
2019-01-09 11:42:07 +02:00
// Creates a new peer with the specified box, sig, and linkShared keys, using the lowest unoccupied port number.
2020-05-23 10:28:57 -05:00
func ( ps * peers ) _newPeer ( box * crypto . BoxPubKey , sig * crypto . SigPubKey , linkShared * crypto . BoxSharedKey , intf linkInterface ) * peer {
2018-05-18 20:41:02 -05:00
now := time . Now ( )
2018-01-04 22:37:51 +00:00
p := peer { box : * box ,
2020-05-16 17:07:47 -05:00
core : ps . core ,
intf : intf ,
2018-10-21 23:20:14 +01:00
sig : * sig ,
2018-12-14 20:49:18 -06:00
shared : * crypto . GetSharedKey ( & ps . core . boxPriv , box ) ,
2018-10-21 23:20:14 +01:00
linkShared : * linkShared ,
firstSeen : now ,
2019-03-04 20:33:08 +00:00
}
2020-03-29 00:23:38 -05:00
oldPorts := ps . ports
2018-01-04 22:37:51 +00:00
newPorts := make ( map [ switchPort ] * peer )
for k , v := range oldPorts {
newPorts [ k ] = v
}
for idx := switchPort ( 0 ) ; true ; idx ++ {
if _ , isIn := newPorts [ idx ] ; ! isIn {
p . port = switchPort ( idx )
newPorts [ p . port ] = & p
break
}
}
2020-03-29 00:23:38 -05:00
ps . ports = newPorts
ps . _updatePeers ( )
2018-01-04 22:37:51 +00:00
return & p
2017-12-28 22:16:20 -06:00
}
2020-03-29 00:23:38 -05:00
func ( p * peer ) _removeSelf ( ) {
p . core . peers . Act ( p , func ( ) {
p . core . peers . _removePeer ( p )
} )
}
2018-06-10 18:03:28 -05:00
// Removes a peer for a given port, if one exists.
2020-03-29 00:23:38 -05:00
func ( ps * peers ) _removePeer ( p * peer ) {
if q := ps . ports [ p . port ] ; p . port == 0 || q != p {
2018-05-05 17:14:03 -05:00
return
2020-03-29 00:23:38 -05:00
} // Can't remove self peer or nonexistant peer
2020-03-29 19:01:50 -05:00
ps . core . switchTable . forgetPeer ( ps , p . port )
2020-03-29 00:23:38 -05:00
oldPorts := ps . ports
2018-05-05 17:14:03 -05:00
newPorts := make ( map [ switchPort ] * peer )
for k , v := range oldPorts {
newPorts [ k ] = v
}
2020-03-29 00:23:38 -05:00
delete ( newPorts , p . port )
2020-05-16 17:07:47 -05:00
p . intf . close ( )
2020-03-29 00:23:38 -05:00
ps . ports = newPorts
ps . _updatePeers ( )
2018-05-05 17:14:03 -05:00
}
2018-06-10 18:03:28 -05:00
// If called, sends a notification to each peer that they should send a new switch message.
// Mainly called by the switch after an update.
2019-08-25 10:36:09 -05:00
func ( ps * peers ) sendSwitchMsgs ( from phony . Actor ) {
2020-03-29 00:23:38 -05:00
ps . Act ( from , func ( ) {
for _ , peer := range ps . ports {
p := peer
if p . port == 0 {
continue
}
p . Act ( ps , p . _sendSwitchMsg )
2018-06-07 00:16:47 -05:00
}
2020-03-29 00:23:38 -05:00
} )
2018-06-07 00:16:47 -05:00
}
2020-05-16 09:25:57 -05:00
func ( ps * peers ) updateDHT ( from phony . Actor ) {
ps . Act ( from , func ( ) {
for _ , peer := range ps . ports {
p := peer
if p . port == 0 {
continue
}
p . Act ( ps , p . _updateDHT )
}
} )
}
2018-06-10 18:03:28 -05:00
// This must be launched in a separate goroutine by whatever sets up the peer struct.
2019-08-25 17:00:02 -05:00
func ( p * peer ) start ( ) {
// Just for good measure, immediately send a switch message to this peer when we start
2019-08-27 19:43:54 -05:00
p . Act ( nil , p . _sendSwitchMsg )
2017-12-28 22:16:20 -06:00
}
2019-08-24 13:15:29 -05:00
func ( p * peer ) _updateDHT ( ) {
if p . dinfo != nil {
p . core . router . insertPeer ( p , p . dinfo )
}
}
2019-08-25 10:36:09 -05:00
func ( p * peer ) handlePacketFrom ( from phony . Actor , packet [ ] byte ) {
2019-08-27 19:43:54 -05:00
p . Act ( from , func ( ) {
2019-08-24 12:55:49 -05:00
p . _handlePacket ( packet )
} )
}
2018-06-10 18:03:28 -05:00
// Called to handle incoming packets.
// Passes the packet to a handler for that packet type.
2019-08-24 12:55:49 -05:00
func ( p * peer ) _handlePacket ( packet [ ] byte ) {
2018-06-12 17:50:08 -05:00
// FIXME this is off by stream padding and msg length overhead, should be done in tcp.go
2019-08-24 14:24:42 -05:00
p . bytesRecvd += uint64 ( len ( packet ) )
2018-01-04 22:37:51 +00:00
pType , pTypeLen := wire_decode_uint64 ( packet )
if pTypeLen == 0 {
return
}
switch pType {
case wire_Traffic :
2019-08-24 16:04:05 -05:00
p . _handleTraffic ( packet )
2018-01-04 22:37:51 +00:00
case wire_ProtocolTraffic :
2019-08-24 16:04:05 -05:00
p . _handleTraffic ( packet )
2018-01-04 22:37:51 +00:00
case wire_LinkProtocolTraffic :
2019-08-24 12:46:24 -05:00
p . _handleLinkTraffic ( packet )
2018-06-07 00:49:06 -05:00
default :
2018-01-04 22:37:51 +00:00
}
2017-12-28 22:16:20 -06:00
}
2020-04-03 00:32:26 -05:00
// Get the coords of a packet without decoding
func peer_getPacketCoords ( packet [ ] byte ) [ ] byte {
_ , pTypeLen := wire_decode_uint64 ( packet )
coords , _ := wire_decode_coords ( packet [ pTypeLen : ] )
return coords
}
2018-06-10 18:03:28 -05:00
// Called to handle traffic or protocolTraffic packets.
// In either case, this reads from the coords of the packet header, does a switch lookup, and forwards to the next node.
2019-08-24 16:04:05 -05:00
func ( p * peer ) _handleTraffic ( packet [ ] byte ) {
2020-03-29 00:23:38 -05:00
if _ , isIn := p . table . elems [ p . port ] ; ! isIn && p . port != 0 {
2018-12-14 21:44:31 -06:00
// Drop traffic if the peer isn't in the switch
2018-06-07 00:16:47 -05:00
return
}
2020-04-03 00:32:26 -05:00
coords := peer_getPacketCoords ( packet )
2020-03-29 00:23:38 -05:00
next := p . table . lookup ( coords )
if nPeer , isIn := p . ports [ next ] ; isIn {
2020-05-17 12:27:43 -05:00
nPeer . sendPacketFrom ( p , packet )
2020-03-29 00:23:38 -05:00
}
//p.core.switchTable.packetInFrom(p, packet)
2017-12-28 22:16:20 -06:00
}
2020-05-17 12:27:43 -05:00
func ( p * peer ) sendPacketFrom ( from phony . Actor , packet [ ] byte ) {
2019-08-27 19:43:54 -05:00
p . Act ( from , func ( ) {
2020-05-17 12:27:43 -05:00
p . _sendPacket ( packet )
2019-08-24 12:46:24 -05:00
} )
}
2020-05-17 12:27:43 -05:00
func ( p * peer ) _sendPacket ( packet [ ] byte ) {
p . queue . push ( packet )
2020-05-27 18:53:14 -05:00
if p . idle {
2020-04-03 00:32:26 -05:00
p . idle = false
p . _handleIdle ( )
2020-05-27 18:53:14 -05:00
} else if p . drop {
2020-05-17 12:58:57 -05:00
for p . queue . size > p . max {
2020-05-17 12:09:40 -05:00
p . queue . drop ( )
}
2020-04-03 00:32:26 -05:00
}
}
func ( p * peer ) _handleIdle ( ) {
var packets [ ] [ ] byte
var size uint64
2020-05-27 18:53:14 -05:00
for {
2020-04-03 00:32:26 -05:00
if packet , success := p . queue . pop ( ) ; success {
packets = append ( packets , packet )
size += uint64 ( len ( packet ) )
} else {
break
}
}
2020-05-24 17:49:48 -05:00
p . seq ++
2020-04-03 00:32:26 -05:00
if len ( packets ) > 0 {
p . bytesSent += uint64 ( size )
2020-05-16 17:07:47 -05:00
p . intf . out ( packets )
2020-05-17 13:32:58 -05:00
p . max = p . queue . size
2020-04-03 00:32:26 -05:00
} else {
p . idle = true
2019-08-18 12:17:54 -05:00
}
2020-05-27 18:53:14 -05:00
p . drop = false
2017-12-28 22:16:20 -06:00
}
2020-05-27 18:53:14 -05:00
func ( p * peer ) notifyBlocked ( from phony . Actor ) {
2020-05-16 17:07:47 -05:00
p . Act ( from , func ( ) {
2020-05-27 18:53:14 -05:00
seq := p . seq
2020-05-24 17:24:50 -05:00
p . Act ( nil , func ( ) {
2020-05-24 17:49:48 -05:00
if seq == p . seq {
2020-05-24 17:24:50 -05:00
p . drop = true
2020-05-27 18:53:14 -05:00
p . max = 2 * p . queue . size + streamMsgSize
2020-05-24 17:24:50 -05:00
}
} )
2020-05-16 17:07:47 -05:00
} )
}
2018-06-10 18:03:28 -05:00
// This wraps the packet in the inner (ephemeral) and outer (permanent) crypto layers.
// It sends it to p.linkOut, which bypasses the usual packet queues.
2019-08-24 12:46:24 -05:00
func ( p * peer ) _sendLinkPacket ( packet [ ] byte ) {
2018-12-14 20:49:18 -06:00
innerPayload , innerNonce := crypto . BoxSeal ( & p . linkShared , packet , nil )
2018-06-08 18:42:56 -05:00
innerLinkPacket := wire_linkProtoTrafficPacket {
Nonce : * innerNonce ,
Payload : innerPayload ,
}
outerPayload := innerLinkPacket . encode ( )
2018-12-14 20:49:18 -06:00
bs , nonce := crypto . BoxSeal ( & p . shared , outerPayload , nil )
2018-01-04 22:37:51 +00:00
linkPacket := wire_linkProtoTrafficPacket {
2018-06-02 21:21:05 +01:00
Nonce : * nonce ,
Payload : bs ,
2018-01-04 22:37:51 +00:00
}
packet = linkPacket . encode ( )
2020-05-16 17:07:47 -05:00
p . intf . linkOut ( packet )
2017-12-28 22:16:20 -06:00
}
2018-06-10 18:03:28 -05:00
// Decrypts the outer (permanent) and inner (ephemeral) crypto layers on link traffic.
// Identifies the link traffic type and calls the appropriate handler.
2019-08-24 12:46:24 -05:00
func ( p * peer ) _handleLinkTraffic ( bs [ ] byte ) {
2018-01-04 22:37:51 +00:00
packet := wire_linkProtoTrafficPacket { }
if ! packet . decode ( bs ) {
return
}
2018-12-14 20:49:18 -06:00
outerPayload , isOK := crypto . BoxOpen ( & p . shared , packet . Payload , & packet . Nonce )
2018-06-08 18:42:56 -05:00
if ! isOK {
return
}
innerPacket := wire_linkProtoTrafficPacket { }
if ! innerPacket . decode ( outerPayload ) {
return
}
2018-12-14 20:49:18 -06:00
payload , isOK := crypto . BoxOpen ( & p . linkShared , innerPacket . Payload , & innerPacket . Nonce )
2018-01-04 22:37:51 +00:00
if ! isOK {
return
}
pType , pTypeLen := wire_decode_uint64 ( payload )
if pTypeLen == 0 {
return
}
switch pType {
2018-06-06 21:11:10 -05:00
case wire_SwitchMsg :
2019-08-24 12:46:24 -05:00
p . _handleSwitchMsg ( payload )
2018-06-10 18:03:28 -05:00
default :
2018-01-04 22:37:51 +00:00
}
2018-06-06 21:11:10 -05:00
}
2018-06-10 18:03:28 -05:00
// Gets a switchMsg from the switch, adds signed next-hop info for this peer, and sends it to them.
2019-08-24 12:46:24 -05:00
func ( p * peer ) _sendSwitchMsg ( ) {
2020-03-29 19:01:50 -05:00
msg := p . table . getMsg ( )
2018-06-07 13:56:11 -05:00
if msg == nil {
return
2018-06-06 21:11:10 -05:00
}
2018-06-07 13:56:11 -05:00
bs := getBytesForSig ( & p . sig , msg )
2018-06-06 21:11:10 -05:00
msg . Hops = append ( msg . Hops , switchMsgHop {
Port : p . port ,
Next : p . sig ,
2018-12-14 20:49:18 -06:00
Sig : * crypto . Sign ( & p . core . sigPriv , bs ) ,
2018-06-06 21:11:10 -05:00
} )
packet := msg . encode ( )
2019-08-24 12:46:24 -05:00
p . _sendLinkPacket ( packet )
2018-06-06 21:11:10 -05:00
}
2018-06-10 18:03:28 -05:00
// Handles a switchMsg from the peer, checking signatures and passing good messages to the switch.
// Also creates a dhtInfo struct and arranges for it to be added to the dht (this is how dht bootstrapping begins).
2019-08-24 12:46:24 -05:00
func ( p * peer ) _handleSwitchMsg ( packet [ ] byte ) {
2018-06-06 21:11:10 -05:00
var msg switchMsg
2018-06-07 16:53:39 -05:00
if ! msg . decode ( packet ) {
return
}
2018-06-06 21:11:10 -05:00
if len ( msg . Hops ) < 1 {
2020-03-29 00:23:38 -05:00
p . _removeSelf ( )
return
2018-06-06 21:11:10 -05:00
}
2018-06-07 13:56:11 -05:00
var loc switchLocator
2018-06-06 22:39:22 -05:00
prevKey := msg . Root
2018-06-07 13:56:11 -05:00
for idx , hop := range msg . Hops {
// Check signatures and collect coords for dht
sigMsg := msg
sigMsg . Hops = msg . Hops [ : idx ]
loc . coords = append ( loc . coords , hop . Port )
bs := getBytesForSig ( & hop . Next , & sigMsg )
2018-12-14 20:49:18 -06:00
if ! crypto . Verify ( & prevKey , bs , & hop . Sig ) {
2020-03-29 00:23:38 -05:00
p . _removeSelf ( )
return
2018-06-06 21:11:10 -05:00
}
2018-06-07 13:56:11 -05:00
prevKey = hop . Next
2018-06-06 21:11:10 -05:00
}
2020-03-29 19:01:50 -05:00
p . core . switchTable . Act ( p , func ( ) {
if ! p . core . switchTable . _checkRoot ( & msg ) {
// Bad switch message
p . Act ( & p . core . switchTable , func ( ) {
p . dinfo = nil
} )
} else {
// handle the message
p . core . switchTable . _handleMsg ( & msg , p . port , false )
p . Act ( & p . core . switchTable , func ( ) {
// Pass a message to the dht informing it that this peer (still) exists
loc . coords = loc . coords [ : len ( loc . coords ) - 1 ]
p . dinfo = & dhtInfo {
key : p . box ,
coords : loc . getCoords ( ) ,
}
p . _updateDHT ( )
} )
}
} )
2017-12-28 22:16:20 -06:00
}
2018-06-10 18:03:28 -05:00
// This generates the bytes that we sign or check the signature of for a switchMsg.
2019-01-09 11:42:07 +02:00
// It begins with the next node's key, followed by the root and the timestamp, followed by coords being advertised to the next node.
2018-12-14 20:49:18 -06:00
func getBytesForSig ( next * crypto . SigPubKey , msg * switchMsg ) [ ] byte {
2018-06-07 13:56:11 -05:00
var loc switchLocator
for _ , hop := range msg . Hops {
loc . coords = append ( loc . coords , hop . Port )
}
2018-01-04 22:37:51 +00:00
bs := append ( [ ] byte ( nil ) , next [ : ] ... )
2018-06-07 13:56:11 -05:00
bs = append ( bs , msg . Root [ : ] ... )
bs = append ( bs , wire_encode_uint64 ( wire_intToUint ( msg . TStamp ) ) ... )
2018-06-06 23:00:17 -05:00
bs = append ( bs , wire_encode_coords ( loc . getCoords ( ) ) ... )
2018-01-04 22:37:51 +00:00
return bs
2017-12-28 22:16:20 -06:00
}