yggdrasil-go/src/yggdrasil/core.go

368 lines
9.9 KiB
Go
Raw Normal View History

2017-12-29 07:16:20 +03:00
package yggdrasil
2018-06-13 01:50:08 +03:00
import (
"encoding/hex"
"io/ioutil"
"log"
"net"
"regexp"
"sync"
"time"
2018-06-13 01:50:08 +03:00
"github.com/yggdrasil-network/yggdrasil-go/src/address"
2018-12-08 04:56:04 +03:00
"github.com/yggdrasil-network/yggdrasil-go/src/config"
"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
2018-12-08 04:56:04 +03:00
"github.com/yggdrasil-network/yggdrasil-go/src/defaults"
2018-06-13 01:50:08 +03:00
)
2017-12-29 07:16:20 +03:00
var buildName string
var buildVersion string
type module interface {
2018-12-29 22:14:26 +03:00
init(*Core, *config.NodeConfig) error
start() error
}
2018-05-28 00:13:37 +03: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 07:16:20 +03:00
type Core struct {
2018-01-05 01:37:51 +03: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
config config.NodeConfig // Active config
configOld config.NodeConfig // Previous config
configMutex sync.RWMutex // Protects both config and configOld
2018-12-29 22:14:26 +03:00
boxPub crypto.BoxPubKey
boxPriv crypto.BoxPrivKey
sigPub crypto.SigPubKey
sigPriv crypto.SigPrivKey
2018-01-05 01:37:51 +03:00
switchTable switchTable
peers peers
sessions sessions
router router
dht dht
2018-01-21 03:17:15 +03:00
admin admin
2018-01-05 01:37:51 +03:00
searches searches
multicast multicast
2018-12-16 01:37:11 +03:00
nodeinfo nodeinfo
tcp tcpInterface
2019-01-05 15:06:45 +03:00
awdl awdl
2018-12-29 22:14:26 +03:00
log *log.Logger
ifceExpr []*regexp.Regexp // the zone of link-local IPv6 peers must match this
2017-12-29 07:16:20 +03:00
}
2018-12-29 22:14:26 +03:00
func (c *Core) init() error {
2018-01-05 01:37:51 +03: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-28 00:13:37 +03:00
if c.log == nil {
c.log = log.New(ioutil.Discard, "", 0)
}
2018-12-29 22:14:26 +03:00
boxPubHex, err := hex.DecodeString(c.config.EncryptionPublicKey)
if err != nil {
return err
}
boxPrivHex, err := hex.DecodeString(c.config.EncryptionPrivateKey)
if err != nil {
return err
}
sigPubHex, err := hex.DecodeString(c.config.SigningPublicKey)
if err != nil {
return err
}
sigPrivHex, err := hex.DecodeString(c.config.SigningPrivateKey)
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)
c.nodeinfo.init(c)
2018-01-05 01:37:51 +03:00
c.searches.init(c)
c.dht.init(c)
c.sessions.init(c)
c.multicast.init(c)
2018-01-05 01:37:51 +03:00
c.peers.init(c)
c.router.init(c)
2018-12-29 22:14:26 +03:00
c.switchTable.init(c) // TODO move before peers? before router?
return nil
2017-12-29 07:16:20 +03: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 {
// Get the peers from the config - these could change!
c.configMutex.RLock()
peers := c.config.Peers
interfacepeers := c.config.InterfacePeers
c.configMutex.RUnlock()
// Add peers from the Peers section
for _, peer := range peers {
c.AddPeer(peer, "")
time.Sleep(time.Second)
}
// Add peers from the InterfacePeers section
for intf, intfpeers := range interfacepeers {
for _, peer := range intfpeers {
c.AddPeer(peer, intf)
time.Sleep(time.Second)
}
}
// Sit for a while
time.Sleep(time.Minute)
}
}
// UpdateConfig updates the configuration in Core and then signals the
// various module goroutines to reconfigure themselves if needed
func (c *Core) UpdateConfig(config *config.NodeConfig) {
c.configMutex.Lock()
c.configOld = c.config
c.config = *config
c.configMutex.Unlock()
2018-12-30 15:04:42 +03:00
components := []chan chan error{
c.admin.reconfigure,
2019-01-14 17:25:52 +03:00
c.searches.reconfigure,
c.dht.reconfigure,
c.sessions.reconfigure,
c.peers.reconfigure,
c.router.reconfigure,
c.router.tun.reconfigure,
c.switchTable.reconfigure,
2018-12-30 18:21:09 +03:00
c.tcp.reconfigure,
2018-12-30 15:04:42 +03:00
c.multicast.reconfigure,
}
for _, component := range components {
response := make(chan error)
component <- response
if err := <-response; err != nil {
c.log.Println(err)
}
}
}
// GetBuildName gets the current build name. This is usually injected if built
// from git, or returns "unknown" otherwise.
func GetBuildName() string {
if buildName == "" {
return "unknown"
}
return buildName
}
// Get the current build version. This is usually injected if built from git,
// or returns "unknown" otherwise.
func GetBuildVersion() string {
if buildVersion == "" {
return "unknown"
}
return buildVersion
}
2018-05-28 00:13:37 +03:00
// 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
// DHT node.
func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error {
c.log = log
if name := GetBuildName(); name != "unknown" {
c.log.Println("Build name:", name)
}
if version := GetBuildVersion(); version != "unknown" {
c.log.Println("Build version:", version)
}
2018-05-28 00:13:37 +03:00
c.log.Println("Starting up...")
c.configMutex.Lock()
c.config = *nc
c.configOld = c.config
c.configMutex.Unlock()
2018-12-29 22:14:26 +03:00
c.init()
2018-05-28 00:13:37 +03:00
c.nodeinfo.setNodeInfo(nc.NodeInfo, nc.NodeInfoPrivacy)
2018-12-15 03:48:27 +03:00
2019-01-14 17:01:38 +03:00
if err := c.tcp.init(c); err != nil {
2018-05-28 00:13:37 +03:00
c.log.Println("Failed to start TCP interface")
return err
}
if err := c.awdl.init(c); err != nil {
c.log.Println("Failed to start AWDL interface")
return err
}
if nc.SwitchOptions.MaxTotalQueueSize >= SwitchQueueTotalMinSize {
c.switchTable.queueTotalMaxSize = nc.SwitchOptions.MaxTotalQueueSize
}
if err := c.switchTable.start(); err != nil {
c.log.Println("Failed to start switch")
return err
}
c.sessions.setSessionFirewallState(nc.SessionFirewall.Enable)
c.sessions.setSessionFirewallDefaults(
nc.SessionFirewall.AllowFromDirect,
nc.SessionFirewall.AllowFromRemote,
nc.SessionFirewall.AlwaysAllowOutbound,
)
c.sessions.setSessionFirewallWhitelist(nc.SessionFirewall.WhitelistEncryptionPublicKeys)
c.sessions.setSessionFirewallBlacklist(nc.SessionFirewall.BlacklistEncryptionPublicKeys)
2018-05-28 00:13:37 +03:00
if err := c.router.start(); err != nil {
c.log.Println("Failed to start router")
return err
}
if err := c.admin.start(); err != nil {
c.log.Println("Failed to start admin socket")
return err
}
if err := c.multicast.start(); err != nil {
c.log.Println("Failed to start multicast interface")
return err
}
2019-01-14 17:25:52 +03:00
if err := c.router.tun.start(); err != nil {
2018-05-28 00:35:30 +03:00
c.log.Println("Failed to start TUN/TAP")
return err
}
go c.addPeerLoop()
2018-05-28 00:13:37 +03:00
c.log.Println("Startup complete")
return nil
}
// Stops the Yggdrasil node.
func (c *Core) Stop() {
c.log.Println("Stopping...")
2018-12-14 20:35:02 +03:00
c.router.tun.close()
2018-07-07 22:04:11 +03:00
c.admin.close()
2018-05-28 00:13:37 +03:00
}
// Generates a new encryption keypair. The encryption keys are used to
// encrypt traffic and to derive the IPv6 address/subnet of the node.
func (c *Core) NewEncryptionKeys() (*crypto.BoxPubKey, *crypto.BoxPrivKey) {
return crypto.NewBoxKeys()
2018-05-28 00:13:37 +03:00
}
// Generates a new signing keypair. The signing keys are used to derive the
// structure of the spanning tree.
func (c *Core) NewSigningKeys() (*crypto.SigPubKey, *crypto.SigPrivKey) {
return crypto.NewSigKeys()
2018-05-28 00:13:37 +03:00
}
// Gets the node ID.
func (c *Core) GetNodeID() *crypto.NodeID {
return crypto.GetNodeID(&c.boxPub)
2017-12-29 07:16:20 +03:00
}
2018-05-28 00:13:37 +03:00
// Gets the tree ID.
func (c *Core) GetTreeID() *crypto.TreeID {
return crypto.GetTreeID(&c.sigPub)
2017-12-29 07:16:20 +03:00
}
2018-05-28 00:13:37 +03:00
// Gets the IPv6 address of the Yggdrasil node. This is always a /128.
func (c *Core) GetAddress() *net.IP {
address := net.IP(address.AddrForNodeID(c.GetNodeID())[:])
2018-05-28 00:13:37 +03:00
return &address
}
// Gets the routed IPv6 subnet of the Yggdrasil node. This is always a /64.
func (c *Core) GetSubnet() *net.IPNet {
subnet := address.SubnetForNodeID(c.GetNodeID())[:]
2018-05-28 00:13:37 +03:00
subnet = append(subnet, 0, 0, 0, 0, 0, 0, 0, 0)
2018-06-02 23:21:05 +03:00
return &net.IPNet{IP: subnet, Mask: net.CIDRMask(64, 128)}
2018-05-28 00:13:37 +03:00
}
2018-12-16 01:37:11 +03:00
// Gets the nodeinfo.
func (c *Core) GetNodeInfo() nodeinfoPayload {
return c.nodeinfo.getNodeInfo()
}
2018-12-16 01:37:11 +03:00
// Sets the nodeinfo.
func (c *Core) SetNodeInfo(nodeinfo interface{}, nodeinfoprivacy bool) {
c.nodeinfo.setNodeInfo(nodeinfo, nodeinfoprivacy)
}
2018-05-28 00:13:37 +03:00
// 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) {
c.log = log
}
// Adds a peer. This should be specified in the peer URI format, i.e.
// tcp://a.b.c.d:e, udp://a.b.c.d:e, socks://a.b.c.d:e/f.g.h.i:j
func (c *Core) AddPeer(addr string, sintf string) error {
return c.admin.addPeer(addr, sintf)
2018-05-28 00:13:37 +03:00
}
// Adds an expression to select multicast interfaces for peer discovery. This
// should be done before calling Start. This function can be called multiple
// times to add multiple search expressions.
func (c *Core) AddMulticastInterfaceExpr(expr *regexp.Regexp) {
c.ifceExpr = append(c.ifceExpr, expr)
}
// Adds an allowed public key. This allow peerings to be restricted only to
// keys that you have selected.
func (c *Core) AddAllowedEncryptionPublicKey(boxStr string) error {
return c.admin.addAllowedEncryptionPublicKey(boxStr)
}
// Gets the default admin listen address for your platform.
func (c *Core) GetAdminDefaultListen() string {
return defaults.GetDefaults().DefaultAdminListen
}
2018-05-28 00:13:37 +03:00
// Gets the default TUN/TAP interface name for your platform.
func (c *Core) GetTUNDefaultIfName() string {
return defaults.GetDefaults().DefaultIfName
2018-05-28 00:13:37 +03:00
}
// Gets the default TUN/TAP interface MTU for your platform. This can be as high
// as 65535, depending on platform, but is never lower than 1280.
func (c *Core) GetTUNDefaultIfMTU() int {
return defaults.GetDefaults().DefaultIfMTU
2018-05-28 00:13:37 +03:00
}
// Gets the maximum supported TUN/TAP interface MTU for your platform. This
// can be as high as 65535, depending on platform, but is never lower than 1280.
func (c *Core) GetTUNMaximumIfMTU() int {
return defaults.GetDefaults().MaximumIfMTU
2018-05-28 00:13:37 +03:00
}
// Gets the default TUN/TAP interface mode for your platform.
func (c *Core) GetTUNDefaultIfTAPMode() bool {
return defaults.GetDefaults().DefaultIfTAPMode
2018-05-28 00:13:37 +03:00
}
// Gets the current TUN/TAP interface name.
func (c *Core) GetTUNIfName() string {
2018-12-14 20:35:02 +03:00
return c.router.tun.iface.Name()
}
2018-05-28 00:13:37 +03:00
// Gets the current TUN/TAP interface MTU.
func (c *Core) GetTUNIfMTU() int {
2018-12-14 20:35:02 +03:00
return c.router.tun.mtu
}