Merge pull request #929 from yggdrasil-network/neilalexander/refactor

Node setup refactoring
This commit is contained in:
Arceliar 2022-08-28 13:46:42 -05:00 committed by GitHub
commit af99fa4f6b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 214 additions and 176 deletions

View File

@ -36,7 +36,7 @@ import (
) )
type node struct { type node struct {
core core.Core core *core.Core
config *config.NodeConfig config *config.NodeConfig
tuntap *tuntap.TunAdapter tuntap *tuntap.TunAdapter
multicast *multicast.Multicast multicast *multicast.Multicast
@ -327,11 +327,32 @@ func run(args yggArgs, ctx context.Context, done chan struct{}) {
// Setup the Yggdrasil node itself. The node{} type includes a Core, so we // Setup the Yggdrasil node itself. The node{} type includes a Core, so we
// don't need to create this manually. // don't need to create this manually.
sk, err := hex.DecodeString(cfg.PrivateKey)
if err != nil {
panic(err)
}
options := []core.SetupOption{
core.IfName(cfg.IfName),
core.IfMTU(cfg.IfMTU),
}
for _, peer := range cfg.Peers {
options = append(options, core.Peer{URI: peer})
}
for intf, peers := range cfg.InterfacePeers {
for _, peer := range peers {
options = append(options, core.Peer{URI: peer, SourceInterface: intf})
}
}
for _, allowed := range cfg.AllowedPublicKeys {
k, err := hex.DecodeString(allowed)
if err != nil {
panic(err)
}
options = append(options, core.AllowedPublicKey(k[:]))
}
n := node{config: cfg} n := node{config: cfg}
// Now start Yggdrasil - this starts the DHT, router, switch and other core n.core, err = core.New(sk[:], options...)
// components needed for Yggdrasil to operate if err != nil {
if err = n.core.Start(cfg, logger); err != nil {
logger.Errorln("An error occurred during startup")
panic(err) panic(err)
} }
// Register the session firewall gatekeeper function // Register the session firewall gatekeeper function
@ -340,21 +361,21 @@ func run(args yggArgs, ctx context.Context, done chan struct{}) {
n.multicast = &multicast.Multicast{} n.multicast = &multicast.Multicast{}
n.tuntap = &tuntap.TunAdapter{} n.tuntap = &tuntap.TunAdapter{}
// Start the admin socket // Start the admin socket
if err := n.admin.Init(&n.core, cfg, logger, nil); err != nil { if err := n.admin.Init(n.core, cfg, logger, nil); err != nil {
logger.Errorln("An error occurred initialising admin socket:", err) logger.Errorln("An error occurred initialising admin socket:", err)
} else if err := n.admin.Start(); err != nil { } else 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) n.admin.SetupAdminHandlers(n.admin)
// Start the multicast interface // Start the multicast interface
if err := n.multicast.Init(&n.core, cfg, logger, nil); err != nil { if err := n.multicast.Init(n.core, cfg, logger, nil); err != nil {
logger.Errorln("An error occurred initialising multicast:", err) logger.Errorln("An error occurred initialising multicast:", err)
} else if err := n.multicast.Start(); err != nil { } else 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)
// Start the TUN/TAP interface // Start the TUN/TAP interface
rwc := ipv6rwc.NewReadWriteCloser(&n.core) rwc := ipv6rwc.NewReadWriteCloser(n.core)
if err := n.tuntap.Init(rwc, cfg, logger, nil); err != nil { if err := n.tuntap.Init(rwc, cfg, logger, nil); err != nil {
logger.Errorln("An error occurred initialising TUN/TAP:", err) logger.Errorln("An error occurred initialising TUN/TAP:", err)
} else if err := n.tuntap.Start(); err != nil { } else if err := n.tuntap.Start(); err != nil {

View File

@ -25,7 +25,7 @@ import (
// functions. Note that in the case of iOS we handle reading/writing to/from TUN // functions. Note that in the case of iOS we handle reading/writing to/from TUN
// in Swift therefore we use the "dummy" TUN interface instead. // in Swift therefore we use the "dummy" TUN interface instead.
type Yggdrasil struct { type Yggdrasil struct {
core core.Core core *core.Core
iprwc *ipv6rwc.ReadWriteCloser iprwc *ipv6rwc.ReadWriteCloser
config *config.NodeConfig config *config.NodeConfig
multicast multicast.Multicast multicast multicast.Multicast
@ -48,19 +48,42 @@ func (m *Yggdrasil) StartJSON(configjson []byte) error {
if err := json.Unmarshal(configjson, &m.config); err != nil { if err := json.Unmarshal(configjson, &m.config); err != nil {
return err return err
} }
m.config.IfName = "none" // Setup the Yggdrasil node itself.
if err := m.core.Start(m.config, logger); err != nil { sk, err := hex.DecodeString(m.config.PrivateKey)
logger.Errorln("An error occured starting Yggdrasil:", err) if err != nil {
return err panic(err)
}
options := []core.SetupOption{
core.IfName("none"),
core.IfMTU(m.config.IfMTU),
}
for _, peer := range m.config.Peers {
options = append(options, core.Peer{URI: peer})
}
for intf, peers := range m.config.InterfacePeers {
for _, peer := range peers {
options = append(options, core.Peer{URI: peer, SourceInterface: intf})
}
}
for _, allowed := range m.config.AllowedPublicKeys {
k, err := hex.DecodeString(allowed)
if err != nil {
panic(err)
}
options = append(options, core.AllowedPublicKey(k[:]))
}
m.core, err = core.New(sk[:], options...)
if err != nil {
panic(err)
} }
mtu := m.config.IfMTU mtu := m.config.IfMTU
m.iprwc = ipv6rwc.NewReadWriteCloser(&m.core) m.iprwc = ipv6rwc.NewReadWriteCloser(m.core)
if m.iprwc.MaxMTU() < mtu { if m.iprwc.MaxMTU() < mtu {
mtu = m.iprwc.MaxMTU() mtu = m.iprwc.MaxMTU()
} }
m.iprwc.SetMTU(mtu) m.iprwc.SetMTU(mtu)
if len(m.config.MulticastInterfaces) > 0 { if len(m.config.MulticastInterfaces) > 0 {
if err := m.multicast.Init(&m.core, m.config, logger, nil); err != nil { if err := m.multicast.Init(m.core, m.config, logger, nil); err != nil {
logger.Errorln("An error occurred initialising multicast:", err) logger.Errorln("An error occurred initialising multicast:", err)
} else if err := m.multicast.Start(); err != nil { } else if err := m.multicast.Start(); err != nil {
logger.Errorln("An error occurred starting multicast:", err) logger.Errorln("An error occurred starting multicast:", err)
@ -136,18 +159,18 @@ func (m *Yggdrasil) GetCoordsString() string {
func (m *Yggdrasil) GetPeersJSON() (result string) { func (m *Yggdrasil) GetPeersJSON() (result string) {
peers := []struct { peers := []struct {
core.Peer core.PeerInfo
IP string IP string
}{} }{}
for _, v := range m.core.GetPeers() { for _, v := range m.core.GetPeers() {
a := address.AddrForKey(v.Key) a := address.AddrForKey(v.Key)
ip := net.IP(a[:]).String() ip := net.IP(a[:]).String()
peers = append(peers, struct { peers = append(peers, struct {
core.Peer core.PeerInfo
IP string IP string
}{ }{
Peer: v, PeerInfo: v,
IP: ip, IP: ip,
}) })
} }
if res, err := json.Marshal(peers); err == nil { if res, err := json.Marshal(peers); err == nil {

2
go.mod
View File

@ -12,7 +12,7 @@ require (
github.com/kardianos/minwinsvc v1.0.0 github.com/kardianos/minwinsvc v1.0.0
github.com/mitchellh/mapstructure v1.4.1 github.com/mitchellh/mapstructure v1.4.1
github.com/vishvananda/netlink v1.1.0 github.com/vishvananda/netlink v1.1.0
golang.org/x/mobile v0.0.0-20220112015953-858099ff7816 golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105
golang.org/x/net v0.0.0-20211101193420-4a448f8816b3 golang.org/x/net v0.0.0-20211101193420-4a448f8816b3
golang.org/x/sys v0.0.0-20211102192858-4dd72447c267 golang.org/x/sys v0.0.0-20211102192858-4dd72447c267
golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b

2
go.sum
View File

@ -52,6 +52,8 @@ golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+o
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20220112015953-858099ff7816 h1:jhDgkcu3yQ4tasBZ+1YwDmK7eFmuVf1w1k+NGGGxfmE= golang.org/x/mobile v0.0.0-20220112015953-858099ff7816 h1:jhDgkcu3yQ4tasBZ+1YwDmK7eFmuVf1w1k+NGGGxfmE=
golang.org/x/mobile v0.0.0-20220112015953-858099ff7816/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ= golang.org/x/mobile v0.0.0-20220112015953-858099ff7816/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ=
golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105 h1:3vUV5x5+3LfQbgk7paCM6INOaJG9xXQbn79xoNkwfIk=
golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=

View File

@ -21,13 +21,13 @@ import (
//"github.com/Arceliar/phony" //"github.com/Arceliar/phony"
) )
type Self struct { type SelfInfo struct {
Key ed25519.PublicKey Key ed25519.PublicKey
Root ed25519.PublicKey Root ed25519.PublicKey
Coords []uint64 Coords []uint64
} }
type Peer struct { type PeerInfo struct {
Key ed25519.PublicKey Key ed25519.PublicKey
Root ed25519.PublicKey Root ed25519.PublicKey
Coords []uint64 Coords []uint64
@ -38,23 +38,23 @@ type Peer struct {
Uptime time.Duration Uptime time.Duration
} }
type DHTEntry struct { type DHTEntryInfo struct {
Key ed25519.PublicKey Key ed25519.PublicKey
Port uint64 Port uint64
Rest uint64 Rest uint64
} }
type PathEntry struct { type PathEntryInfo struct {
Key ed25519.PublicKey Key ed25519.PublicKey
Path []uint64 Path []uint64
} }
type Session struct { type SessionInfo struct {
Key ed25519.PublicKey Key ed25519.PublicKey
} }
func (c *Core) GetSelf() Self { func (c *Core) GetSelf() SelfInfo {
var self Self var self SelfInfo
s := c.PacketConn.PacketConn.Debug.GetSelf() s := c.PacketConn.PacketConn.Debug.GetSelf()
self.Key = s.Key self.Key = s.Key
self.Root = s.Root self.Root = s.Root
@ -62,8 +62,8 @@ func (c *Core) GetSelf() Self {
return self return self
} }
func (c *Core) GetPeers() []Peer { func (c *Core) GetPeers() []PeerInfo {
var peers []Peer var peers []PeerInfo
names := make(map[net.Conn]string) names := make(map[net.Conn]string)
c.links.mutex.Lock() c.links.mutex.Lock()
for _, info := range c.links.links { for _, info := range c.links.links {
@ -72,7 +72,7 @@ func (c *Core) GetPeers() []Peer {
c.links.mutex.Unlock() c.links.mutex.Unlock()
ps := c.PacketConn.PacketConn.Debug.GetPeers() ps := c.PacketConn.PacketConn.Debug.GetPeers()
for _, p := range ps { for _, p := range ps {
var info Peer var info PeerInfo
info.Key = p.Key info.Key = p.Key
info.Root = p.Root info.Root = p.Root
info.Coords = p.Coords info.Coords = p.Coords
@ -91,11 +91,11 @@ func (c *Core) GetPeers() []Peer {
return peers return peers
} }
func (c *Core) GetDHT() []DHTEntry { func (c *Core) GetDHT() []DHTEntryInfo {
var dhts []DHTEntry var dhts []DHTEntryInfo
ds := c.PacketConn.PacketConn.Debug.GetDHT() ds := c.PacketConn.PacketConn.Debug.GetDHT()
for _, d := range ds { for _, d := range ds {
var info DHTEntry var info DHTEntryInfo
info.Key = d.Key info.Key = d.Key
info.Port = d.Port info.Port = d.Port
info.Rest = d.Rest info.Rest = d.Rest
@ -104,11 +104,11 @@ func (c *Core) GetDHT() []DHTEntry {
return dhts return dhts
} }
func (c *Core) GetPaths() []PathEntry { func (c *Core) GetPaths() []PathEntryInfo {
var paths []PathEntry var paths []PathEntryInfo
ps := c.PacketConn.PacketConn.Debug.GetPaths() ps := c.PacketConn.PacketConn.Debug.GetPaths()
for _, p := range ps { for _, p := range ps {
var info PathEntry var info PathEntryInfo
info.Key = p.Key info.Key = p.Key
info.Path = p.Path info.Path = p.Path
paths = append(paths, info) paths = append(paths, info)
@ -116,11 +116,11 @@ func (c *Core) GetPaths() []PathEntry {
return paths return paths
} }
func (c *Core) GetSessions() []Session { func (c *Core) GetSessions() []SessionInfo {
var sessions []Session var sessions []SessionInfo
ss := c.PacketConn.Debug.GetSessions() ss := c.PacketConn.Debug.GetSessions()
for _, s := range ss { for _, s := range ss {
var info Session var info SessionInfo
info.Key = s.Key info.Key = s.Key
sessions = append(sessions, info) sessions = append(sessions, info)
} }

View File

@ -3,12 +3,11 @@ package core
import ( import (
"context" "context"
"crypto/ed25519" "crypto/ed25519"
"encoding/hex"
"errors"
"fmt" "fmt"
"io" "io"
"net" "net"
"net/url" "net/url"
"os"
"time" "time"
iwe "github.com/Arceliar/ironwood/encrypted" iwe "github.com/Arceliar/ironwood/encrypted"
@ -16,9 +15,8 @@ import (
"github.com/Arceliar/phony" "github.com/Arceliar/phony"
"github.com/gologme/log" "github.com/gologme/log"
"github.com/yggdrasil-network/yggdrasil-go/src/config"
//"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
"github.com/yggdrasil-network/yggdrasil-go/src/version" "github.com/yggdrasil-network/yggdrasil-go/src/version"
//"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
) )
// The Core object represents the Yggdrasil node. You should create a Core // The Core object represents the Yggdrasil node. You should create a Core
@ -29,62 +27,98 @@ type Core struct {
// guarantee that it will be covered by the mutex // guarantee that it will be covered by the mutex
phony.Inbox phony.Inbox
*iwe.PacketConn *iwe.PacketConn
config *config.NodeConfig // Config ctx context.Context
cancel context.CancelFunc
secret ed25519.PrivateKey secret ed25519.PrivateKey
public ed25519.PublicKey public ed25519.PublicKey
links links links links
proto protoHandler proto protoHandler
log *log.Logger log *log.Logger
addPeerTimer *time.Timer addPeerTimer *time.Timer
ctx context.Context config struct {
ctxCancel context.CancelFunc _peers map[Peer]struct{} // configurable after startup
_listeners map[ListenAddress]struct{} // configurable after startup
nodeinfo NodeInfo // immutable after startup
nodeinfoPrivacy NodeInfoPrivacy // immutable after startup
ifname IfName // immutable after startup
ifmtu IfMTU // immutable after startup
_allowedPublicKeys map[[32]byte]struct{} // configurable after startup
}
} }
func (c *Core) _init() error { func New(secret ed25519.PrivateKey, opts ...SetupOption) (*Core, error) {
// TODO separate init and start functions if len(secret) != ed25519.PrivateKeySize {
// Init sets up structs return nil, fmt.Errorf("private key is incorrect length")
// Start launches goroutines that depend on structs being set up }
// This is pretty much required to completely avoid race conditions c := &Core{
c.config.RLock() secret: secret,
defer c.config.RUnlock() public: secret.Public().(ed25519.PublicKey),
log: log.New(os.Stdout, "", 0), // TODO: not this
}
c.ctx, c.cancel = context.WithCancel(context.Background())
var err error
if c.PacketConn, err = iwe.NewPacketConn(c.secret); err != nil {
return nil, fmt.Errorf("error creating encryption: %w", err)
}
c.config._peers = map[Peer]struct{}{}
c.config._listeners = map[ListenAddress]struct{}{}
c.config._allowedPublicKeys = map[[32]byte]struct{}{}
for _, opt := range opts {
c._applyOption(opt)
}
if c.log == nil { if c.log == nil {
c.log = log.New(io.Discard, "", 0) c.log = log.New(io.Discard, "", 0)
} }
sigPriv, err := hex.DecodeString(c.config.PrivateKey)
if err != nil {
return err
}
if len(sigPriv) < ed25519.PrivateKeySize {
return errors.New("PrivateKey is incorrect length")
}
c.secret = ed25519.PrivateKey(sigPriv)
c.public = c.secret.Public().(ed25519.PublicKey)
// TODO check public against current.PublicKey, error if they don't match
c.PacketConn, err = iwe.NewPacketConn(c.secret)
c.ctx, c.ctxCancel = context.WithCancel(context.Background())
c.proto.init(c) c.proto.init(c)
if err := c.proto.nodeinfo.setNodeInfo(c.config.NodeInfo, c.config.NodeInfoPrivacy); err != nil { if err := c.links.init(c); err != nil {
return fmt.Errorf("setNodeInfo: %w", err) return nil, fmt.Errorf("error initialising links: %w", err)
}
if err := c.proto.nodeinfo.setNodeInfo(c.config.nodeinfo, bool(c.config.nodeinfoPrivacy)); err != nil {
return nil, fmt.Errorf("error setting node info: %w", err)
}
c.addPeerTimer = time.AfterFunc(time.Minute, func() {
c.Act(nil, c._addPeerLoop)
})
if name := version.BuildName(); name != "unknown" {
c.log.Infoln("Build name:", name)
}
if version := version.BuildVersion(); version != "unknown" {
c.log.Infoln("Build version:", version)
}
return c, nil
}
func (c *Core) _applyOption(opt SetupOption) {
switch v := opt.(type) {
case Peer:
c.config._peers[v] = struct{}{}
case ListenAddress:
c.config._listeners[v] = struct{}{}
case NodeInfo:
c.config.nodeinfo = v
case NodeInfoPrivacy:
c.config.nodeinfoPrivacy = v
case IfName:
c.config.ifname = v
case IfMTU:
c.config.ifmtu = v
case AllowedPublicKey:
pk := [32]byte{}
copy(pk[:], v)
c.config._allowedPublicKeys[pk] = struct{}{}
} }
return err
} }
// If any static peers were provided in the configuration above then we should // If any static peers were provided in the configuration above then we should
// configure them. The loop ensures that disconnected peers will eventually // configure them. The loop ensures that disconnected peers will eventually
// be reconnected with. // be reconnected with.
func (c *Core) _addPeerLoop() { func (c *Core) _addPeerLoop() {
c.config.RLock()
defer c.config.RUnlock()
if c.addPeerTimer == nil { if c.addPeerTimer == nil {
return return
} }
// Add peers from the Peers section // Add peers from the Peers section
for _, peer := range c.config.Peers { for peer := range c.config._peers {
go func(peer string, intf string) { go func(peer string, intf string) {
u, err := url.Parse(peer) u, err := url.Parse(peer)
if err != nil { if err != nil {
@ -93,22 +127,7 @@ func (c *Core) _addPeerLoop() {
if err := c.CallPeer(u, intf); err != nil { if err := c.CallPeer(u, intf); err != nil {
c.log.Errorln("Failed to add peer:", err) c.log.Errorln("Failed to add peer:", err)
} }
}(peer, "") // TODO: this should be acted and not in a goroutine? }(peer.URI, peer.SourceInterface) // TODO: this should be acted and not in a goroutine?
}
// Add peers from the InterfacePeers section
for intf, intfpeers := range c.config.InterfacePeers {
for _, peer := range intfpeers {
go func(peer string, intf string) {
u, err := url.Parse(peer)
if err != nil {
c.log.Errorln("Failed to parse peer url:", peer, err)
}
if err := c.CallPeer(u, intf); err != nil {
c.log.Errorln("Failed to add peer:", err)
}
}(peer, intf) // TODO: this should be acted and not in a goroutine?
}
} }
c.addPeerTimer = time.AfterFunc(time.Minute, func() { c.addPeerTimer = time.AfterFunc(time.Minute, func() {
@ -116,49 +135,6 @@ func (c *Core) _addPeerLoop() {
}) })
} }
// 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).
func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) (err error) {
phony.Block(c, func() {
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) error {
c.log = log
c.config = nc
if name := version.BuildName(); name != "unknown" {
c.log.Infoln("Build name:", name)
}
if version := version.BuildVersion(); version != "unknown" {
c.log.Infoln("Build version:", version)
}
c.log.Infoln("Starting up...")
if err := c._init(); err != nil {
c.log.Errorln("Failed to initialize core")
return err
}
if err := c.links.init(c); err != nil {
c.log.Errorln("Failed to start link interfaces")
return err
}
c.addPeerTimer = time.AfterFunc(0, func() {
c.Act(nil, c._addPeerLoop)
})
c.log.Infoln("Startup complete")
return nil
}
// Stop shuts down the Yggdrasil node. // Stop shuts down the Yggdrasil node.
func (c *Core) Stop() { func (c *Core) Stop() {
phony.Block(c, func() { phony.Block(c, func() {
@ -168,17 +144,9 @@ func (c *Core) Stop() {
}) })
} }
func (c *Core) Close() error {
var err error
phony.Block(c, func() {
err = c._close()
})
return err
}
// This function is unsafe and should only be ran by the core actor. // This function is unsafe and should only be ran by the core actor.
func (c *Core) _close() error { func (c *Core) _close() error {
c.ctxCancel() c.cancel()
err := c.PacketConn.Close() err := c.PacketConn.Close()
if c.addPeerTimer != nil { if c.addPeerTimer != nil {
c.addPeerTimer.Stop() c.addPeerTimer.Stop()

View File

@ -2,6 +2,7 @@ package core
import ( import (
"bytes" "bytes"
"crypto/ed25519"
"math/rand" "math/rand"
"net/url" "net/url"
"os" "os"
@ -9,21 +10,8 @@ import (
"time" "time"
"github.com/gologme/log" "github.com/gologme/log"
"github.com/yggdrasil-network/yggdrasil-go/src/config"
"github.com/yggdrasil-network/yggdrasil-go/src/defaults"
) )
// GenerateConfig produces default configuration with suitable modifications for tests.
func GenerateConfig() *config.NodeConfig {
cfg := defaults.GenerateConfig()
cfg.AdminListen = "none"
cfg.Listen = []string{"tcp://127.0.0.1:0"}
cfg.IfName = "none"
return cfg
}
// GetLoggerWithPrefix creates a new logger instance with prefix. // GetLoggerWithPrefix creates a new logger instance with prefix.
// If verbose is set to true, three log levels are enabled: "info", "warn", "error". // If verbose is set to true, three log levels are enabled: "info", "warn", "error".
func GetLoggerWithPrefix(prefix string, verbose bool) *log.Logger { func GetLoggerWithPrefix(prefix string, verbose bool) *log.Logger {
@ -40,13 +28,18 @@ func GetLoggerWithPrefix(prefix string, verbose bool) *log.Logger {
// CreateAndConnectTwo creates two nodes. nodeB connects to nodeA. // CreateAndConnectTwo creates two nodes. nodeB connects to nodeA.
// Verbosity flag is passed to logger. // Verbosity flag is passed to logger.
func CreateAndConnectTwo(t testing.TB, verbose bool) (nodeA *Core, nodeB *Core) { func CreateAndConnectTwo(t testing.TB, verbose bool) (nodeA *Core, nodeB *Core) {
nodeA = new(Core) var err error
if err := nodeA.Start(GenerateConfig(), GetLoggerWithPrefix("A: ", verbose)); err != nil { var skA, skB ed25519.PrivateKey
if _, skA, err = ed25519.GenerateKey(nil); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if _, skB, err = ed25519.GenerateKey(nil); err != nil {
nodeB = new(Core) t.Fatal(err)
if err := nodeB.Start(GenerateConfig(), GetLoggerWithPrefix("B: ", verbose)); err != nil { }
if nodeA, err = New(skA, ListenAddress("tcp://127.0.0.1:0"), IfName("none")); err != nil {
t.Fatal(err)
}
if nodeB, err = New(skB, ListenAddress("tcp://127.0.0.1:0"), IfName("none")); err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -1,6 +1,7 @@
package core package core
import ( import (
"bytes"
"crypto/ed25519" "crypto/ed25519"
"encoding/hex" "encoding/hex"
"errors" "errors"
@ -16,6 +17,7 @@ import (
"sync/atomic" "sync/atomic"
"github.com/Arceliar/phony"
"github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/address"
"github.com/yggdrasil-network/yggdrasil-go/src/util" "github.com/yggdrasil-network/yggdrasil-go/src/util"
"golang.org/x/net/proxy" "golang.org/x/net/proxy"
@ -61,7 +63,14 @@ func (l *links) init(c *Core) error {
l.mutex.Unlock() l.mutex.Unlock()
l.stopped = make(chan struct{}) l.stopped = make(chan struct{})
if err := l.tcp.init(l); err != nil { var listeners []ListenAddress
phony.Block(c, func() {
listeners = make([]ListenAddress, 0, len(c.config._listeners))
for listener := range c.config._listeners {
listeners = append(listeners, listener)
}
})
if err := l.tcp.init(l, listeners); err != nil {
c.log.Errorln("Failed to start TCP interface") c.log.Errorln("Failed to start TCP interface")
return err return err
} }
@ -70,10 +79,6 @@ func (l *links) init(c *Core) error {
} }
func (l *links) call(u *url.URL, sintf string) error { func (l *links) call(u *url.URL, sintf string) error {
//u, err := url.Parse(uri)
//if err != nil {
// return fmt.Errorf("peer %s is not correctly formatted (%s)", uri, err)
//}
tcpOpts := tcpOptions{} tcpOpts := tcpOptions{}
if pubkeys, ok := u.Query()["key"]; ok && len(pubkeys) > 0 { if pubkeys, ok := u.Query()["key"]; ok && len(pubkeys) > 0 {
tcpOpts.pinnedEd25519Keys = make(map[keyArray]struct{}) tcpOpts.pinnedEd25519Keys = make(map[keyArray]struct{})
@ -215,12 +220,10 @@ func (intf *link) handler() (chan struct{}, error) {
} }
} }
// Check if we're authorized to connect to this key / IP // Check if we're authorized to connect to this key / IP
intf.links.core.config.RLock() allowed := intf.links.core.config._allowedPublicKeys
allowed := intf.links.core.config.AllowedPublicKeys
intf.links.core.config.RUnlock()
isallowed := len(allowed) == 0 isallowed := len(allowed) == 0
for _, k := range allowed { for k := range allowed {
if k == hex.EncodeToString(meta.key) { // TODO: this is yuck if bytes.Equal(k[:], meta.key) {
isallowed = true isallowed = true
break break
} }

30
src/core/options.go Normal file
View File

@ -0,0 +1,30 @@
package core
import (
"crypto/ed25519"
)
type SetupOption interface {
isSetupOption()
}
type ListenAddress string
type AdminListenAddress string
type Peer struct {
URI string
SourceInterface string
}
type NodeInfo map[string]interface{}
type NodeInfoPrivacy bool
type IfName string
type IfMTU uint16
type AllowedPublicKey ed25519.PublicKey
func (a ListenAddress) isSetupOption() {}
func (a AdminListenAddress) isSetupOption() {}
func (a Peer) isSetupOption() {}
func (a NodeInfo) isSetupOption() {}
func (a NodeInfoPrivacy) isSetupOption() {}
func (a IfName) isSetupOption() {}
func (a IfMTU) isSetupOption() {}
func (a AllowedPublicKey) isSetupOption() {}

View File

@ -96,7 +96,7 @@ func (t *tcp) getAddr() *net.TCPAddr {
} }
// Initializes the struct. // Initializes the struct.
func (t *tcp) init(l *links) error { func (t *tcp) init(l *links, listeners []ListenAddress) error {
t.links = l t.links = l
t.tls.init(t) t.tls.init(t)
t.mutex.Lock() t.mutex.Lock()
@ -105,10 +105,8 @@ func (t *tcp) init(l *links) error {
t.listeners = make(map[string]*TcpListener) t.listeners = make(map[string]*TcpListener)
t.mutex.Unlock() t.mutex.Unlock()
t.links.core.config.RLock() for _, listenaddr := range listeners {
defer t.links.core.config.RUnlock() u, err := url.Parse(string(listenaddr))
for _, listenaddr := range t.links.core.config.Listen {
u, err := url.Parse(listenaddr)
if err != nil { if err != nil {
t.links.core.log.Errorln("Failed to parse listener: listener", listenaddr, "is not correctly formatted, ignoring") t.links.core.log.Errorln("Failed to parse listener: listener", listenaddr, "is not correctly formatted, ignoring")
} }