Define module.Module interface, update admin/tuntap/multicast modules to comply with it, fix #581

This commit is contained in:
Neil Alexander 2019-10-23 10:44:58 +01:00
parent fc71624919
commit a072e063d8
No known key found for this signature in database
GPG Key ID: A02A2019A2BB0944
6 changed files with 108 additions and 42 deletions

View File

@ -23,6 +23,7 @@ import (
"github.com/yggdrasil-network/yggdrasil-go/src/admin" "github.com/yggdrasil-network/yggdrasil-go/src/admin"
"github.com/yggdrasil-network/yggdrasil-go/src/config" "github.com/yggdrasil-network/yggdrasil-go/src/config"
"github.com/yggdrasil-network/yggdrasil-go/src/crypto" "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/multicast"
"github.com/yggdrasil-network/yggdrasil-go/src/tuntap" "github.com/yggdrasil-network/yggdrasil-go/src/tuntap"
"github.com/yggdrasil-network/yggdrasil-go/src/version" "github.com/yggdrasil-network/yggdrasil-go/src/version"
@ -32,9 +33,9 @@ import (
type node struct { type node struct {
core yggdrasil.Core core yggdrasil.Core
state *config.NodeState state *config.NodeState
tuntap tuntap.TunAdapter tuntap module.Module // tuntap.TunAdapter
multicast multicast.Multicast multicast module.Module // multicast.Multicast
admin admin.AdminSocket admin module.Module // admin.AdminSocket
} }
func readConfig(useconf *bool, useconffile *string, normaliseconf *bool) *config.NodeConfig { func readConfig(useconf *bool, useconffile *string, normaliseconf *bool) *config.NodeConfig {
@ -231,25 +232,30 @@ func main() {
} }
// Register the session firewall gatekeeper function // Register the session firewall gatekeeper function
n.core.SetSessionGatekeeper(n.sessionFirewall) n.core.SetSessionGatekeeper(n.sessionFirewall)
// Allocate our modules
n.admin = &admin.AdminSocket{}
n.multicast = &multicast.Multicast{}
n.tuntap = &tuntap.TunAdapter{}
// Start the admin socket // Start the admin socket
n.admin.Init(&n.core, n.state, logger, nil) n.admin.Init(&n.core, n.state, logger, nil)
if err := n.admin.Start(); err != nil { if err := n.admin.Start(); err != nil {
logger.Errorln("An error occurred starting admin socket:", err) logger.Errorln("An error occurred starting admin socket:", err)
} }
n.admin.SetupAdminHandlers(n.admin.(*admin.AdminSocket))
// Start the multicast interface // Start the multicast interface
n.multicast.Init(&n.core, n.state, logger, nil) n.multicast.Init(&n.core, n.state, logger, nil)
if err := n.multicast.Start(); err != nil { if err := n.multicast.Start(); err != nil {
logger.Errorln("An error occurred starting multicast:", err) 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 // Start the TUN/TAP interface
if listener, err := n.core.ConnListen(); err == nil { if listener, err := n.core.ConnListen(); err == nil {
if dialer, err := n.core.ConnDialer(); 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 { if err := n.tuntap.Start(); err != nil {
logger.Errorln("An error occurred starting TUN/TAP:", err) logger.Errorln("An error occurred starting TUN/TAP:", err)
} }
n.tuntap.SetupAdminHandlers(&n.admin) n.tuntap.SetupAdminHandlers(n.admin.(*admin.AdminSocket))
} else { } else {
logger.Errorln("Unable to get Dialer:", err) logger.Errorln("Unable to get Dialer:", err)
} }

2
go.mod
View File

@ -17,3 +17,5 @@ require (
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a
golang.org/x/text v0.3.2 golang.org/x/text v0.3.2
) )
go 1.13

View File

@ -25,12 +25,12 @@ import (
// TODO: Add authentication // TODO: Add authentication
type AdminSocket struct { type AdminSocket struct {
core *yggdrasil.Core core *yggdrasil.Core
log *log.Logger log *log.Logger
reconfigure chan chan error listenaddr string
listenaddr string listener net.Listener
listener net.Listener handlers map[string]handler
handlers map[string]handler started bool
} }
// Info refers to information that is returned to the admin socket handler. // 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. // 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.core = c
a.log = log a.log = log
a.reconfigure = make(chan chan error, 1)
a.handlers = make(map[string]handler) 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() current := state.GetCurrent()
a.listenaddr = current.AdminListen 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) { a.AddHandler("list", []string{}, func(in Info) (Info, error) {
handlers := make(map[string]interface{}) handlers := make(map[string]interface{})
for handlername, handler := range a.handlers { 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 return Info{"list": handlers}, nil
}) })
a.AddHandler("getSelf", []string{}, func(in Info) (Info, error) { a.AddHandler("getSelf", []string{}, func(in Info) (Info, error) {
ip := c.Address().String() ip := a.core.Address().String()
subnet := c.Subnet() subnet := a.core.Subnet()
return Info{ return Info{
"self": Info{ "self": Info{
ip: Info{ ip: Info{
"box_pub_key": c.EncryptionPublicKey(), "box_pub_key": a.core.EncryptionPublicKey(),
"build_name": version.BuildName(), "build_name": version.BuildName(),
"build_version": version.BuildVersion(), "build_version": version.BuildVersion(),
"coords": fmt.Sprintf("%v", c.Coords()), "coords": fmt.Sprintf("%v", a.core.Coords()),
"subnet": subnet.String(), "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 { func (a *AdminSocket) Start() error {
if a.listenaddr != "none" && a.listenaddr != "" { if a.listenaddr != "none" && a.listenaddr != "" {
go a.listen() go a.listen()
a.started = true
} }
return nil 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 { func (a *AdminSocket) Stop() error {
if a.listener != nil { if a.listener != nil {
a.started = false
return a.listener.Close() return a.listener.Close()
} else { } else {
return nil return nil

20
src/module/module.go Normal file
View File

@ -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
}

View File

@ -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 // listen for multicast beacons from other hosts and will advertise multicast
// beacons out to the network. // beacons out to the network.
func (m *Multicast) Start() error { 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) addr, err := net.ResolveUDPAddr("udp", m.groupAddr)
if err != nil { if err != nil {
return err return err
@ -80,8 +86,14 @@ func (m *Multicast) Start() error {
return nil 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. // Stop is not implemented for multicast yet.
func (m *Multicast) Stop() error { func (m *Multicast) Stop() error {
m.log.Infoln("Stopping multicast module")
m.isOpen = false m.isOpen = false
if m.announcer != nil { if m.announcer != nil {
m.announcer.Stop() m.announcer.Stop()
@ -98,7 +110,16 @@ func (m *Multicast) Stop() error {
// needed. // needed.
func (m *Multicast) UpdateConfig(config *config.NodeConfig) { func (m *Multicast) UpdateConfig(config *config.NodeConfig) {
m.log.Debugln("Reloading multicast configuration...") 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.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 // GetInterfaces returns the currently known/enabled multicast interfaces. It is

View File

@ -56,6 +56,11 @@ type TunAdapter struct {
isOpen bool isOpen bool
} }
type TunOptions struct {
Listener *yggdrasil.Listener
Dialer *yggdrasil.Dialer
}
// Gets the maximum supported MTU for the platform based on the defaults in // Gets the maximum supported MTU for the platform based on the defaults in
// defaults.GetDefaults(). // defaults.GetDefaults().
func getSupportedMTU(mtu int) int { 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 // 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. // 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.config = config
tun.log = log tun.log = log
tun.listener = listener tun.listener = tunoptions.Listener
tun.dialer = dialer tun.dialer = tunoptions.Dialer
tun.addrToConn = make(map[address.Address]*tunConn) tun.addrToConn = make(map[address.Address]*tunConn)
tun.subnetToConn = make(map[address.Subnet]*tunConn) tun.subnetToConn = make(map[address.Subnet]*tunConn)
tun.dials = make(map[string][][]byte) tun.dials = make(map[string][][]byte)
tun.writer.tun = tun tun.writer.tun = tun
tun.reader.tun = tun tun.reader.tun = tun
return nil
} }
// Start the setup process for the TUN/TAP adapter. If successful, starts the // Start the setup process for the TUN/TAP adapter. If successful, starts the
@ -160,13 +170,6 @@ func (tun *TunAdapter) _start() error {
return nil return nil
} }
tun.isOpen = true tun.isOpen = true
tun.reconfigure = make(chan chan error)
go func() {
for {
e := <-tun.reconfigure
e <- nil
}
}()
go tun.handler() go tun.handler()
tun.reader.Act(nil, tun.reader._read) // Start the reader tun.reader.Act(nil, tun.reader._read) // Start the reader
tun.icmpv6.Init(tun) tun.icmpv6.Init(tun)
@ -177,6 +180,11 @@ func (tun *TunAdapter) _start() error {
return nil 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 // Start the setup process for the TUN/TAP adapter. If successful, starts the
// read/write goroutines to handle packets on that interface. // read/write goroutines to handle packets on that interface.
func (tun *TunAdapter) Stop() error { func (tun *TunAdapter) Stop() error {