From a072e063d8864caf24c9c6a41600756990eabb5f Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 23 Oct 2019 10:44:58 +0100 Subject: [PATCH 1/3] Define module.Module interface, update admin/tuntap/multicast modules to comply with it, fix #581 --- cmd/yggdrasil/main.go | 18 +++++++---- go.mod | 2 ++ src/admin/admin.go | 61 ++++++++++++++++++++++---------------- src/module/module.go | 20 +++++++++++++ src/multicast/multicast.go | 21 +++++++++++++ src/tuntap/tun.go | 28 ++++++++++------- 6 files changed, 108 insertions(+), 42 deletions(-) create mode 100644 src/module/module.go diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 33a8769d..91cea9a7 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -23,6 +23,7 @@ import ( "github.com/yggdrasil-network/yggdrasil-go/src/admin" "github.com/yggdrasil-network/yggdrasil-go/src/config" "github.com/yggdrasil-network/yggdrasil-go/src/crypto" + "github.com/yggdrasil-network/yggdrasil-go/src/module" "github.com/yggdrasil-network/yggdrasil-go/src/multicast" "github.com/yggdrasil-network/yggdrasil-go/src/tuntap" "github.com/yggdrasil-network/yggdrasil-go/src/version" @@ -32,9 +33,9 @@ import ( type node struct { core yggdrasil.Core state *config.NodeState - tuntap tuntap.TunAdapter - multicast multicast.Multicast - admin admin.AdminSocket + tuntap module.Module // tuntap.TunAdapter + multicast module.Module // multicast.Multicast + admin module.Module // admin.AdminSocket } func readConfig(useconf *bool, useconffile *string, normaliseconf *bool) *config.NodeConfig { @@ -231,25 +232,30 @@ func main() { } // Register the session firewall gatekeeper function n.core.SetSessionGatekeeper(n.sessionFirewall) + // Allocate our modules + n.admin = &admin.AdminSocket{} + n.multicast = &multicast.Multicast{} + n.tuntap = &tuntap.TunAdapter{} // Start the admin socket n.admin.Init(&n.core, n.state, logger, nil) if err := n.admin.Start(); err != nil { logger.Errorln("An error occurred starting admin socket:", err) } + n.admin.SetupAdminHandlers(n.admin.(*admin.AdminSocket)) // Start the multicast interface n.multicast.Init(&n.core, n.state, logger, nil) if err := n.multicast.Start(); err != nil { logger.Errorln("An error occurred starting multicast:", err) } - n.multicast.SetupAdminHandlers(&n.admin) + n.multicast.SetupAdminHandlers(n.admin.(*admin.AdminSocket)) // 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, n.state, logger, tuntap.TunOptions{Listener: listener, Dialer: dialer}) if err := n.tuntap.Start(); err != nil { logger.Errorln("An error occurred starting TUN/TAP:", err) } - n.tuntap.SetupAdminHandlers(&n.admin) + n.tuntap.SetupAdminHandlers(n.admin.(*admin.AdminSocket)) } else { logger.Errorln("Unable to get Dialer:", err) } diff --git a/go.mod b/go.mod index 83c22924..d1a3587f 100644 --- a/go.mod +++ b/go.mod @@ -17,3 +17,5 @@ require ( golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a golang.org/x/text v0.3.2 ) + +go 1.13 diff --git a/src/admin/admin.go b/src/admin/admin.go index c7fc151d..f7637998 100644 --- a/src/admin/admin.go +++ b/src/admin/admin.go @@ -25,12 +25,12 @@ import ( // TODO: Add authentication type AdminSocket struct { - core *yggdrasil.Core - log *log.Logger - reconfigure chan chan error - listenaddr string - listener net.Listener - handlers map[string]handler + core *yggdrasil.Core + log *log.Logger + listenaddr string + listener net.Listener + handlers map[string]handler + started bool } // Info refers to information that is returned to the admin socket handler. @@ -54,25 +54,27 @@ 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, state *config.NodeState, log *log.Logger, options interface{}) error { a.core = c a.log = log - a.reconfigure = make(chan chan error, 1) a.handlers = make(map[string]handler) - go func() { - for { - e := <-a.reconfigure - current, previous := state.GetCurrent(), state.GetPrevious() - if current.AdminListen != previous.AdminListen { - a.listenaddr = current.AdminListen - a.Stop() - a.Start() - } - e <- nil - } - }() current := state.GetCurrent() a.listenaddr = current.AdminListen + return nil +} + +func (a *AdminSocket) UpdateConfig(config *config.NodeConfig) { + a.log.Debugln("Reloading admin configuration...") + if a.listenaddr != config.AdminListen { + a.listenaddr = config.AdminListen + if a.IsStarted() { + a.Stop() + } + a.Start() + } +} + +func (a *AdminSocket) SetupAdminHandlers(na *AdminSocket) { a.AddHandler("list", []string{}, func(in Info) (Info, error) { handlers := make(map[string]interface{}) for handlername, handler := range a.handlers { @@ -81,15 +83,15 @@ func (a *AdminSocket) Init(c *yggdrasil.Core, state *config.NodeState, log *log. return Info{"list": handlers}, nil }) a.AddHandler("getSelf", []string{}, func(in Info) (Info, error) { - ip := c.Address().String() - subnet := c.Subnet() + ip := a.core.Address().String() + subnet := a.core.Subnet() return Info{ "self": Info{ ip: Info{ - "box_pub_key": c.EncryptionPublicKey(), + "box_pub_key": a.core.EncryptionPublicKey(), "build_name": version.BuildName(), "build_version": version.BuildVersion(), - "coords": fmt.Sprintf("%v", c.Coords()), + "coords": fmt.Sprintf("%v", a.core.Coords()), "subnet": subnet.String(), }, }, @@ -312,17 +314,24 @@ 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. +// Start runs the admin API socket to listen for / respond to admin API calls. func (a *AdminSocket) Start() error { if a.listenaddr != "none" && a.listenaddr != "" { go a.listen() + a.started = true } return nil } -// cleans up when stopping +// IsStarted returns true if the module has been started. +func (a *AdminSocket) IsStarted() bool { + return a.started +} + +// Stop will stop the admin API and close the socket. func (a *AdminSocket) Stop() error { if a.listener != nil { + a.started = false return a.listener.Close() } else { return nil diff --git a/src/module/module.go b/src/module/module.go new file mode 100644 index 00000000..ab704e70 --- /dev/null +++ b/src/module/module.go @@ -0,0 +1,20 @@ +package module + +import ( + "github.com/gologme/log" + + "github.com/yggdrasil-network/yggdrasil-go/src/admin" + "github.com/yggdrasil-network/yggdrasil-go/src/config" + "github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil" +) + +// Module is an interface that defines which functions must be supported by a +// given Yggdrasil module. +type Module interface { + Init(core *yggdrasil.Core, state *config.NodeState, log *log.Logger, options interface{}) error + Start() error + Stop() error + UpdateConfig(config *config.NodeConfig) + SetupAdminHandlers(a *admin.AdminSocket) + IsStarted() bool +} diff --git a/src/multicast/multicast.go b/src/multicast/multicast.go index 7044eaa2..6c1af1c4 100644 --- a/src/multicast/multicast.go +++ b/src/multicast/multicast.go @@ -55,6 +55,12 @@ 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 len(m.config.GetCurrent().MulticastInterfaces) == 0 { + return fmt.Errorf("no MulticastInterfaces configured") + } + + m.log.Infoln("Starting multicast module") + addr, err := net.ResolveUDPAddr("udp", m.groupAddr) if err != nil { return err @@ -80,8 +86,14 @@ func (m *Multicast) Start() error { return nil } +// IsStarted returns true if the module has been started. +func (m *Multicast) IsStarted() bool { + return m.isOpen +} + // Stop is not implemented for multicast yet. func (m *Multicast) Stop() error { + m.log.Infoln("Stopping multicast module") m.isOpen = false if m.announcer != nil { m.announcer.Stop() @@ -98,7 +110,16 @@ func (m *Multicast) Stop() error { // needed. func (m *Multicast) UpdateConfig(config *config.NodeConfig) { m.log.Debugln("Reloading multicast configuration...") + if m.IsStarted() { + if len(config.MulticastInterfaces) == 0 || config.LinkLocalTCPPort != m.listenPort { + m.Stop() + } + } m.config.Replace(*config) + m.listenPort = config.LinkLocalTCPPort + if !m.IsStarted() && len(config.MulticastInterfaces) > 0 { + m.Start() + } } // GetInterfaces returns the currently known/enabled multicast interfaces. It is diff --git a/src/tuntap/tun.go b/src/tuntap/tun.go index 5d77ecab..06feede0 100644 --- a/src/tuntap/tun.go +++ b/src/tuntap/tun.go @@ -56,6 +56,11 @@ type TunAdapter struct { isOpen bool } +type TunOptions struct { + Listener *yggdrasil.Listener + Dialer *yggdrasil.Dialer +} + // Gets the maximum supported MTU for the platform based on the defaults in // defaults.GetDefaults(). func getSupportedMTU(mtu int) int { @@ -110,16 +115,21 @@ 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) { +func (tun *TunAdapter) Init(core *yggdrasil.Core, config *config.NodeState, log *log.Logger, options interface{}) error { + tunoptions, ok := options.(TunOptions) + if !ok { + return fmt.Errorf("invalid options supplied to TunAdapter module") + } tun.config = config tun.log = log - tun.listener = listener - tun.dialer = dialer + tun.listener = tunoptions.Listener + tun.dialer = tunoptions.Dialer tun.addrToConn = make(map[address.Address]*tunConn) tun.subnetToConn = make(map[address.Subnet]*tunConn) tun.dials = make(map[string][][]byte) tun.writer.tun = tun tun.reader.tun = tun + return nil } // Start the setup process for the TUN/TAP adapter. If successful, starts the @@ -160,13 +170,6 @@ func (tun *TunAdapter) _start() error { return nil } tun.isOpen = true - tun.reconfigure = make(chan chan error) - go func() { - for { - e := <-tun.reconfigure - e <- nil - } - }() go tun.handler() tun.reader.Act(nil, tun.reader._read) // Start the reader tun.icmpv6.Init(tun) @@ -177,6 +180,11 @@ func (tun *TunAdapter) _start() error { return nil } +// IsStarted returns true if the module has been started. +func (tun *TunAdapter) IsStarted() bool { + return tun.isOpen +} + // Start the setup process for the TUN/TAP adapter. If successful, starts the // read/write goroutines to handle packets on that interface. func (tun *TunAdapter) Stop() error { From 337626a32cc38c13e6809e63aff60b2c4be1b0ae Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 23 Oct 2019 11:12:51 +0100 Subject: [PATCH 2/3] Act multicast updates for safety --- go.mod | 2 -- src/admin/admin.go | 14 +++++++------- src/multicast/multicast.go | 4 ++++ 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index d1a3587f..83c22924 100644 --- a/go.mod +++ b/go.mod @@ -17,5 +17,3 @@ require ( golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a golang.org/x/text v0.3.2 ) - -go 1.13 diff --git a/src/admin/admin.go b/src/admin/admin.go index f7637998..8028bcc6 100644 --- a/src/admin/admin.go +++ b/src/admin/admin.go @@ -60,6 +60,13 @@ func (a *AdminSocket) Init(c *yggdrasil.Core, state *config.NodeState, log *log. a.handlers = make(map[string]handler) current := state.GetCurrent() a.listenaddr = current.AdminListen + a.AddHandler("list", []string{}, func(in Info) (Info, error) { + handlers := make(map[string]interface{}) + for handlername, handler := range a.handlers { + handlers[handlername] = Info{"fields": handler.args} + } + return Info{"list": handlers}, nil + }) return nil } @@ -75,13 +82,6 @@ func (a *AdminSocket) UpdateConfig(config *config.NodeConfig) { } func (a *AdminSocket) SetupAdminHandlers(na *AdminSocket) { - a.AddHandler("list", []string{}, func(in Info) (Info, error) { - handlers := make(map[string]interface{}) - for handlername, handler := range a.handlers { - handlers[handlername] = Info{"fields": handler.args} - } - return Info{"list": handlers}, nil - }) a.AddHandler("getSelf", []string{}, func(in Info) (Info, error) { ip := a.core.Address().String() subnet := a.core.Subnet() diff --git a/src/multicast/multicast.go b/src/multicast/multicast.go index 6c1af1c4..deffecd2 100644 --- a/src/multicast/multicast.go +++ b/src/multicast/multicast.go @@ -109,6 +109,10 @@ func (m *Multicast) Stop() error { // and then signals the various module goroutines to reconfigure themselves if // needed. func (m *Multicast) UpdateConfig(config *config.NodeConfig) { + m.Act(m, func() { m._updateConfig(config) }) +} + +func (m *Multicast) _updateConfig(config *config.NodeConfig) { m.log.Debugln("Reloading multicast configuration...") if m.IsStarted() { if len(config.MulticastInterfaces) == 0 || config.LinkLocalTCPPort != m.listenPort { From cd9396993057ba25ef904a890f2e01e5b73b1286 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 24 Oct 2019 23:37:39 +0100 Subject: [PATCH 3/3] Fix isOpen for TUN/TAP actor --- src/tuntap/tun.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/tuntap/tun.go b/src/tuntap/tun.go index 06feede0..f0250c93 100644 --- a/src/tuntap/tun.go +++ b/src/tuntap/tun.go @@ -143,9 +143,12 @@ func (tun *TunAdapter) Start() error { } func (tun *TunAdapter) _start() error { + if tun.isOpen { + return errors.New("TUN/TAP module is already started") + } current := tun.config.GetCurrent() if tun.config == nil || tun.listener == nil || tun.dialer == nil { - return errors.New("No configuration available to TUN/TAP") + return errors.New("no configuration available to TUN/TAP") } var boxPub crypto.BoxPubKey boxPubHex, err := hex.DecodeString(current.EncryptionPublicKey) @@ -182,7 +185,11 @@ func (tun *TunAdapter) _start() error { // IsStarted returns true if the module has been started. func (tun *TunAdapter) IsStarted() bool { - return tun.isOpen + var isOpen bool + phony.Block(tun, func() { + isOpen = tun.isOpen + }) + return isOpen } // Start the setup process for the TUN/TAP adapter. If successful, starts the