Remove config.NodeState (hot reconfig is no longer supported)

This commit is contained in:
Neil Alexander 2021-06-02 14:19:32 +01:00
parent 978124dbb1
commit 166336a418
10 changed files with 63 additions and 102 deletions

View File

@ -35,7 +35,7 @@ import (
type node struct { type node struct {
core core.Core core core.Core
state *config.NodeState config *config.NodeConfig
tuntap module.Module // tuntap.TunAdapter tuntap module.Module // tuntap.TunAdapter
multicast module.Module // multicast.Multicast multicast module.Module // multicast.Multicast
admin module.Module // admin.AdminSocket admin module.Module // admin.AdminSocket
@ -272,8 +272,7 @@ func main() {
n := node{} n := node{}
// Now start Yggdrasil - this starts the DHT, router, switch and other core // Now start Yggdrasil - this starts the DHT, router, switch and other core
// components needed for Yggdrasil to operate // components needed for Yggdrasil to operate
n.state, err = n.core.Start(cfg, logger) if err = n.core.Start(cfg, logger); err != nil {
if err != nil {
logger.Errorln("An error occurred during startup") logger.Errorln("An error occurred during startup")
panic(err) panic(err)
} }
@ -284,19 +283,19 @@ func main() {
n.tuntap = &tuntap.TunAdapter{} n.tuntap = &tuntap.TunAdapter{}
n.tuntap.(*tuntap.TunAdapter).SetSessionGatekeeper(n.sessionFirewall) n.tuntap.(*tuntap.TunAdapter).SetSessionGatekeeper(n.sessionFirewall)
// Start the admin socket // Start the admin socket
n.admin.Init(&n.core, n.state, logger, nil) n.admin.Init(&n.core, cfg, logger, nil)
if err := n.admin.Start(); err != nil { if err := n.admin.Start(); err != nil {
logger.Errorln("An error occurred starting admin socket:", err) logger.Errorln("An error occurred starting admin socket:", err)
} }
n.admin.SetupAdminHandlers(n.admin.(*admin.AdminSocket)) n.admin.SetupAdminHandlers(n.admin.(*admin.AdminSocket))
// Start the multicast interface // Start the multicast interface
n.multicast.Init(&n.core, n.state, logger, nil) n.multicast.Init(&n.core, cfg, logger, nil)
if err := n.multicast.Start(); err != nil { if err := n.multicast.Start(); err != nil {
logger.Errorln("An error occurred starting multicast:", err) logger.Errorln("An error occurred starting multicast:", err)
} }
n.multicast.SetupAdminHandlers(n.admin.(*admin.AdminSocket)) n.multicast.SetupAdminHandlers(n.admin.(*admin.AdminSocket))
// Start the TUN/TAP interface // Start the TUN/TAP interface
n.tuntap.Init(&n.core, n.state, logger, nil) n.tuntap.Init(&n.core, cfg, logger, nil)
if err := n.tuntap.Start(); err != nil { if err := n.tuntap.Start(); err != nil {
logger.Errorln("An error occurred starting TUN/TAP:", err) logger.Errorln("An error occurred starting TUN/TAP:", err)
} }
@ -324,16 +323,16 @@ func (n *node) shutdown() {
} }
func (n *node) sessionFirewall(pubkey ed25519.PublicKey, initiator bool) bool { func (n *node) sessionFirewall(pubkey ed25519.PublicKey, initiator bool) bool {
n.state.Mutex.RLock() n.config.RLock()
defer n.state.Mutex.RUnlock() defer n.config.RUnlock()
// Allow by default if the session firewall is disabled // Allow by default if the session firewall is disabled
if !n.state.Current.SessionFirewall.Enable { if !n.config.SessionFirewall.Enable {
return true return true
} }
// Reject blacklisted nodes // Reject blacklisted nodes
for _, b := range n.state.Current.SessionFirewall.BlacklistPublicKeys { for _, b := range n.config.SessionFirewall.BlacklistPublicKeys {
key, err := hex.DecodeString(b) key, err := hex.DecodeString(b)
if err == nil { if err == nil {
if bytes.Equal(key, pubkey) { if bytes.Equal(key, pubkey) {
@ -343,7 +342,7 @@ func (n *node) sessionFirewall(pubkey ed25519.PublicKey, initiator bool) bool {
} }
// Allow whitelisted nodes // Allow whitelisted nodes
for _, b := range n.state.Current.SessionFirewall.WhitelistPublicKeys { for _, b := range n.config.SessionFirewall.WhitelistPublicKeys {
key, err := hex.DecodeString(b) key, err := hex.DecodeString(b)
if err == nil { if err == nil {
if bytes.Equal(key, pubkey) { if bytes.Equal(key, pubkey) {
@ -353,7 +352,7 @@ func (n *node) sessionFirewall(pubkey ed25519.PublicKey, initiator bool) bool {
} }
// Allow outbound sessions if appropriate // Allow outbound sessions if appropriate
if n.state.Current.SessionFirewall.AlwaysAllowOutbound { if n.config.SessionFirewall.AlwaysAllowOutbound {
if initiator { if initiator {
return true return true
} }
@ -369,12 +368,12 @@ func (n *node) sessionFirewall(pubkey ed25519.PublicKey, initiator bool) bool {
} }
// Allow direct peers if appropriate // Allow direct peers if appropriate
if n.state.Current.SessionFirewall.AllowFromDirect && isDirectPeer { if n.config.SessionFirewall.AllowFromDirect && isDirectPeer {
return true return true
} }
// Allow remote nodes if appropriate // Allow remote nodes if appropriate
if n.state.Current.SessionFirewall.AllowFromRemote && !isDirectPeer { if n.config.SessionFirewall.AllowFromRemote && !isDirectPeer {
return true return true
} }

View File

@ -63,12 +63,13 @@ func (a *AdminSocket) AddHandler(name string, args []string, handlerfunc func(js
} }
// Init runs the initial admin setup. // Init runs the initial admin setup.
func (a *AdminSocket) Init(c *core.Core, state *config.NodeState, log *log.Logger, options interface{}) error { func (a *AdminSocket) Init(c *core.Core, nc *config.NodeConfig, log *log.Logger, options interface{}) error {
a.core = c a.core = c
a.log = log a.log = log
a.handlers = make(map[string]handler) a.handlers = make(map[string]handler)
current := state.GetCurrent() nc.RLock()
a.listenaddr = current.AdminListen a.listenaddr = nc.AdminListen
nc.RUnlock()
a.done = make(chan struct{}) a.done = make(chan struct{})
close(a.done) // Start in a done / not-started state close(a.done) // Start in a done / not-started state
_ = a.AddHandler("list", []string{}, func(_ json.RawMessage) (interface{}, error) { _ = a.AddHandler("list", []string{}, func(_ json.RawMessage) (interface{}, error) {

View File

@ -24,41 +24,11 @@ import (
"github.com/yggdrasil-network/yggdrasil-go/src/defaults" "github.com/yggdrasil-network/yggdrasil-go/src/defaults"
) )
// NodeState represents the active and previous configuration of an Yggdrasil
// node. A NodeState object is returned when starting an Yggdrasil node. Note
// that this structure and related functions are likely to disappear soon.
type NodeState struct {
Current NodeConfig
Previous NodeConfig
Mutex sync.RWMutex
}
// Current returns the active node configuration.
func (s *NodeState) GetCurrent() NodeConfig {
s.Mutex.RLock()
defer s.Mutex.RUnlock()
return s.Current
}
// Previous returns the previous node configuration.
func (s *NodeState) GetPrevious() NodeConfig {
s.Mutex.RLock()
defer s.Mutex.RUnlock()
return s.Previous
}
// Replace the node configuration with new configuration.
func (s *NodeState) Replace(n NodeConfig) {
s.Mutex.Lock()
defer s.Mutex.Unlock()
s.Previous = s.Current
s.Current = n
}
// NodeConfig is the main configuration structure, containing configuration // NodeConfig is the main configuration structure, containing configuration
// options that are necessary for an Yggdrasil node to run. You will need to // options that are necessary for an Yggdrasil node to run. You will need to
// supply one of these structs to the Yggdrasil core when starting a node. // supply one of these structs to the Yggdrasil core when starting a node.
type NodeConfig struct { type NodeConfig struct {
sync.RWMutex
Peers []string `comment:"List of connection strings for outbound peer connections in URI format,\ne.g. tcp://a.b.c.d:e or socks://a.b.c.d:e/f.g.h.i:j. These connections\nwill obey the operating system routing table, therefore you should\nuse this section when you may connect via different interfaces."` Peers []string `comment:"List of connection strings for outbound peer connections in URI format,\ne.g. tcp://a.b.c.d:e or socks://a.b.c.d:e/f.g.h.i:j. These connections\nwill obey the operating system routing table, therefore you should\nuse this section when you may connect via different interfaces."`
InterfacePeers map[string][]string `comment:"List of connection strings for outbound peer connections in URI format,\narranged by source interface, e.g. { \"eth0\": [ tcp://a.b.c.d:e ] }.\nNote that SOCKS peerings will NOT be affected by this option and should\ngo in the \"Peers\" section instead."` InterfacePeers map[string][]string `comment:"List of connection strings for outbound peer connections in URI format,\narranged by source interface, e.g. { \"eth0\": [ tcp://a.b.c.d:e ] }.\nNote that SOCKS peerings will NOT be affected by this option and should\ngo in the \"Peers\" section instead."`
Listen []string `comment:"Listen addresses for incoming connections. You will need to add\nlisteners in order to accept incoming peerings from non-local nodes.\nMulticast peer discovery will work regardless of any listeners set\nhere. Each listener should be specified in URI format as above, e.g.\ntcp://0.0.0.0:0 or tcp://[::]:0 to listen on all interfaces."` Listen []string `comment:"Listen addresses for incoming connections. You will need to add\nlisteners in order to accept incoming peerings from non-local nodes.\nMulticast peer discovery will work regardless of any listeners set\nhere. Each listener should be specified in URI format as above, e.g.\ntcp://0.0.0.0:0 or tcp://[::]:0 to listen on all interfaces."`

View File

@ -25,7 +25,7 @@ type Core struct {
// guarantee that it will be covered by the mutex // guarantee that it will be covered by the mutex
phony.Inbox phony.Inbox
*iw.PacketConn *iw.PacketConn
config config.NodeState // Config config *config.NodeConfig // Config
secret ed25519.PrivateKey secret ed25519.PrivateKey
public ed25519.PublicKey public ed25519.PublicKey
links links links links
@ -42,9 +42,9 @@ func (c *Core) _init() error {
c.log = log.New(ioutil.Discard, "", 0) c.log = log.New(ioutil.Discard, "", 0)
} }
current := c.config.GetCurrent() c.config.RLock()
sigPriv, err := hex.DecodeString(c.config.PrivateKey)
sigPriv, err := hex.DecodeString(current.PrivateKey) c.config.RUnlock()
if err != nil { if err != nil {
return err return err
} }
@ -64,11 +64,11 @@ func (c *Core) _init() error {
// configure them. The loop ensures that disconnected peers will eventually // configure them. The loop ensures that disconnected peers will eventually
// be reconnected with. // be reconnected with.
func (c *Core) _addPeerLoop() { func (c *Core) _addPeerLoop() {
// Get the peers from the config - these could change! c.config.RLock()
current := c.config.GetCurrent() defer c.config.RUnlock()
// Add peers from the Peers section // Add peers from the Peers section
for _, peer := range current.Peers { for _, peer := range c.config.Peers {
go func(peer string, intf string) { go func(peer string, intf string) {
u, err := url.Parse(peer) u, err := url.Parse(peer)
if err != nil { if err != nil {
@ -81,7 +81,7 @@ func (c *Core) _addPeerLoop() {
} }
// Add peers from the InterfacePeers section // Add peers from the InterfacePeers section
for intf, intfpeers := range current.InterfacePeers { for intf, intfpeers := range c.config.InterfacePeers {
for _, peer := range intfpeers { for _, peer := range intfpeers {
go func(peer string, intf string) { go func(peer string, intf string) {
u, err := url.Parse(peer) u, err := url.Parse(peer)
@ -107,21 +107,17 @@ func (c *Core) _addPeerLoop() {
// TCP and UDP sockets, a multicast discovery socket, an admin socket, router, // TCP and UDP sockets, a multicast discovery socket, an admin socket, router,
// switch and DHT node. A config.NodeState is returned which contains both the // switch and DHT node. A config.NodeState is returned which contains both the
// current and previous configurations (from reconfigures). // current and previous configurations (from reconfigures).
func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) (conf *config.NodeState, err error) { func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) (err error) {
phony.Block(c, func() { phony.Block(c, func() {
conf, err = c._start(nc, log) err = c._start(nc, log)
}) })
return return
} }
// This function is unsafe and should only be ran by the core actor. // This function is unsafe and should only be ran by the core actor.
func (c *Core) _start(nc *config.NodeConfig, log *log.Logger) (*config.NodeState, error) { func (c *Core) _start(nc *config.NodeConfig, log *log.Logger) error {
c.log = log c.log = log
c.config = nc
c.config = config.NodeState{
Current: *nc,
Previous: *nc,
}
if name := version.BuildName(); name != "unknown" { if name := version.BuildName(); name != "unknown" {
c.log.Infoln("Build name:", name) c.log.Infoln("Build name:", name)
@ -133,30 +129,20 @@ func (c *Core) _start(nc *config.NodeConfig, log *log.Logger) (*config.NodeState
c.log.Infoln("Starting up...") c.log.Infoln("Starting up...")
if err := c._init(); err != nil { if err := c._init(); err != nil {
c.log.Errorln("Failed to initialize core") c.log.Errorln("Failed to initialize core")
return nil, err return err
} }
if err := c.links.init(c); err != nil { if err := c.links.init(c); err != nil {
c.log.Errorln("Failed to start link interfaces") c.log.Errorln("Failed to start link interfaces")
return nil, err return err
} }
//if err := c.switchTable.start(); err != nil {
// c.log.Errorln("Failed to start switch")
// return nil, err
//}
//if err := c.router.start(); err != nil {
// c.log.Errorln("Failed to start router")
// return nil, err
//}
c.addPeerTimer = time.AfterFunc(0, func() { c.addPeerTimer = time.AfterFunc(0, func() {
c.Act(nil, c._addPeerLoop) c.Act(nil, c._addPeerLoop)
}) })
c.log.Infoln("Startup complete") c.log.Infoln("Startup complete")
return &c.config, nil return nil
} }
// Stop shuts down the Yggdrasil node. // Stop shuts down the Yggdrasil node.

View File

@ -40,14 +40,12 @@ func GetLoggerWithPrefix(prefix string, verbose bool) *log.Logger {
// Verbosity flag is passed to logger. // Verbosity flag is passed to logger.
func CreateAndConnectTwo(t testing.TB, verbose bool) (nodeA *Core, nodeB *Core) { func CreateAndConnectTwo(t testing.TB, verbose bool) (nodeA *Core, nodeB *Core) {
nodeA = new(Core) nodeA = new(Core)
_, err := nodeA.Start(GenerateConfig(), GetLoggerWithPrefix("A: ", verbose)) if err := nodeA.Start(GenerateConfig(), GetLoggerWithPrefix("A: ", verbose)); err != nil {
if err != nil {
t.Fatal(err) t.Fatal(err)
} }
nodeB = new(Core) nodeB = new(Core)
_, err = nodeB.Start(GenerateConfig(), GetLoggerWithPrefix("B: ", verbose)) if err := nodeB.Start(GenerateConfig(), GetLoggerWithPrefix("B: ", verbose)); err != nil {
if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -188,7 +188,9 @@ func (intf *link) handler() (chan struct{}, error) {
} }
} }
// Check if we're authorized to connect to this key / IP // Check if we're authorized to connect to this key / IP
allowed := intf.links.core.config.GetCurrent().AllowedPublicKeys intf.links.core.config.RLock()
allowed := intf.links.core.config.AllowedPublicKeys
intf.links.core.config.RUnlock()
isallowed := len(allowed) == 0 isallowed := len(allowed) == 0
for _, k := range allowed { for _, k := range allowed {
if k == hex.EncodeToString(meta.key) { // TODO: this is yuck if k == hex.EncodeToString(meta.key) { // TODO: this is yuck

View File

@ -104,9 +104,9 @@ func (t *tcp) init(l *links) error {
t.listeners = make(map[string]*TcpListener) t.listeners = make(map[string]*TcpListener)
t.mutex.Unlock() t.mutex.Unlock()
t.links.core.config.Mutex.RLock() t.links.core.config.RLock()
defer t.links.core.config.Mutex.RUnlock() defer t.links.core.config.RUnlock()
for _, listenaddr := range t.links.core.config.Current.Listen { for _, listenaddr := range t.links.core.config.Listen {
u, err := url.Parse(listenaddr) u, err := url.Parse(listenaddr)
if err != nil { if err != nil {
t.links.core.log.Errorln("Failed to parse listener: listener", listenaddr, "is not correctly formatted, ignoring") t.links.core.log.Errorln("Failed to parse listener: listener", listenaddr, "is not correctly formatted, ignoring")

View File

@ -11,7 +11,7 @@ import (
// Module is an interface that defines which functions must be supported by a // Module is an interface that defines which functions must be supported by a
// given Yggdrasil module. // given Yggdrasil module.
type Module interface { type Module interface {
Init(core *core.Core, state *config.NodeState, log *log.Logger, options interface{}) error Init(core *core.Core, state *config.NodeConfig, log *log.Logger, options interface{}) error
Start() error Start() error
Stop() error Stop() error
SetupAdminHandlers(a *admin.AdminSocket) SetupAdminHandlers(a *admin.AdminSocket)

View File

@ -23,7 +23,7 @@ import (
type Multicast struct { type Multicast struct {
phony.Inbox phony.Inbox
core *core.Core core *core.Core
config *config.NodeState config *config.NodeConfig
log *log.Logger log *log.Logger
sock *ipv6.PacketConn sock *ipv6.PacketConn
groupAddr string groupAddr string
@ -45,14 +45,13 @@ type listenerInfo struct {
} }
// Init prepares the multicast interface for use. // Init prepares the multicast interface for use.
func (m *Multicast) Init(core *core.Core, state *config.NodeState, log *log.Logger, options interface{}) error { func (m *Multicast) Init(core *core.Core, nc *config.NodeConfig, log *log.Logger, options interface{}) error {
m.core = core m.core = core
m.config = state m.config = nc
m.log = log m.log = log
m.listeners = make(map[string]*listenerInfo) m.listeners = make(map[string]*listenerInfo)
m._interfaces = make(map[string]interfaceInfo) m._interfaces = make(map[string]interfaceInfo)
current := m.config.GetCurrent() m.listenPort = m.config.LinkLocalTCPPort
m.listenPort = current.LinkLocalTCPPort
m.groupAddr = "[ff02::114]:9001" m.groupAddr = "[ff02::114]:9001"
return nil return nil
} }
@ -73,7 +72,9 @@ func (m *Multicast) _start() error {
if m.isOpen { if m.isOpen {
return fmt.Errorf("multicast module is already started") return fmt.Errorf("multicast module is already started")
} }
if len(m.config.GetCurrent().MulticastInterfaces) == 0 { m.config.RLock()
defer m.config.RUnlock()
if len(m.config.MulticastInterfaces) == 0 {
return nil return nil
} }
m.log.Infoln("Starting multicast module") m.log.Infoln("Starting multicast module")
@ -161,8 +162,7 @@ func (m *Multicast) Interfaces() map[string]net.Interface {
func (m *Multicast) getAllowedInterfaces() map[string]net.Interface { func (m *Multicast) getAllowedInterfaces() map[string]net.Interface {
interfaces := make(map[string]net.Interface) interfaces := make(map[string]net.Interface)
// Get interface expressions from config // Get interface expressions from config
current := m.config.GetCurrent() exprs := m.config.MulticastInterfaces
exprs := current.MulticastInterfaces
// Ask the system for network interfaces // Ask the system for network interfaces
allifaces, err := net.Interfaces() allifaces, err := net.Interfaces()
if err != nil { if err != nil {

View File

@ -35,7 +35,7 @@ type MTU uint16
type TunAdapter struct { type TunAdapter struct {
core *core.Core core *core.Core
store keyStore store keyStore
config *config.NodeState config *config.NodeConfig
log *log.Logger log *log.Logger
addr address.Address addr address.Address
subnet address.Subnet subnet address.Subnet
@ -103,13 +103,17 @@ func MaximumMTU() uint64 {
// Init initialises the TUN module. You must have acquired a Listener from // Init initialises the TUN module. You must have acquired a Listener from
// the Yggdrasil core before this point and it must not be in use elsewhere. // the Yggdrasil core before this point and it must not be in use elsewhere.
func (tun *TunAdapter) Init(core *core.Core, config *config.NodeState, log *log.Logger, options interface{}) error { func (tun *TunAdapter) Init(core *core.Core, config *config.NodeConfig, log *log.Logger, options interface{}) error {
tun.core = core tun.core = core
tun.store.init(tun) tun.store.init(tun)
tun.config = config tun.config = config
tun.log = log tun.log = log
tun.proto.init(tun) tun.proto.init(tun)
tun.proto.nodeinfo.setNodeInfo(config.Current.NodeInfo, config.Current.NodeInfoPrivacy) tun.config.RLock()
if err := tun.proto.nodeinfo.setNodeInfo(tun.config.NodeInfo, tun.config.NodeInfoPrivacy); err != nil {
return fmt.Errorf("tun.proto.nodeinfo.setNodeInfo: %w", err)
}
tun.config.RUnlock()
if err := tun.core.SetOutOfBandHandler(tun.oobHandler); err != nil { if err := tun.core.SetOutOfBandHandler(tun.oobHandler); err != nil {
return fmt.Errorf("tun.core.SetOutOfBandHander: %w", err) return fmt.Errorf("tun.core.SetOutOfBandHander: %w", err)
} }
@ -130,7 +134,8 @@ func (tun *TunAdapter) _start() error {
if tun.isOpen { if tun.isOpen {
return errors.New("TUN module is already started") return errors.New("TUN module is already started")
} }
current := tun.config.GetCurrent() tun.config.RLock()
defer tun.config.RUnlock()
if tun.config == nil { if tun.config == nil {
return errors.New("no configuration available to TUN") return errors.New("no configuration available to TUN")
} }
@ -139,21 +144,21 @@ func (tun *TunAdapter) _start() error {
tun.addr = *address.AddrForKey(pk) tun.addr = *address.AddrForKey(pk)
tun.subnet = *address.SubnetForKey(pk) tun.subnet = *address.SubnetForKey(pk)
addr := fmt.Sprintf("%s/%d", net.IP(tun.addr[:]).String(), 8*len(address.GetPrefix())-1) addr := fmt.Sprintf("%s/%d", net.IP(tun.addr[:]).String(), 8*len(address.GetPrefix())-1)
if current.IfName == "none" || current.IfName == "dummy" { if tun.config.IfName == "none" || tun.config.IfName == "dummy" {
tun.log.Debugln("Not starting TUN as ifname is none or dummy") tun.log.Debugln("Not starting TUN as ifname is none or dummy")
tun.isEnabled = false tun.isEnabled = false
go tun.write() go tun.write()
return nil return nil
} }
mtu := current.IfMTU mtu := tun.config.IfMTU
if tun.maxSessionMTU() < mtu { if tun.maxSessionMTU() < mtu {
mtu = tun.maxSessionMTU() mtu = tun.maxSessionMTU()
} }
if err := tun.setup(current.IfName, addr, mtu); err != nil { if err := tun.setup(tun.config.IfName, addr, mtu); err != nil {
return err return err
} }
if tun.MTU() != mtu { if tun.MTU() != mtu {
tun.log.Warnf("Warning: Interface MTU %d automatically adjusted to %d (supported range is 1280-%d)", current.IfMTU, tun.MTU(), MaximumMTU()) tun.log.Warnf("Warning: Interface MTU %d automatically adjusted to %d (supported range is 1280-%d)", tun.config.IfMTU, tun.MTU(), MaximumMTU())
} }
tun.isOpen = true tun.isOpen = true
tun.isEnabled = true tun.isEnabled = true