2017-12-29 04:16:20 +00:00
|
|
|
package yggdrasil
|
|
|
|
|
2018-06-12 22:50:08 +00:00
|
|
|
import (
|
|
|
|
"encoding/hex"
|
2019-04-01 18:59:50 +00:00
|
|
|
"errors"
|
2018-06-12 22:50:08 +00:00
|
|
|
"io/ioutil"
|
|
|
|
"net"
|
2019-01-14 17:21:15 +00:00
|
|
|
"time"
|
2018-06-12 22:50:08 +00:00
|
|
|
|
2019-01-27 13:31:43 +00:00
|
|
|
"github.com/gologme/log"
|
|
|
|
|
2018-12-15 00:30:36 +00:00
|
|
|
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
2018-12-08 01:56:04 +00:00
|
|
|
"github.com/yggdrasil-network/yggdrasil-go/src/config"
|
2018-12-15 00:30:36 +00:00
|
|
|
"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
|
2018-06-12 22:50:08 +00:00
|
|
|
)
|
2017-12-29 04:16:20 +00:00
|
|
|
|
2018-12-07 22:20:11 +00:00
|
|
|
var buildName string
|
|
|
|
var buildVersion string
|
|
|
|
|
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
|
2018-12-29 18:51:51 +00:00
|
|
|
// 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-03-28 00:30:25 +00:00
|
|
|
config config.NodeState // Config
|
2018-12-29 19:14:26 +00:00
|
|
|
boxPub crypto.BoxPubKey
|
|
|
|
boxPriv crypto.BoxPrivKey
|
|
|
|
sigPub crypto.SigPubKey
|
|
|
|
sigPriv crypto.SigPrivKey
|
2018-01-04 22:37:51 +00:00
|
|
|
switchTable switchTable
|
|
|
|
peers peers
|
|
|
|
sessions sessions
|
|
|
|
router router
|
|
|
|
dht dht
|
2018-01-21 00:17:15 +00:00
|
|
|
admin admin
|
2018-01-04 22:37:51 +00:00
|
|
|
searches searches
|
2019-03-28 18:03:14 +00:00
|
|
|
link link
|
|
|
|
log *log.Logger
|
2017-12-29 04:16:20 +00:00
|
|
|
}
|
|
|
|
|
2018-12-29 19:14:26 +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
|
2018-01-26 23:30:51 +00:00
|
|
|
// 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
|
|
|
|
2019-03-28 00:30:25 +00:00
|
|
|
current, _ := c.config.Get()
|
|
|
|
|
|
|
|
boxPubHex, err := hex.DecodeString(current.EncryptionPublicKey)
|
2018-12-29 19:14:26 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-03-28 00:30:25 +00:00
|
|
|
boxPrivHex, err := hex.DecodeString(current.EncryptionPrivateKey)
|
2018-12-29 19:14:26 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-03-28 00:30:25 +00:00
|
|
|
sigPubHex, err := hex.DecodeString(current.SigningPublicKey)
|
2018-12-29 19:14:26 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-03-28 00:30:25 +00:00
|
|
|
sigPrivHex, err := hex.DecodeString(current.SigningPrivateKey)
|
2018-12-29 19:14:26 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
copy(c.boxPub[:], boxPubHex)
|
|
|
|
copy(c.boxPriv[:], boxPrivHex)
|
|
|
|
copy(c.sigPub[:], sigPubHex)
|
|
|
|
copy(c.sigPriv[:], sigPrivHex)
|
|
|
|
|
|
|
|
c.admin.init(c)
|
2018-01-04 22:37:51 +00:00
|
|
|
c.searches.init(c)
|
|
|
|
c.dht.init(c)
|
|
|
|
c.sessions.init(c)
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2019-01-14 17:21:15 +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.
|
|
|
|
func (c *Core) addPeerLoop() {
|
|
|
|
for {
|
2019-03-29 18:05:17 +00:00
|
|
|
// the peers from the config - these could change!
|
2019-03-28 00:30:25 +00:00
|
|
|
current, _ := c.config.Get()
|
2019-01-14 17:21:15 +00:00
|
|
|
|
|
|
|
// Add peers from the Peers section
|
2019-03-28 00:30:25 +00:00
|
|
|
for _, peer := range current.Peers {
|
2019-01-14 17:21:15 +00:00
|
|
|
c.AddPeer(peer, "")
|
|
|
|
time.Sleep(time.Second)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add peers from the InterfacePeers section
|
2019-03-28 00:30:25 +00:00
|
|
|
for intf, intfpeers := range current.InterfacePeers {
|
2019-01-14 17:21:15 +00:00
|
|
|
for _, peer := range intfpeers {
|
|
|
|
c.AddPeer(peer, intf)
|
|
|
|
time.Sleep(time.Second)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sit for a while
|
|
|
|
time.Sleep(time.Minute)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-29 18:05:17 +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.
|
2018-12-29 18:51:51 +00:00
|
|
|
func (c *Core) UpdateConfig(config *config.NodeConfig) {
|
2019-03-01 18:26:52 +00:00
|
|
|
c.log.Infoln("Reloading configuration...")
|
|
|
|
|
2019-03-28 00:30:25 +00:00
|
|
|
c.config.Replace(*config)
|
2018-12-29 18:51:51 +00:00
|
|
|
|
2019-03-01 18:26:52 +00:00
|
|
|
errors := 0
|
|
|
|
|
2018-12-30 12:04:42 +00:00
|
|
|
components := []chan chan error{
|
|
|
|
c.admin.reconfigure,
|
2019-01-14 14:25:52 +00:00
|
|
|
c.searches.reconfigure,
|
|
|
|
c.dht.reconfigure,
|
|
|
|
c.sessions.reconfigure,
|
|
|
|
c.peers.reconfigure,
|
|
|
|
c.router.reconfigure,
|
2019-01-14 18:34:15 +00:00
|
|
|
c.router.cryptokey.reconfigure,
|
2019-01-14 14:25:52 +00:00
|
|
|
c.switchTable.reconfigure,
|
2019-03-04 18:41:32 +00:00
|
|
|
c.link.reconfigure,
|
2018-12-30 12:04:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, component := range components {
|
|
|
|
response := make(chan error)
|
|
|
|
component <- response
|
|
|
|
if err := <-response; err != nil {
|
2019-03-01 18:26:52 +00:00
|
|
|
c.log.Errorln(err)
|
|
|
|
errors++
|
2018-12-30 12:04:42 +00:00
|
|
|
}
|
|
|
|
}
|
2019-03-01 18:26:52 +00:00
|
|
|
|
|
|
|
if errors > 0 {
|
|
|
|
c.log.Warnln(errors, "modules reported errors during configuration reload")
|
|
|
|
} else {
|
|
|
|
c.log.Infoln("Configuration reloaded successfully")
|
|
|
|
}
|
2018-12-29 18:51:51 +00:00
|
|
|
}
|
|
|
|
|
2019-03-29 18:05:17 +00:00
|
|
|
// BuildName gets the current build name. This is usually injected if built
|
2018-12-29 18:51:51 +00:00
|
|
|
// from git, or returns "unknown" otherwise.
|
2019-03-29 18:05:17 +00:00
|
|
|
func BuildName() string {
|
2018-12-07 22:20:11 +00:00
|
|
|
if buildName == "" {
|
|
|
|
return "unknown"
|
|
|
|
}
|
|
|
|
return buildName
|
|
|
|
}
|
|
|
|
|
2019-03-29 18:05:17 +00:00
|
|
|
// BuildVersion gets the current build version. This is usually injected if
|
|
|
|
// built from git, or returns "unknown" otherwise.
|
|
|
|
func BuildVersion() string {
|
2018-12-07 22:20:11 +00:00
|
|
|
if buildVersion == "" {
|
|
|
|
return "unknown"
|
|
|
|
}
|
|
|
|
return buildVersion
|
|
|
|
}
|
|
|
|
|
2019-03-29 18:05:17 +00:00
|
|
|
// 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-03-28 19:09:19 +00:00
|
|
|
func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) (*config.NodeState, error) {
|
2018-05-27 21:13:37 +00:00
|
|
|
c.log = log
|
2018-12-07 22:24:01 +00:00
|
|
|
|
2019-03-28 00:30:25 +00:00
|
|
|
c.config = config.NodeState{
|
|
|
|
Current: *nc,
|
|
|
|
Previous: *nc,
|
|
|
|
}
|
|
|
|
|
2019-03-29 18:05:17 +00:00
|
|
|
if name := BuildName(); name != "unknown" {
|
2019-01-27 13:31:43 +00:00
|
|
|
c.log.Infoln("Build name:", name)
|
2018-12-07 22:24:01 +00:00
|
|
|
}
|
2019-03-29 18:05:17 +00:00
|
|
|
if version := BuildVersion(); version != "unknown" {
|
2019-01-27 13:31:43 +00:00
|
|
|
c.log.Infoln("Build version:", version)
|
2018-12-07 22:24:01 +00:00
|
|
|
}
|
|
|
|
|
2019-01-27 13:31:43 +00:00
|
|
|
c.log.Infoln("Starting up...")
|
2018-05-27 21:13:37 +00:00
|
|
|
|
2018-12-29 19:14:26 +00:00
|
|
|
c.init()
|
2018-05-27 21:13:37 +00:00
|
|
|
|
2019-01-19 12:19:24 +00:00
|
|
|
if err := c.link.init(c); err != nil {
|
2019-01-27 20:54:21 +00:00
|
|
|
c.log.Errorln("Failed to start link interfaces")
|
2019-03-28 19:09:19 +00:00
|
|
|
return nil, err
|
2019-01-19 00:42:53 +00:00
|
|
|
}
|
2019-01-04 17:14:40 +00:00
|
|
|
|
2019-03-28 00:30:25 +00:00
|
|
|
c.config.Mutex.RLock()
|
|
|
|
if c.config.Current.SwitchOptions.MaxTotalQueueSize >= SwitchQueueTotalMinSize {
|
|
|
|
c.switchTable.queueTotalMaxSize = c.config.Current.SwitchOptions.MaxTotalQueueSize
|
2018-12-02 23:24:54 +00:00
|
|
|
}
|
2019-03-28 00:30:25 +00:00
|
|
|
c.config.Mutex.RUnlock()
|
2018-12-02 23:24:54 +00:00
|
|
|
|
2018-06-24 00:08:32 +00:00
|
|
|
if err := c.switchTable.start(); err != nil {
|
2019-01-27 13:31:43 +00:00
|
|
|
c.log.Errorln("Failed to start switch")
|
2019-03-28 19:09:19 +00:00
|
|
|
return nil, err
|
2018-06-24 00:08:32 +00:00
|
|
|
}
|
|
|
|
|
2018-05-27 21:13:37 +00:00
|
|
|
if err := c.router.start(); err != nil {
|
2019-01-27 13:31:43 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
if err := c.admin.start(); err != nil {
|
2019-01-27 13:31:43 +00:00
|
|
|
c.log.Errorln("Failed to start admin socket")
|
2019-03-28 19:09:19 +00:00
|
|
|
return nil, err
|
2018-05-27 21:13:37 +00:00
|
|
|
}
|
|
|
|
|
2019-01-14 17:21:15 +00:00
|
|
|
go c.addPeerLoop()
|
|
|
|
|
2019-01-27 13:31:43 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2019-03-29 18:05:17 +00:00
|
|
|
// Stop shuts down the Yggdrasil node.
|
2018-05-27 21:13:37 +00:00
|
|
|
func (c *Core) Stop() {
|
2019-01-27 13:31:43 +00:00
|
|
|
c.log.Infoln("Stopping...")
|
2018-07-07 19:04:11 +00:00
|
|
|
c.admin.close()
|
2018-05-27 21:13:37 +00:00
|
|
|
}
|
|
|
|
|
2019-04-19 21:57:52 +00:00
|
|
|
// ListenConn returns a listener for Yggdrasil session connections.
|
2019-04-20 15:32:27 +00:00
|
|
|
func (c *Core) ConnListen() (*Listener, error) {
|
2019-04-19 21:57:52 +00:00
|
|
|
c.sessions.listenerMutex.Lock()
|
|
|
|
defer c.sessions.listenerMutex.Unlock()
|
|
|
|
if c.sessions.listener != nil {
|
|
|
|
return nil, errors.New("a listener already exists")
|
|
|
|
}
|
|
|
|
c.sessions.listener = &Listener{
|
2019-04-19 22:04:09 +00:00
|
|
|
core: c,
|
2019-04-19 21:57:52 +00:00
|
|
|
conn: make(chan *Conn),
|
|
|
|
close: make(chan interface{}),
|
|
|
|
}
|
|
|
|
return c.sessions.listener, nil
|
|
|
|
}
|
|
|
|
|
2019-04-20 15:32:27 +00:00
|
|
|
// ConnDialer returns a dialer for Yggdrasil session connections.
|
|
|
|
func (c *Core) ConnDialer() (*Dialer, error) {
|
|
|
|
return &Dialer{
|
|
|
|
core: c,
|
|
|
|
}, nil
|
2019-04-18 23:11:43 +00:00
|
|
|
}
|
|
|
|
|
2019-03-29 18:05:17 +00:00
|
|
|
// ListenTCP starts a new TCP listener. The input URI should match that of the
|
|
|
|
// "Listen" configuration item, e.g.
|
|
|
|
// tcp://a.b.c.d:e
|
2019-03-28 16:13:14 +00:00
|
|
|
func (c *Core) ListenTCP(uri string) (*TcpListener, error) {
|
|
|
|
return c.link.tcp.listen(uri)
|
|
|
|
}
|
|
|
|
|
2019-03-29 18:05:17 +00:00
|
|
|
// NewEncryptionKeys generates a new encryption keypair. The encryption keys are
|
|
|
|
// used to encrypt traffic and to derive the IPv6 address/subnet of the node.
|
2018-12-15 02:49:18 +00:00
|
|
|
func (c *Core) NewEncryptionKeys() (*crypto.BoxPubKey, *crypto.BoxPrivKey) {
|
|
|
|
return crypto.NewBoxKeys()
|
2018-05-27 21:13:37 +00:00
|
|
|
}
|
|
|
|
|
2019-03-29 18:05:17 +00:00
|
|
|
// NewSigningKeys generates a new signing keypair. The signing keys are used to
|
|
|
|
// derive the structure of the spanning tree.
|
2018-12-15 02:49:18 +00:00
|
|
|
func (c *Core) NewSigningKeys() (*crypto.SigPubKey, *crypto.SigPrivKey) {
|
|
|
|
return crypto.NewSigKeys()
|
2018-05-27 21:13:37 +00:00
|
|
|
}
|
|
|
|
|
2019-03-29 18:05:17 +00:00
|
|
|
// NodeID gets the node ID.
|
|
|
|
func (c *Core) NodeID() *crypto.NodeID {
|
2019-03-29 18:24:57 +00:00
|
|
|
return crypto.GetNodeID(&c.boxPub)
|
2017-12-29 04:16:20 +00:00
|
|
|
}
|
|
|
|
|
2019-03-29 18:05:17 +00:00
|
|
|
// TreeID gets the tree ID.
|
|
|
|
func (c *Core) TreeID() *crypto.TreeID {
|
2019-03-29 18:24:57 +00:00
|
|
|
return crypto.GetTreeID(&c.sigPub)
|
2017-12-29 04:16:20 +00:00
|
|
|
}
|
2018-05-21 15:15:31 +00:00
|
|
|
|
2019-04-01 17:02:06 +00:00
|
|
|
// SigPubKey gets the node's signing public key.
|
|
|
|
func (c *Core) SigPubKey() string {
|
|
|
|
return hex.EncodeToString(c.sigPub[:])
|
|
|
|
}
|
|
|
|
|
|
|
|
// BoxPubKey gets the node's encryption public key.
|
|
|
|
func (c *Core) BoxPubKey() string {
|
|
|
|
return hex.EncodeToString(c.boxPub[:])
|
|
|
|
}
|
|
|
|
|
2019-03-29 18:05:17 +00:00
|
|
|
// Address gets the IPv6 address of the Yggdrasil node. This is always a /128
|
|
|
|
// address.
|
|
|
|
func (c *Core) Address() *net.IP {
|
|
|
|
address := net.IP(address.AddrForNodeID(c.NodeID())[:])
|
2018-05-27 21:13:37 +00:00
|
|
|
return &address
|
|
|
|
}
|
|
|
|
|
2019-03-29 18:05:17 +00:00
|
|
|
// Subnet gets the routed IPv6 subnet of the Yggdrasil node. This is always a
|
|
|
|
// /64 subnet.
|
|
|
|
func (c *Core) Subnet() *net.IPNet {
|
|
|
|
subnet := address.SubnetForNodeID(c.NodeID())[:]
|
2018-05-27 21:13:37 +00:00
|
|
|
subnet = append(subnet, 0, 0, 0, 0, 0, 0, 0, 0)
|
2018-06-02 20:21:05 +00:00
|
|
|
return &net.IPNet{IP: subnet, Mask: net.CIDRMask(64, 128)}
|
2018-05-27 21:13:37 +00:00
|
|
|
}
|
|
|
|
|
2019-03-29 18:05:17 +00:00
|
|
|
// RouterAddresses returns the raw address and subnet types as used by the
|
2019-03-28 19:09:19 +00:00
|
|
|
// router
|
2019-03-29 18:05:17 +00:00
|
|
|
func (c *Core) RouterAddresses() (address.Address, address.Subnet) {
|
2019-03-28 19:09:19 +00:00
|
|
|
return c.router.addr, c.router.subnet
|
|
|
|
}
|
|
|
|
|
2019-03-29 18:05:17 +00:00
|
|
|
// NodeInfo gets the currently configured nodeinfo.
|
|
|
|
func (c *Core) NodeInfo() nodeinfoPayload {
|
2019-01-14 19:05:16 +00:00
|
|
|
return c.router.nodeinfo.getNodeInfo()
|
2018-12-12 22:40:49 +00:00
|
|
|
}
|
|
|
|
|
2019-03-29 18:05:17 +00:00
|
|
|
// SetNodeInfo the lcal nodeinfo. Note that nodeinfo can be any value or struct,
|
|
|
|
// it will be serialised into JSON automatically.
|
2018-12-21 09:56:34 +00:00
|
|
|
func (c *Core) SetNodeInfo(nodeinfo interface{}, nodeinfoprivacy bool) {
|
2019-01-14 19:05:16 +00:00
|
|
|
c.router.nodeinfo.setNodeInfo(nodeinfo, nodeinfoprivacy)
|
2018-12-12 22:40:49 +00:00
|
|
|
}
|
|
|
|
|
2019-03-29 18:05:17 +00:00
|
|
|
// SetLogger sets the output logger of the Yggdrasil node after startup. This
|
|
|
|
// may be useful if you want to redirect the output later.
|
2018-05-27 21:13:37 +00:00
|
|
|
func (c *Core) SetLogger(log *log.Logger) {
|
|
|
|
c.log = log
|
|
|
|
}
|
|
|
|
|
2019-03-29 18:05:17 +00:00
|
|
|
// AddPeer adds a peer. This should be specified in the peer URI format, e.g.:
|
|
|
|
// tcp://a.b.c.d:e
|
|
|
|
// socks://a.b.c.d:e/f.g.h.i:j
|
|
|
|
// This adds the peer to the peer list, so that they will be called again if the
|
|
|
|
// connection drops.
|
2018-09-25 15:55:57 +00:00
|
|
|
func (c *Core) AddPeer(addr string, sintf string) error {
|
2019-03-29 08:58:30 +00:00
|
|
|
if err := c.CallPeer(addr, sintf); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
c.config.Mutex.Lock()
|
|
|
|
if sintf == "" {
|
|
|
|
c.config.Current.Peers = append(c.config.Current.Peers, addr)
|
|
|
|
} else {
|
|
|
|
c.config.Current.InterfacePeers[sintf] = append(c.config.Current.InterfacePeers[sintf], addr)
|
|
|
|
}
|
|
|
|
c.config.Mutex.Unlock()
|
|
|
|
return nil
|
2018-05-27 21:13:37 +00:00
|
|
|
}
|
|
|
|
|
2019-03-29 18:05:17 +00:00
|
|
|
// CallPeer calls a peer once. This should be specified in the peer URI format,
|
|
|
|
// e.g.:
|
|
|
|
// tcp://a.b.c.d:e
|
|
|
|
// socks://a.b.c.d:e/f.g.h.i:j
|
|
|
|
// This does not add the peer to the peer list, so if the connection drops, the
|
|
|
|
// peer will not be called again automatically.
|
2019-03-28 16:13:14 +00:00
|
|
|
func (c *Core) CallPeer(addr string, sintf string) error {
|
|
|
|
return c.link.call(addr, sintf)
|
|
|
|
}
|
|
|
|
|
2019-03-29 18:05:17 +00:00
|
|
|
// AddAllowedEncryptionPublicKey adds an allowed public key. This allow peerings
|
|
|
|
// to be restricted only to keys that you have selected.
|
2018-05-27 21:13:37 +00:00
|
|
|
func (c *Core) AddAllowedEncryptionPublicKey(boxStr string) error {
|
|
|
|
return c.admin.addAllowedEncryptionPublicKey(boxStr)
|
|
|
|
}
|