5
0
mirror of https://github.com/cwinfo/yggdrasil-go.git synced 2024-12-27 20:55:41 +00:00
yggdrasil-go/src/yggdrasil/core.go

208 lines
5.8 KiB
Go
Raw Normal View History

2017-12-29 04:16:20 +00:00
package yggdrasil
2018-06-12 22:50:08 +00:00
import (
"encoding/hex"
"errors"
2018-06-12 22:50:08 +00:00
"io/ioutil"
"time"
2018-06-12 22:50:08 +00:00
"github.com/Arceliar/phony"
"github.com/gologme/log"
2018-12-08 01:56:04 +00:00
"github.com/yggdrasil-network/yggdrasil-go/src/config"
"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
"github.com/yggdrasil-network/yggdrasil-go/src/version"
2018-06-12 22:50:08 +00:00
)
2017-12-29 04:16:20 +00:00
2018-05-27 21:13:37 +00:00
// The Core object represents the Yggdrasil node. You should create a Core
// object for each Yggdrasil node you plan to run.
2017-12-29 04:16:20 +00:00
type Core struct {
2018-01-04 22:37:51 +00:00
// This is the main data structure that holds everything else for a node
// We're going to keep our own copy of the provided config - that way we can
// guarantee that it will be covered by the mutex
2019-08-28 18:31:04 +00:00
phony.Inbox
config config.NodeState // Config
boxPub crypto.BoxPubKey
boxPriv crypto.BoxPrivKey
sigPub crypto.SigPubKey
sigPriv crypto.SigPrivKey
switchTable switchTable
peers peers
router router
link link
log *log.Logger
addPeerTimer *time.Timer
2017-12-29 04:16:20 +00:00
}
2019-08-28 18:53:52 +00:00
func (c *Core) _init() error {
2018-01-04 22:37:51 +00:00
// TODO separate init and start functions
// Init sets up structs
// Start launches goroutines that depend on structs being set up
// This is pretty much required to completely avoid race conditions
2018-05-27 21:13:37 +00:00
if c.log == nil {
c.log = log.New(ioutil.Discard, "", 0)
}
2018-12-29 19:14:26 +00:00
current := c.config.GetCurrent()
boxPrivHex, err := hex.DecodeString(current.EncryptionPrivateKey)
2018-12-29 19:14:26 +00:00
if err != nil {
return err
}
if len(boxPrivHex) < crypto.BoxPrivKeyLen {
return errors.New("EncryptionPrivateKey is incorrect length")
2018-12-29 19:14:26 +00:00
}
sigPrivHex, err := hex.DecodeString(current.SigningPrivateKey)
2018-12-29 19:14:26 +00:00
if err != nil {
return err
}
if len(sigPrivHex) < crypto.SigPrivKeyLen {
return errors.New("SigningPrivateKey is incorrect length")
}
2018-12-29 19:14:26 +00:00
copy(c.boxPriv[:], boxPrivHex)
copy(c.sigPriv[:], sigPrivHex)
boxPub, sigPub := c.boxPriv.Public(), c.sigPriv.Public()
copy(c.boxPub[:], boxPub[:])
copy(c.sigPub[:], sigPub[:])
if bp := hex.EncodeToString(c.boxPub[:]); current.EncryptionPublicKey != bp {
c.log.Warnln("EncryptionPublicKey in config is incorrect, should be", bp)
}
if sp := hex.EncodeToString(c.sigPub[:]); current.SigningPublicKey != sp {
c.log.Warnln("SigningPublicKey in config is incorrect, should be", sp)
}
2018-01-04 22:37:51 +00:00
c.peers.init(c)
c.router.init(c)
2018-12-29 19:14:26 +00:00
c.switchTable.init(c) // TODO move before peers? before router?
return nil
2017-12-29 04:16:20 +00:00
}
// If any static peers were provided in the configuration above then we should
// configure them. The loop ensures that disconnected peers will eventually
// be reconnected with.
2019-08-28 18:53:52 +00:00
func (c *Core) _addPeerLoop() {
// Get the peers from the config - these could change!
current := c.config.GetCurrent()
2019-08-28 18:53:52 +00:00
// Add peers from the Peers section
for _, peer := range current.Peers {
2019-09-18 15:15:33 +00:00
go func(peer, intf string) {
2019-09-18 23:46:03 +00:00
if err := c.CallPeer(peer, intf); err != nil {
c.log.Errorln("Failed to add peer:", err)
}
2019-09-18 15:15:33 +00:00
}(peer, "") // TODO: this should be acted and not in a goroutine?
2019-08-28 18:53:52 +00:00
}
2019-08-28 18:53:52 +00:00
// Add peers from the InterfacePeers section
for intf, intfpeers := range current.InterfacePeers {
for _, peer := range intfpeers {
2019-09-18 15:15:33 +00:00
go func(peer, intf string) {
2019-09-18 23:46:03 +00:00
if err := c.CallPeer(peer, intf); err != nil {
c.log.Errorln("Failed to add peer:", err)
}
2019-09-18 15:15:33 +00:00
}(peer, intf) // TODO: this should be acted and not in a goroutine?
2019-08-28 18:53:52 +00:00
}
}
2019-08-28 18:53:52 +00:00
c.addPeerTimer = time.AfterFunc(time.Minute, func() {
2019-09-18 23:46:03 +00:00
c.Act(nil, c._addPeerLoop)
2019-08-28 18:53:52 +00:00
})
}
// UpdateConfig updates the configuration in Core with the provided
// config.NodeConfig and then signals the various module goroutines to
// reconfigure themselves if needed.
func (c *Core) UpdateConfig(config *config.NodeConfig) {
2019-08-28 18:53:52 +00:00
c.Act(nil, func() {
c.log.Debugln("Reloading node configuration...")
2019-08-28 18:53:52 +00:00
// Replace the active configuration with the supplied one
c.config.Replace(*config)
2019-08-28 18:53:52 +00:00
// Notify the router and switch about the new configuration
c.router.Act(c, c.router.reconfigure)
c.switchTable.Act(c, c.switchTable.reconfigure)
})
}
// Start starts up Yggdrasil using the provided config.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 DHT node. A config.NodeState is returned which contains both the
// current and previous configurations (from reconfigures).
2019-08-28 18:53:52 +00:00
func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) (conf *config.NodeState, err error) {
phony.Block(c, func() {
conf, err = c._start(nc, log)
})
return
}
// 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) {
2018-05-27 21:13:37 +00:00
c.log = log
c.config = config.NodeState{
Current: *nc,
Previous: *nc,
}
if name := version.BuildName(); name != "unknown" {
c.log.Infoln("Build name:", name)
}
if version := version.BuildVersion(); version != "unknown" {
c.log.Infoln("Build version:", version)
}
c.log.Infoln("Starting up...")
if err := c._init(); err != nil {
c.log.Errorln("Failed to initialize core")
return nil, err
}
2018-05-27 21:13:37 +00:00
if err := c.link.init(c); err != nil {
c.log.Errorln("Failed to start link interfaces")
2019-03-28 19:09:19 +00:00
return nil, err
}
if err := c.switchTable.start(); err != nil {
c.log.Errorln("Failed to start switch")
2019-03-28 19:09:19 +00:00
return nil, err
}
2018-05-27 21:13:37 +00:00
if err := c.router.start(); err != nil {
c.log.Errorln("Failed to start router")
2019-03-28 19:09:19 +00:00
return nil, err
2018-05-27 21:13:37 +00:00
}
2019-09-18 14:34:26 +00:00
c.Act(c, c._addPeerLoop)
c.log.Infoln("Startup complete")
2019-03-28 19:09:19 +00:00
return &c.config, nil
2018-05-27 21:13:37 +00:00
}
// Stop shuts down the Yggdrasil node.
2018-05-27 21:13:37 +00:00
func (c *Core) Stop() {
2019-08-28 18:53:52 +00:00
phony.Block(c, c._stop)
}
// This function is unsafe and should only be ran by the core actor.
func (c *Core) _stop() {
c.log.Infoln("Stopping...")
2019-09-18 14:34:26 +00:00
if c.addPeerTimer != nil {
c.addPeerTimer.Stop()
}
2019-09-18 15:32:22 +00:00
c.link.stop()
2020-03-29 05:48:41 +00:00
/* FIXME this deadlocks, need a waitgroup or something to coordinate shutdown
for _, peer := range c.GetPeers() {
c.DisconnectPeer(peer.Port)
}
2020-03-29 05:48:41 +00:00
*/
2019-09-18 15:32:22 +00:00
c.log.Infoln("Stopped")
2018-05-27 21:13:37 +00:00
}