From 51026d762ef9958353327f0180770b3e60e1555c Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 14 Jan 2019 18:24:35 +0000 Subject: [PATCH] Make session firewall thread-safe for config updates --- src/yggdrasil/session.go | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/yggdrasil/session.go b/src/yggdrasil/session.go index 3c8e013..3cd1cf7 100644 --- a/src/yggdrasil/session.go +++ b/src/yggdrasil/session.go @@ -7,6 +7,7 @@ package yggdrasil import ( "bytes" "encoding/hex" + "sync" "time" "github.com/yggdrasil-network/yggdrasil-go/src/address" @@ -115,6 +116,7 @@ type sessions struct { addrToPerm map[address.Address]*crypto.BoxPubKey subnetToPerm map[address.Subnet]*crypto.BoxPubKey // Options from the session firewall + sessionFirewallMutex sync.RWMutex sessionFirewallEnabled bool sessionFirewallAllowsDirect bool sessionFirewallAllowsRemote bool @@ -157,12 +159,16 @@ func (ss *sessions) init(core *Core) { // Enable or disable the session firewall func (ss *sessions) setSessionFirewallState(enabled bool) { + ss.sessionFirewallMutex.Lock() + defer ss.sessionFirewallMutex.Unlock() ss.sessionFirewallEnabled = enabled } // Set the session firewall defaults (first parameter is whether to allow // sessions from direct peers, second is whether to allow from remote nodes). func (ss *sessions) setSessionFirewallDefaults(allowsDirect bool, allowsRemote bool, alwaysAllowsOutbound bool) { + ss.sessionFirewallMutex.Lock() + defer ss.sessionFirewallMutex.Unlock() ss.sessionFirewallAllowsDirect = allowsDirect ss.sessionFirewallAllowsRemote = allowsRemote ss.sessionFirewallAlwaysAllowsOutbound = alwaysAllowsOutbound @@ -170,17 +176,24 @@ func (ss *sessions) setSessionFirewallDefaults(allowsDirect bool, allowsRemote b // Set the session firewall whitelist - nodes always allowed to open sessions. func (ss *sessions) setSessionFirewallWhitelist(whitelist []string) { + ss.sessionFirewallMutex.Lock() + defer ss.sessionFirewallMutex.Unlock() ss.sessionFirewallWhitelist = whitelist } // Set the session firewall blacklist - nodes never allowed to open sessions. func (ss *sessions) setSessionFirewallBlacklist(blacklist []string) { + ss.sessionFirewallMutex.Lock() + defer ss.sessionFirewallMutex.Unlock() ss.sessionFirewallBlacklist = blacklist } // Determines whether the session with a given publickey is allowed based on // session firewall rules. func (ss *sessions) isSessionAllowed(pubkey *crypto.BoxPubKey, initiator bool) bool { + ss.sessionFirewallMutex.RLock() + defer ss.sessionFirewallMutex.RUnlock() + // Allow by default if the session firewall is disabled if !ss.sessionFirewallEnabled { return true @@ -286,10 +299,8 @@ func (ss *sessions) getByTheirSubnet(snet *address.Subnet) (*sessionInfo, bool) // 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). func (ss *sessions) createSession(theirPermKey *crypto.BoxPubKey) *sessionInfo { - if ss.sessionFirewallEnabled { - if !ss.isSessionAllowed(theirPermKey, true) { - return nil - } + if !ss.isSessionAllowed(theirPermKey, true) { + return nil } sinfo := sessionInfo{} sinfo.core = ss.core @@ -465,11 +476,14 @@ func (ss *sessions) handlePing(ping *sessionPing) { // Get the corresponding session (or create a new session) sinfo, isIn := ss.getByTheirPerm(&ping.SendPermPub) // Check the session firewall + ss.sessionFirewallMutex.RLock() if !isIn && ss.sessionFirewallEnabled { if !ss.isSessionAllowed(&ping.SendPermPub, false) { + ss.sessionFirewallMutex.RUnlock() return } } + ss.sessionFirewallMutex.RUnlock() if !isIn || sinfo.timedout() { if isIn { sinfo.close()