Simplify configuration code throughout

This commit is contained in:
Neil Alexander 2019-09-02 11:54:27 +01:00
parent 174ebceaac
commit 029be2d86b
No known key found for this signature in database
GPG key ID: A02A2019A2BB0944
17 changed files with 223 additions and 209 deletions

View file

@ -18,7 +18,6 @@ import (
gsyslog "github.com/hashicorp/go-syslog"
"github.com/hjson/hjson-go"
"github.com/kardianos/minwinsvc"
"github.com/mitchellh/mapstructure"
"github.com/yggdrasil-network/yggdrasil-go/src/admin"
"github.com/yggdrasil-network/yggdrasil-go/src/config"
@ -31,7 +30,6 @@ import (
type node struct {
core yggdrasil.Core
state *config.NodeState
tuntap tuntap.TunAdapter
multicast multicast.Multicast
admin admin.AdminSocket
@ -66,47 +64,10 @@ func readConfig(useconf *bool, useconffile *string, normaliseconf *bool) *config
panic(err)
}
}
// Generate a new configuration - this gives us a set of sane defaults -
// then parse the configuration we loaded above on top of it. The effect
// of this is that any configuration item that is missing from the provided
// configuration will use a sane default.
// Generate blank configuration
cfg := config.GenerateConfig()
var dat map[string]interface{}
if err := hjson.Unmarshal(conf, &dat); err != nil {
panic(err)
}
// Check for fields that have changed type recently, e.g. the Listen config
// option is now a []string rather than a string
if listen, ok := dat["Listen"].(string); ok {
dat["Listen"] = []string{listen}
}
if tunnelrouting, ok := dat["TunnelRouting"].(map[string]interface{}); ok {
if c, ok := tunnelrouting["IPv4Sources"]; ok {
delete(tunnelrouting, "IPv4Sources")
tunnelrouting["IPv4LocalSubnets"] = c
}
if c, ok := tunnelrouting["IPv6Sources"]; ok {
delete(tunnelrouting, "IPv6Sources")
tunnelrouting["IPv6LocalSubnets"] = c
}
if c, ok := tunnelrouting["IPv4Destinations"]; ok {
delete(tunnelrouting, "IPv4Destinations")
tunnelrouting["IPv4RemoteSubnets"] = c
}
if c, ok := tunnelrouting["IPv6Destinations"]; ok {
delete(tunnelrouting, "IPv6Destinations")
tunnelrouting["IPv6RemoteSubnets"] = c
}
}
// Sanitise the config
confJson, err := json.Marshal(dat)
if err != nil {
panic(err)
}
json.Unmarshal(confJson, &cfg)
// Overlay our newly mapped configuration onto the autoconf node config that
// we generated above.
if err = mapstructure.Decode(dat, &cfg); err != nil {
// ... and then update it with the supplied HJSON input
if err := cfg.UnmarshalHJSON(conf); err != nil {
panic(err)
}
return cfg
@ -119,9 +80,9 @@ func doGenconf(isjson bool) string {
var bs []byte
var err error
if isjson {
bs, err = json.MarshalIndent(cfg, "", " ")
bs, err = cfg.MarshalJSON()
} else {
bs, err = hjson.Marshal(cfg)
bs, err = cfg.MarshalHJSON()
}
if err != nil {
panic(err)
@ -224,7 +185,7 @@ func main() {
n := node{}
// Now start Yggdrasil - this starts the DHT, router, switch and other core
// components needed for Yggdrasil to operate
n.state, err = n.core.Start(cfg, logger)
err = n.core.Start(cfg, logger)
if err != nil {
logger.Errorln("An error occurred during startup")
panic(err)
@ -232,12 +193,12 @@ func main() {
// Register the session firewall gatekeeper function
n.core.SetSessionGatekeeper(n.sessionFirewall)
// Start the admin socket
n.admin.Init(&n.core, n.state, logger, nil)
n.admin.Init(&n.core, logger, nil)
if err := n.admin.Start(); err != nil {
logger.Errorln("An error occurred starting admin socket:", err)
}
// Start the multicast interface
n.multicast.Init(&n.core, n.state, logger, nil)
n.multicast.Init(&n.core, logger, nil)
if err := n.multicast.Start(); err != nil {
logger.Errorln("An error occurred starting multicast:", err)
}
@ -245,7 +206,7 @@ func main() {
// Start the TUN/TAP interface
if listener, err := n.core.ConnListen(); err == nil {
if dialer, err := n.core.ConnDialer(); err == nil {
n.tuntap.Init(n.state, logger, listener, dialer)
n.tuntap.Init(&n.core, logger, listener, dialer)
if err := n.tuntap.Start(); err != nil {
logger.Errorln("An error occurred starting TUN/TAP:", err)
}
@ -281,8 +242,8 @@ func main() {
cfg = readConfig(useconf, useconffile, normaliseconf)
logger.Infoln("Reloading configuration from", *useconffile)
n.core.UpdateConfig(cfg)
n.tuntap.UpdateConfig(cfg)
n.multicast.UpdateConfig(cfg)
n.tuntap.UpdateConfig()
n.multicast.UpdateConfig()
} else {
logger.Errorln("Reloading config at runtime is only possible with -useconffile")
}
@ -300,18 +261,17 @@ func (n *node) shutdown() {
}
func (n *node) sessionFirewall(pubkey *crypto.BoxPubKey, initiator bool) bool {
n.state.Mutex.RLock()
defer n.state.Mutex.RUnlock()
current := n.core.GetConfig()
// Allow by default if the session firewall is disabled
if !n.state.Current.SessionFirewall.Enable {
if !current.SessionFirewall.Enable {
return true
}
// Prepare for checking whitelist/blacklist
var box crypto.BoxPubKey
// Reject blacklisted nodes
for _, b := range n.state.Current.SessionFirewall.BlacklistEncryptionPublicKeys {
for _, b := range current.SessionFirewall.BlacklistEncryptionPublicKeys {
key, err := hex.DecodeString(b)
if err == nil {
copy(box[:crypto.BoxPubKeyLen], key)
@ -322,7 +282,7 @@ func (n *node) sessionFirewall(pubkey *crypto.BoxPubKey, initiator bool) bool {
}
// Allow whitelisted nodes
for _, b := range n.state.Current.SessionFirewall.WhitelistEncryptionPublicKeys {
for _, b := range current.SessionFirewall.WhitelistEncryptionPublicKeys {
key, err := hex.DecodeString(b)
if err == nil {
copy(box[:crypto.BoxPubKeyLen], key)
@ -333,7 +293,7 @@ func (n *node) sessionFirewall(pubkey *crypto.BoxPubKey, initiator bool) bool {
}
// Allow outbound sessions if appropriate
if n.state.Current.SessionFirewall.AlwaysAllowOutbound {
if current.SessionFirewall.AlwaysAllowOutbound {
if initiator {
return true
}
@ -349,12 +309,12 @@ func (n *node) sessionFirewall(pubkey *crypto.BoxPubKey, initiator bool) bool {
}
// Allow direct peers if appropriate
if n.state.Current.SessionFirewall.AllowFromDirect && isDirectPeer {
if current.SessionFirewall.AllowFromDirect && isDirectPeer {
return true
}
// Allow remote nodes if appropriate
if n.state.Current.SessionFirewall.AllowFromRemote && !isDirectPeer {
if current.SessionFirewall.AllowFromRemote && !isDirectPeer {
return true
}

View file

@ -15,7 +15,6 @@ import (
"github.com/gologme/log"
"github.com/yggdrasil-network/yggdrasil-go/src/address"
"github.com/yggdrasil-network/yggdrasil-go/src/config"
"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
"github.com/yggdrasil-network/yggdrasil-go/src/util"
"github.com/yggdrasil-network/yggdrasil-go/src/version"
@ -54,7 +53,7 @@ func (a *AdminSocket) AddHandler(name string, args []string, handlerfunc func(In
}
// init runs the initial admin setup.
func (a *AdminSocket) Init(c *yggdrasil.Core, state *config.NodeState, log *log.Logger, options interface{}) {
func (a *AdminSocket) Init(c *yggdrasil.Core, log *log.Logger, options interface{}) {
a.core = c
a.log = log
a.reconfigure = make(chan chan error, 1)
@ -62,17 +61,15 @@ func (a *AdminSocket) Init(c *yggdrasil.Core, state *config.NodeState, log *log.
go func() {
for {
e := <-a.reconfigure
current, previous := state.GetCurrent(), state.GetPrevious()
if current.AdminListen != previous.AdminListen {
a.listenaddr = current.AdminListen
if newlistenaddr := c.GetConfig().AdminListen; newlistenaddr != a.listenaddr {
a.listenaddr = newlistenaddr
a.Stop()
a.Start()
}
e <- nil
}
}()
current := state.GetCurrent()
a.listenaddr = current.AdminListen
a.listenaddr = c.GetConfig().AdminListen
a.AddHandler("list", []string{}, func(in Info) (Info, error) {
handlers := make(map[string]interface{})
for handlername, handler := range a.handlers {
@ -314,6 +311,9 @@ func (a *AdminSocket) Init(c *yggdrasil.Core, state *config.NodeState, log *log.
// start runs the admin API socket to listen for / respond to admin API calls.
func (a *AdminSocket) Start() error {
if a.core == nil {
return errors.New("admin socket has not been initialised, call Init first")
}
if a.listenaddr != "none" && a.listenaddr != "" {
go a.listen()
}

5
src/config/admin.go Normal file
View file

@ -0,0 +1,5 @@
package config
type AdminConfig struct {
AdminListen string `comment:"Listen address for admin connections. Default is to listen for local\nconnections either on TCP/9001 or a UNIX socket depending on your\nplatform. Use this value for yggdrasilctl -endpoint=X. To disable\nthe admin socket, use the value \"none\" instead."`
}

View file

@ -2,61 +2,29 @@ package config
import (
"encoding/hex"
"sync"
"encoding/json"
hjson "github.com/hjson/hjson-go"
"github.com/mitchellh/mapstructure"
"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
"github.com/yggdrasil-network/yggdrasil-go/src/defaults"
)
// NodeState represents the active and previous configuration of the node and
// protects it with a mutex
type NodeState struct {
Current NodeConfig
Previous NodeConfig
Mutex sync.RWMutex
}
// Current returns the current node config
func (s *NodeState) GetCurrent() NodeConfig {
s.Mutex.RLock()
defer s.Mutex.RUnlock()
return s.Current
}
// Previous returns the previous node config
func (s *NodeState) GetPrevious() NodeConfig {
s.Mutex.RLock()
defer s.Mutex.RUnlock()
return s.Previous
}
// Replace the node configuration with new configuration. This method returns
// both the new and the previous node configs
func (s *NodeState) Replace(n NodeConfig) {
s.Mutex.Lock()
defer s.Mutex.Unlock()
s.Previous = s.Current
s.Current = n
}
// NodeConfig defines all configuration values needed to run a signle yggdrasil node
type NodeConfig struct {
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."`
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."`
AdminListen string `comment:"Listen address for admin connections. Default is to listen for local\nconnections either on TCP/9001 or a UNIX socket depending on your\nplatform. Use this value for yggdrasilctl -endpoint=X. To disable\nthe admin socket, use the value \"none\" instead."`
MulticastInterfaces []string `comment:"Regular expressions for which interfaces multicast peer discovery\nshould be enabled on. If none specified, multicast peer discovery is\ndisabled. The default value is .* which uses all interfaces."`
AllowedEncryptionPublicKeys []string `comment:"List of peer encryption public keys to allow incoming TCP peering\nconnections from. If left empty/undefined then all connections will\nbe allowed by default. This does not affect outgoing peerings, nor\ndoes it affect link-local peers discovered via multicast."`
EncryptionPublicKey string `comment:"Your public encryption key. Your peers may ask you for this to put\ninto their AllowedEncryptionPublicKeys configuration."`
EncryptionPrivateKey string `comment:"Your private encryption key. DO NOT share this with anyone!"`
SigningPublicKey string `comment:"Your public signing key. You should not ordinarily need to share\nthis with anyone."`
SigningPrivateKey string `comment:"Your private signing key. DO NOT share this with anyone!"`
LinkLocalTCPPort uint16 `comment:"The port number to be used for the link-local TCP listeners for the\nconfigured MulticastInterfaces. This option does not affect listeners\nspecified in the Listen option. Unless you plan to firewall link-local\ntraffic, it is best to leave this as the default value of 0. This\noption cannot currently be changed by reloading config during runtime."`
IfName string `comment:"Local network interface name for TUN/TAP adapter, or \"auto\" to select\nan interface automatically, or \"none\" to run without TUN/TAP."`
IfTAPMode bool `comment:"Set local network interface to TAP mode rather than TUN mode if\nsupported by your platform - option will be ignored if not."`
IfMTU int `comment:"Maximux Transmission Unit (MTU) size for your local TUN/TAP interface.\nDefault is the largest supported size for your platform. The lowest\npossible value is 1280."`
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."`
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."`
AdminConfig ``
MulticastConfig ``
AllowedEncryptionPublicKeys []string `comment:"List of peer encryption public keys to allow incoming TCP peering\nconnections from. If left empty/undefined then all connections will\nbe allowed by default. This does not affect outgoing peerings, nor\ndoes it affect link-local peers discovered via multicast."`
EncryptionPublicKey string `comment:"Your public encryption key. Your peers may ask you for this to put\ninto their AllowedEncryptionPublicKeys configuration."`
EncryptionPrivateKey string `comment:"Your private encryption key. DO NOT share this with anyone!"`
SigningPublicKey string `comment:"Your public signing key. You should not ordinarily need to share\nthis with anyone."`
SigningPrivateKey string `comment:"Your private signing key. DO NOT share this with anyone!"`
LinkLocalTCPPort uint16 `comment:"The port number to be used for the link-local TCP listeners for the\nconfigured MulticastInterfaces. This option does not affect listeners\nspecified in the Listen option. Unless you plan to firewall link-local\ntraffic, it is best to leave this as the default value of 0. This\noption cannot currently be changed by reloading config during runtime."`
TunTapConfig ``
SessionFirewall SessionFirewall `comment:"The session firewall controls who can send/receive network traffic\nto/from. This is useful if you want to protect this node without\nresorting to using a real firewall. This does not affect traffic\nbeing routed via this node to somewhere else. Rules are prioritised as\nfollows: blacklist, whitelist, always allow outgoing, direct, remote."`
TunnelRouting TunnelRouting `comment:"Allow tunneling non-Yggdrasil traffic over Yggdrasil. This effectively\nallows you to use Yggdrasil to route to, or to bridge other networks,\nsimilar to a VPN tunnel. Tunnelling works between any two nodes and\ndoes not require them to be directly peered."`
SwitchOptions SwitchOptions `comment:"Advanced options for tuning the switch. Normally you will not need\nto edit these options."`
NodeInfoPrivacy bool `comment:"By default, nodeinfo contains some defaults including the platform,\narchitecture and Yggdrasil version. These can help when surveying\nthe network and diagnosing network routing problems. Enabling\nnodeinfo privacy prevents this, so that only items specified in\n\"NodeInfo\" are sent back if specified."`
NodeInfo map[string]interface{} `comment:"Optional node info. This must be a { \"key\": \"value\", ... } map\nor set as null. This is entirely optional but, if set, is visible\nto the whole network on request."`
@ -72,15 +40,6 @@ type SessionFirewall struct {
BlacklistEncryptionPublicKeys []string `comment:"List of public keys from which network traffic is always rejected,\nregardless of the whitelist, AllowFromDirect or AllowFromRemote."`
}
// TunnelRouting contains the crypto-key routing tables for tunneling
type TunnelRouting struct {
Enable bool `comment:"Enable or disable tunnel routing."`
IPv6RemoteSubnets map[string]string `comment:"IPv6 subnets belonging to remote nodes, mapped to the node's public\nkey, e.g. { \"aaaa:bbbb:cccc::/e\": \"boxpubkey\", ... }"`
IPv6LocalSubnets []string `comment:"IPv6 subnets belonging to this node's end of the tunnels. Only traffic\nfrom these ranges (or the Yggdrasil node's IPv6 address/subnet)\nwill be tunnelled."`
IPv4RemoteSubnets map[string]string `comment:"IPv4 subnets belonging to remote nodes, mapped to the node's public\nkey, e.g. { \"a.b.c.d/e\": \"boxpubkey\", ... }"`
IPv4LocalSubnets []string `comment:"IPv4 subnets belonging to this node's end of the tunnels. Only traffic\nfrom these ranges will be tunnelled."`
}
// SwitchOptions contains tuning options for the switch
type SwitchOptions struct {
MaxTotalQueueSize uint64 `comment:"Maximum size of all switch queues combined (in bytes)."`
@ -137,3 +96,83 @@ func (cfg *NodeConfig) NewSigningKeys() {
cfg.SigningPublicKey = hex.EncodeToString(spub[:])
cfg.SigningPrivateKey = hex.EncodeToString(spriv[:])
}
// MarshalJSON exports the configuration into JSON format. No comments are
// included in the JSON export as comments are not valid in pure JSON.
func (cfg *NodeConfig) MarshalJSON() ([]byte, error) {
bs, err := json.MarshalIndent(cfg, "", " ")
if err != nil {
return nil, err
}
return bs, nil
}
// MarshalHJSON exports the configuration into HJSON format, complete with
// comments describing what each configuration item does.
func (cfg *NodeConfig) MarshalHJSON() ([]byte, error) {
bs, err := hjson.Marshal(cfg)
if err != nil {
return nil, err
}
return bs, nil
}
// UnmarshalJSON parses the configuration in pure JSON format and updates the
// NodeConfig accordingly. The input JSON can be partial - only supplied fields
// will be updated.
func (cfg *NodeConfig) UnmarshalJSON(conf []byte) error {
var dat map[string]interface{}
if err := json.Unmarshal(conf, &dat); err != nil {
return err
}
return cfg.decodeConfig(dat)
}
// UnmarshalHJSON parses the configuration in HJSON format and updates the
// NodeConfig accordingly. The input HJSON can be partial - only supplied fields
// will be updated.
func (cfg *NodeConfig) UnmarshalHJSON(conf []byte) error {
var dat map[string]interface{}
if err := hjson.Unmarshal(conf, &dat); err != nil {
return err
}
return cfg.decodeConfig(dat)
}
func (cfg *NodeConfig) decodeConfig(dat map[string]interface{}) error {
// Check for fields that have changed type recently, e.g. the Listen config
// option is now a []string rather than a string
if listen, ok := dat["Listen"].(string); ok {
dat["Listen"] = []string{listen}
}
if tunnelrouting, ok := dat["TunnelRouting"].(map[string]interface{}); ok {
if c, ok := tunnelrouting["IPv4Sources"]; ok {
delete(tunnelrouting, "IPv4Sources")
tunnelrouting["IPv4LocalSubnets"] = c
}
if c, ok := tunnelrouting["IPv6Sources"]; ok {
delete(tunnelrouting, "IPv6Sources")
tunnelrouting["IPv6LocalSubnets"] = c
}
if c, ok := tunnelrouting["IPv4Destinations"]; ok {
delete(tunnelrouting, "IPv4Destinations")
tunnelrouting["IPv4RemoteSubnets"] = c
}
if c, ok := tunnelrouting["IPv6Destinations"]; ok {
delete(tunnelrouting, "IPv6Destinations")
tunnelrouting["IPv6RemoteSubnets"] = c
}
}
// Sanitise the config
/*confJson, err := json.Marshal(dat)
if err != nil {
return err
}
json.Unmarshal(confJson, &cfg)*/
// Overlay our newly mapped configuration onto the autoconf node config that
// we generated above.
if err := mapstructure.Decode(dat, &cfg); err != nil {
return err
}
return nil
}

5
src/config/multicast.go Normal file
View file

@ -0,0 +1,5 @@
package config
type MulticastConfig struct {
MulticastInterfaces []string `comment:"Regular expressions for which interfaces multicast peer discovery\nshould be enabled on. If none specified, multicast peer discovery is\ndisabled. The default value is .* which uses all interfaces."`
}

17
src/config/tuntap.go Normal file
View file

@ -0,0 +1,17 @@
package config
type TunTapConfig struct {
IfName string `comment:"Local network interface name for TUN/TAP adapter, or \"auto\" to select\nan interface automatically, or \"none\" to run without TUN/TAP."`
IfTAPMode bool `comment:"Set local network interface to TAP mode rather than TUN mode if\nsupported by your platform - option will be ignored if not."`
IfMTU int `comment:"Maximux Transmission Unit (MTU) size for your local TUN/TAP interface.\nDefault is the largest supported size for your platform. The lowest\npossible value is 1280."`
TunnelRouting TunnelRouting `comment:"Allow tunneling non-Yggdrasil traffic over Yggdrasil. This effectively\nallows you to use Yggdrasil to route to, or to bridge other networks,\nsimilar to a VPN tunnel. Tunnelling works between any two nodes and\ndoes not require them to be directly peered."`
}
// TunnelRouting contains the crypto-key routing tables for tunneling
type TunnelRouting struct {
Enable bool `comment:"Enable or disable tunnel routing."`
IPv6RemoteSubnets map[string]string `comment:"IPv6 subnets belonging to remote nodes, mapped to the node's public\nkey, e.g. { \"aaaa:bbbb:cccc::/e\": \"boxpubkey\", ... }"`
IPv6LocalSubnets []string `comment:"IPv6 subnets belonging to this node's end of the tunnels. Only traffic\nfrom these ranges (or the Yggdrasil node's IPv6 address/subnet)\nwill be tunnelled."`
IPv4RemoteSubnets map[string]string `comment:"IPv4 subnets belonging to remote nodes, mapped to the node's public\nkey, e.g. { \"a.b.c.d/e\": \"boxpubkey\", ... }"`
IPv4LocalSubnets []string `comment:"IPv4 subnets belonging to this node's end of the tunnels. Only traffic\nfrom these ranges will be tunnelled."`
}

View file

@ -2,6 +2,7 @@ package multicast
import (
"context"
"errors"
"fmt"
"net"
"regexp"
@ -9,7 +10,6 @@ import (
"github.com/gologme/log"
"github.com/yggdrasil-network/yggdrasil-go/src/config"
"github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil"
"golang.org/x/net/ipv6"
)
@ -20,7 +20,6 @@ import (
// automatically.
type Multicast struct {
core *yggdrasil.Core
config *config.NodeState
log *log.Logger
sock *ipv6.PacketConn
groupAddr string
@ -30,13 +29,11 @@ type Multicast struct {
}
// Init prepares the multicast interface for use.
func (m *Multicast) Init(core *yggdrasil.Core, state *config.NodeState, log *log.Logger, options interface{}) error {
func (m *Multicast) Init(core *yggdrasil.Core, log *log.Logger, options interface{}) error {
m.core = core
m.config = state
m.log = log
m.listeners = make(map[string]*yggdrasil.TcpListener)
current := m.config.GetCurrent()
m.listenPort = current.LinkLocalTCPPort
m.listenPort = m.core.GetConfig().LinkLocalTCPPort
m.groupAddr = "[ff02::114]:9001"
return nil
}
@ -45,6 +42,9 @@ func (m *Multicast) Init(core *yggdrasil.Core, state *config.NodeState, log *log
// listen for multicast beacons from other hosts and will advertise multicast
// beacons out to the network.
func (m *Multicast) Start() error {
if m.core == nil {
return errors.New("multicast has not been initialised, call Init first")
}
addr, err := net.ResolveUDPAddr("udp", m.groupAddr)
if err != nil {
return err
@ -80,9 +80,8 @@ func (m *Multicast) Stop() error {
// UpdateConfig updates the multicast module with the provided config.NodeConfig
// and then signals the various module goroutines to reconfigure themselves if
// needed.
func (m *Multicast) UpdateConfig(config *config.NodeConfig) {
m.log.Debugln("Reloading multicast configuration...")
m.config.Replace(*config)
func (m *Multicast) UpdateConfig() {
}
// GetInterfaces returns the currently known/enabled multicast interfaces. It is
@ -91,7 +90,7 @@ func (m *Multicast) UpdateConfig(config *config.NodeConfig) {
func (m *Multicast) Interfaces() map[string]net.Interface {
interfaces := make(map[string]net.Interface)
// Get interface expressions from config
current := m.config.GetCurrent()
current := m.core.GetConfig()
exprs := current.MulticastInterfaces
// Ask the system for network interfaces
allifaces, err := net.Interfaces()

View file

@ -44,7 +44,7 @@ func (c *cryptokey) init(tun *TunAdapter) {
// Configure the CKR routes. This should only ever be ran by the TUN/TAP actor.
func (c *cryptokey) configure() {
current := c.tun.config.GetCurrent()
current := c.tun.core.GetConfig()
// Set enabled/disabled state
c.setEnabled(current.TunnelRouting.Enable)

View file

@ -21,7 +21,6 @@ import (
"github.com/yggdrasil-network/water"
"github.com/yggdrasil-network/yggdrasil-go/src/address"
"github.com/yggdrasil-network/yggdrasil-go/src/config"
"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
"github.com/yggdrasil-network/yggdrasil-go/src/defaults"
"github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil"
@ -35,9 +34,9 @@ const tun_ETHER_HEADER_LENGTH = 14
// you should pass this object to the yggdrasil.SetRouterAdapter() function
// before calling yggdrasil.Start().
type TunAdapter struct {
core *yggdrasil.Core
writer tunWriter
reader tunReader
config *config.NodeState
log *log.Logger
reconfigure chan chan error
listener *yggdrasil.Listener
@ -110,8 +109,8 @@ func MaximumMTU() int {
// Init initialises the TUN/TAP module. You must have acquired a Listener from
// the Yggdrasil core before this point and it must not be in use elsewhere.
func (tun *TunAdapter) Init(config *config.NodeState, log *log.Logger, listener *yggdrasil.Listener, dialer *yggdrasil.Dialer) {
tun.config = config
func (tun *TunAdapter) Init(c *yggdrasil.Core, log *log.Logger, listener *yggdrasil.Listener, dialer *yggdrasil.Dialer) {
tun.core = c
tun.log = log
tun.listener = listener
tun.dialer = dialer
@ -133,9 +132,9 @@ func (tun *TunAdapter) Start() error {
}
func (tun *TunAdapter) _start() error {
current := tun.config.GetCurrent()
if tun.config == nil || tun.listener == nil || tun.dialer == nil {
return errors.New("No configuration available to TUN/TAP")
current := tun.core.GetConfig()
if tun.core == nil || tun.listener == nil || tun.dialer == nil {
return errors.New("TUN/TAP has not been initialised, call Init first")
}
var boxPub crypto.BoxPubKey
boxPubHex, err := hex.DecodeString(current.EncryptionPublicKey)
@ -200,12 +199,9 @@ func (tun *TunAdapter) _stop() error {
// UpdateConfig updates the TUN/TAP module with the provided config.NodeConfig
// and then signals the various module goroutines to reconfigure themselves if
// needed.
func (tun *TunAdapter) UpdateConfig(config *config.NodeConfig) {
func (tun *TunAdapter) UpdateConfig() {
tun.log.Debugln("Reloading TUN/TAP configuration...")
// Replace the active configuration with the supplied one
tun.config.Replace(*config)
// Notify children about the configuration change
tun.Act(nil, tun.ckr.configure)
}

View file

@ -370,13 +370,12 @@ func (c *Core) AddPeer(addr string, sintf string) error {
if err := c.CallPeer(addr, sintf); err != nil {
return err
}
c.config.Mutex.Lock()
config := c.GetConfig()
if sintf == "" {
c.config.Current.Peers = append(c.config.Current.Peers, addr)
config.Peers = append(config.Peers, addr)
} else {
c.config.Current.InterfacePeers[sintf] = append(c.config.Current.InterfacePeers[sintf], addr)
config.InterfacePeers[sintf] = append(config.InterfacePeers[sintf], addr)
}
c.config.Mutex.Unlock()
return nil
}

View file

@ -4,6 +4,7 @@ import (
"encoding/hex"
"errors"
"io/ioutil"
"sync/atomic"
"time"
"github.com/Arceliar/phony"
@ -21,7 +22,7 @@ type Core struct {
// 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
phony.Inbox
config config.NodeState // Config
config atomic.Value // *config.NodeConfig
boxPub crypto.BoxPubKey
boxPriv crypto.BoxPrivKey
sigPub crypto.SigPubKey
@ -42,7 +43,7 @@ func (c *Core) _init() error {
c.log = log.New(ioutil.Discard, "", 0)
}
current := c.config.GetCurrent()
current := c.config.Load().(*config.NodeConfig)
boxPrivHex, err := hex.DecodeString(current.EncryptionPrivateKey)
if err != nil {
@ -87,7 +88,7 @@ func (c *Core) _init() error {
// be reconnected with.
func (c *Core) _addPeerLoop() {
// Get the peers from the config - these could change!
current := c.config.GetCurrent()
current := c.GetConfig()
// Add peers from the Peers section
for _, peer := range current.Peers {
@ -109,6 +110,11 @@ func (c *Core) _addPeerLoop() {
})
}
// GetConfig atomically returns the current active node configuration.
func (c *Core) GetConfig() *config.NodeConfig {
return c.config.Load().(*config.NodeConfig)
}
// UpdateConfig updates the configuration in Core with the provided
// config.NodeConfig and then signals the various module goroutines to
// reconfigure themselves if needed.
@ -116,12 +122,16 @@ func (c *Core) UpdateConfig(config *config.NodeConfig) {
c.Act(nil, func() {
c.log.Debugln("Reloading node configuration...")
// Replace the active configuration with the supplied one
c.config.Replace(*config)
new, old := config, c.GetConfig()
c.config.Store(new)
// Notify the router and switch about the new configuration
c.router.Act(c, c.router.reconfigure)
c.switchTable.Act(c, c.switchTable.reconfigure)
c.router.Act(c, func() {
c.router.reconfigure(new, old)
})
c.switchTable.Act(c, func() {
c.switchTable.reconfigure(new, old)
})
})
}
@ -130,21 +140,17 @@ func (c *Core) UpdateConfig(config *config.NodeConfig) {
// 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).
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() {
conf, err = c._start(nc, log)
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) {
func (c *Core) _start(nc *config.NodeConfig, log *log.Logger) error {
c.log = log
c.config = config.NodeState{
Current: *nc,
Previous: *nc,
}
c.config.Store(nc)
if name := version.BuildName(); name != "unknown" {
c.log.Infoln("Build name:", name)
@ -158,23 +164,23 @@ func (c *Core) _start(nc *config.NodeConfig, log *log.Logger) (*config.NodeState
if err := c.link.init(c); err != nil {
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
return err
}
if err := c.router.start(); err != nil {
c.log.Errorln("Failed to start router")
return nil, err
return err
}
c.Act(c, c._addPeerLoop)
c.log.Infoln("Startup complete")
return &c.config, nil
return nil
}
// Stop shuts down the Yggdrasil node.

View file

@ -14,6 +14,7 @@ import (
"time"
"github.com/yggdrasil-network/yggdrasil-go/src/address"
"github.com/yggdrasil-network/yggdrasil-go/src/config"
"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
"github.com/yggdrasil-network/yggdrasil-go/src/util"
@ -79,8 +80,8 @@ func (l *link) init(c *Core) error {
return nil
}
func (l *link) reconfigure() {
l.tcp.reconfigure()
func (l *link) reconfigure(current, previous *config.NodeConfig) {
l.tcp.reconfigure(current, previous)
}
func (l *link) call(uri string, sintf string) error {

View file

@ -10,6 +10,7 @@ import (
"sync/atomic"
"time"
"github.com/yggdrasil-network/yggdrasil-go/src/config"
"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
"github.com/yggdrasil-network/yggdrasil-go/src/util"
@ -34,7 +35,7 @@ func (ps *peers) init(c *Core) {
ps.core = c
}
func (ps *peers) reconfigure() {
func (ps *peers) reconfigure(current, previous *config.NodeConfig) {
// This is where reconfiguration would go, if we had anything to do
}
@ -42,42 +43,34 @@ func (ps *peers) reconfigure() {
// because the key is in the whitelist or because the whitelist is empty.
func (ps *peers) isAllowedEncryptionPublicKey(box *crypto.BoxPubKey) bool {
boxstr := hex.EncodeToString(box[:])
ps.core.config.Mutex.RLock()
defer ps.core.config.Mutex.RUnlock()
for _, v := range ps.core.config.Current.AllowedEncryptionPublicKeys {
current := ps.core.GetConfig()
for _, v := range current.AllowedEncryptionPublicKeys {
if v == boxstr {
return true
}
}
return len(ps.core.config.Current.AllowedEncryptionPublicKeys) == 0
return len(current.AllowedEncryptionPublicKeys) == 0
}
// Adds a key to the whitelist.
func (ps *peers) addAllowedEncryptionPublicKey(box string) {
ps.core.config.Mutex.RLock()
defer ps.core.config.Mutex.RUnlock()
ps.core.config.Current.AllowedEncryptionPublicKeys =
append(ps.core.config.Current.AllowedEncryptionPublicKeys, box)
current := ps.core.GetConfig()
current.AllowedEncryptionPublicKeys = append(current.AllowedEncryptionPublicKeys, box)
}
// Removes a key from the whitelist.
func (ps *peers) removeAllowedEncryptionPublicKey(box string) {
ps.core.config.Mutex.RLock()
defer ps.core.config.Mutex.RUnlock()
for k, v := range ps.core.config.Current.AllowedEncryptionPublicKeys {
current := ps.core.GetConfig()
for k, v := range current.AllowedEncryptionPublicKeys {
if v == box {
ps.core.config.Current.AllowedEncryptionPublicKeys =
append(ps.core.config.Current.AllowedEncryptionPublicKeys[:k],
ps.core.config.Current.AllowedEncryptionPublicKeys[k+1:]...)
current.AllowedEncryptionPublicKeys = append(current.AllowedEncryptionPublicKeys[:k], current.AllowedEncryptionPublicKeys[k+1:]...)
}
}
}
// Gets the whitelist of allowed keys for incoming connections.
func (ps *peers) getAllowedEncryptionPublicKeys() []string {
ps.core.config.Mutex.RLock()
defer ps.core.config.Mutex.RUnlock()
return ps.core.config.Current.AllowedEncryptionPublicKeys
return ps.core.GetConfig().AllowedEncryptionPublicKeys
}
// Atomically gets a map[switchPort]*peer of known peers.

View file

@ -28,6 +28,7 @@ import (
"time"
"github.com/yggdrasil-network/yggdrasil-go/src/address"
"github.com/yggdrasil-network/yggdrasil-go/src/config"
"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
"github.com/yggdrasil-network/yggdrasil-go/src/util"
@ -65,9 +66,8 @@ func (r *router) init(core *Core) {
p.out = func(packets [][]byte) { r.handlePackets(p, packets) }
r.out = func(bs []byte) { p.handlePacketFrom(r, bs) }
r.nodeinfo.init(r.core)
r.core.config.Mutex.RLock()
r.nodeinfo.setNodeInfo(r.core.config.Current.NodeInfo, r.core.config.Current.NodeInfoPrivacy)
r.core.config.Mutex.RUnlock()
current := r.core.GetConfig()
r.nodeinfo.setNodeInfo(current.NodeInfo, current.NodeInfoPrivacy)
r.dht.init(r)
r.searches.init(r)
r.sessions.init(r)
@ -75,9 +75,8 @@ func (r *router) init(core *Core) {
// Reconfigures the router and any child modules. This should only ever be run
// by the router actor.
func (r *router) reconfigure() {
func (r *router) reconfigure(current, previous *config.NodeConfig) {
// Reconfigure the router
current := r.core.config.GetCurrent()
if err := r.nodeinfo.setNodeInfo(current.NodeInfo, current.NodeInfoPrivacy); err != nil {
r.core.log.Errorln("Error reloading NodeInfo:", err)
} else {

View file

@ -187,9 +187,7 @@ func (ss *sessions) createSession(theirPermKey *crypto.BoxPubKey) *sessionInfo {
sinfo.mySesPriv = *priv
sinfo.myNonce = *crypto.NewBoxNonce()
sinfo.theirMTU = 1280
ss.router.core.config.Mutex.RLock()
sinfo.myMTU = uint16(ss.router.core.config.Current.IfMTU)
ss.router.core.config.Mutex.RUnlock()
sinfo.myMTU = uint16(ss.router.core.GetConfig().IfMTU)
now := time.Now()
sinfo.timeOpened = now
sinfo.time = now

View file

@ -17,6 +17,7 @@ import (
"sync/atomic"
"time"
"github.com/yggdrasil-network/yggdrasil-go/src/config"
"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
"github.com/yggdrasil-network/yggdrasil-go/src/util"
@ -193,22 +194,21 @@ func (t *switchTable) init(core *Core) {
t.table.Store(lookupTable{})
t.drop = make(map[crypto.SigPubKey]int64)
phony.Block(t, func() {
core.config.Mutex.RLock()
if core.config.Current.SwitchOptions.MaxTotalQueueSize > SwitchQueueTotalMinSize {
t.queues.totalMaxSize = core.config.Current.SwitchOptions.MaxTotalQueueSize
current := t.core.GetConfig()
if current.SwitchOptions.MaxTotalQueueSize > SwitchQueueTotalMinSize {
t.queues.totalMaxSize = current.SwitchOptions.MaxTotalQueueSize
} else {
t.queues.totalMaxSize = SwitchQueueTotalMinSize
}
core.config.Mutex.RUnlock()
t.queues.bufs = make(map[string]switch_buffer)
t.idle = make(map[switchPort]time.Time)
})
}
func (t *switchTable) reconfigure() {
func (t *switchTable) reconfigure(current, previous *config.NodeConfig) {
// This is where reconfiguration would go, if we had anything useful to do.
t.core.link.reconfigure()
t.core.peers.reconfigure()
t.core.link.reconfigure(current, previous)
t.core.peers.reconfigure(current, previous)
}
// Safely gets a copy of this node's locator.

View file

@ -25,6 +25,7 @@ import (
"golang.org/x/net/proxy"
"github.com/yggdrasil-network/yggdrasil-go/src/config"
"github.com/yggdrasil-network/yggdrasil-go/src/util"
)
@ -81,9 +82,7 @@ func (t *tcp) init(l *link) error {
t.listeners = make(map[string]*TcpListener)
t.mutex.Unlock()
t.link.core.config.Mutex.RLock()
defer t.link.core.config.Mutex.RUnlock()
for _, listenaddr := range t.link.core.config.Current.Listen {
for _, listenaddr := range t.link.core.GetConfig().Listen {
if listenaddr[:6] != "tcp://" {
continue
}
@ -95,11 +94,9 @@ func (t *tcp) init(l *link) error {
return nil
}
func (t *tcp) reconfigure() {
t.link.core.config.Mutex.RLock()
added := util.Difference(t.link.core.config.Current.Listen, t.link.core.config.Previous.Listen)
deleted := util.Difference(t.link.core.config.Previous.Listen, t.link.core.config.Current.Listen)
t.link.core.config.Mutex.RUnlock()
func (t *tcp) reconfigure(current, previous *config.NodeConfig) {
added := util.Difference(current.Listen, previous.Listen)
deleted := util.Difference(previous.Listen, current.Listen)
if len(added) > 0 || len(deleted) > 0 {
for _, a := range added {
if a[:6] != "tcp://" {