From 042a3400fe08050ae0e30b8e453ae2f59bb5c50d Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 12 Dec 2018 22:40:49 +0000 Subject: [PATCH] Wrap the metadata with a mutex to guarantee thread safety across core/router/sessions --- src/yggdrasil/core.go | 15 ++++++++++----- src/yggdrasil/router.go | 2 ++ src/yggdrasil/session.go | 30 +++++++++++++++++++++++------- src/yggdrasil/wire.go | 7 +++++-- 4 files changed, 40 insertions(+), 14 deletions(-) diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index 5a9267c..72a11d7 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -80,11 +80,6 @@ func GetBuildVersion() string { return buildVersion } -// Gets the friendly name of this node, as specified in the NodeConfig. -func (c *Core) GetMeta() metadata { - return c.sessions.myMetadata -} - // Starts up Yggdrasil using the provided NodeConfig, and outputs debug logging // through the provided log.Logger. The started stack will include TCP and UDP // sockets, a multicast discovery socket, an admin socket, router, switch and @@ -245,6 +240,16 @@ func (c *Core) GetSubnet() *net.IPNet { return &net.IPNet{IP: subnet, Mask: net.CIDRMask(64, 128)} } +// Gets the node metadata. +func (c *Core) GetMetadata() metadata { + return c.sessions.getMetadata() +} + +// Sets the node metadata. +func (c *Core) SetMetadata(meta metadata) { + c.sessions.setMetadata(meta) +} + // Sets the output logger of the Yggdrasil node after startup. This may be // useful if you want to redirect the output later. func (c *Core) SetLogger(log *log.Logger) { diff --git a/src/yggdrasil/router.go b/src/yggdrasil/router.go index a40563b..8ac59d8 100644 --- a/src/yggdrasil/router.go +++ b/src/yggdrasil/router.go @@ -58,7 +58,9 @@ func (r *router) init(core *Core) { r.addr = *address_addrForNodeID(&r.core.dht.nodeID) r.subnet = *address_subnetForNodeID(&r.core.dht.nodeID) in := make(chan []byte, 32) // TODO something better than this... + r.core.sessions.myMetadataMutex.RLock() p := r.core.peers.newPeer(&r.core.boxPub, &r.core.sigPub, &boxSharedKey{}, "(self)", r.core.sessions.myMetadata) + r.core.sessions.myMetadataMutex.RUnlock() p.out = func(packet []byte) { // This is to make very sure it never blocks select { diff --git a/src/yggdrasil/session.go b/src/yggdrasil/session.go index 690f603..a7bed8a 100644 --- a/src/yggdrasil/session.go +++ b/src/yggdrasil/session.go @@ -7,6 +7,7 @@ package yggdrasil import ( "bytes" "encoding/hex" + "sync" "time" ) @@ -128,7 +129,8 @@ type sessions struct { sessionFirewallWhitelist []string sessionFirewallBlacklist []string // Metadata for this node - myMetadata metadata + myMetadata metadata + myMetadataMutex sync.RWMutex } // Initializes the session struct. @@ -143,8 +145,17 @@ func (ss *sessions) init(core *Core) { ss.lastCleanup = time.Now() } -// Enable or disable the session firewall +// Get the metadata +func (ss *sessions) getMetadata() metadata { + ss.myMetadataMutex.RLock() + defer ss.myMetadataMutex.RUnlock() + return ss.myMetadata +} + +// Set the metadata func (ss *sessions) setMetadata(meta metadata) { + ss.myMetadataMutex.Lock() + defer ss.myMetadataMutex.Unlock() ss.myMetadata = meta } @@ -485,18 +496,23 @@ func (ss *sessions) handlePing(ping *sessionPing) { bs, sinfo.packet = sinfo.packet, nil ss.core.router.sendPacket(bs) } - if time.Since(sinfo.metaResTime).Minutes() > 15 { - if time.Since(sinfo.metaReqTime).Minutes() > 1 { - ss.sendMeta(sinfo, false) - } - } + // This requests metadata from the remote side fairly quickly after + // establishing the session, and if other time constraints apply (no more + // often than 15 minutes since receiving the last metadata) + //if time.Since(sinfo.metaResTime).Minutes() > 15 { + // if time.Since(sinfo.metaReqTime).Minutes() > 1 { + // ss.sendMeta(sinfo, false) + // } + //} } func (ss *sessions) sendMeta(sinfo *sessionInfo, isResponse bool) { + ss.myMetadataMutex.RLock() meta := sessionMeta{ IsResponse: isResponse, Metadata: ss.myMetadata, } + ss.myMetadataMutex.RUnlock() bs := meta.encode() shared := ss.getSharedKey(&ss.core.boxPriv, &sinfo.theirPermPub) payload, nonce := boxSeal(shared, bs, nil) diff --git a/src/yggdrasil/wire.go b/src/yggdrasil/wire.go index 7f394d7..2d87a37 100644 --- a/src/yggdrasil/wire.go +++ b/src/yggdrasil/wire.go @@ -355,7 +355,7 @@ func (p *sessionPing) decode(bs []byte) bool { //////////////////////////////////////////////////////////////////////////////// -// Encodes a sessionPing into its wire format. +// Encodes a sessionMeta into its wire format. func (p *sessionMeta) encode() []byte { var pTypeVal uint64 if p.IsResponse { @@ -370,7 +370,7 @@ func (p *sessionMeta) encode() []byte { return bs } -// Decodes an encoded sessionPing into the struct, returning true if successful. +// Decodes an encoded sessionMeta into the struct, returning true if successful. func (p *sessionMeta) decode(bs []byte) bool { var pType uint64 switch { @@ -380,6 +380,9 @@ func (p *sessionMeta) decode(bs []byte) bool { return false } if p.IsResponse = pType == wire_SessionMetaResponse; p.IsResponse { + if len(bs) == 0 { + return false + } p.Metadata = make(metadata, len(bs)) if !wire_chop_slice(p.Metadata[:], &bs) { return false