From 219fb96553a0f647036657f99fc7ed6cfbe00d18 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 29 Dec 2018 18:51:51 +0000 Subject: [PATCH 01/40] Support notifying components for config reload, listen for SIGHUP --- cmd/yggdrasil/main.go | 12 ++++++++- src/yggdrasil/admin.go | 24 ++++++++++++++--- src/yggdrasil/core.go | 53 ++++++++++++++++++++++++++++++++------ src/yggdrasil/dht.go | 23 +++++++++++++---- src/yggdrasil/multicast.go | 19 +++++++++++--- src/yggdrasil/peer.go | 13 ++++++++++ src/yggdrasil/router.go | 33 ++++++++++++++---------- src/yggdrasil/search.go | 17 ++++++++++-- src/yggdrasil/session.go | 24 +++++++++++++++++ src/yggdrasil/switch.go | 7 +++++ 10 files changed, 189 insertions(+), 36 deletions(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 2b6d2f04..e98e6238 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -314,7 +314,9 @@ func main() { logger.Printf("Your IPv6 subnet is %s", subnet.String()) // Catch interrupts from the operating system to exit gracefully. c := make(chan os.Signal, 1) + r := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) + signal.Notify(r, os.Interrupt, syscall.SIGHUP) // Create a function to capture the service being stopped on Windows. winTerminate := func() { c <- os.Interrupt @@ -322,5 +324,13 @@ func main() { minwinsvc.SetOnExit(winTerminate) // Wait for the terminate/interrupt signal. Once a signal is received, the // deferred Stop function above will run which will shut down TUN/TAP. - <-c + for { + select { + case _ = <-r: + n.core.UpdateConfig(cfg) + case _ = <-c: + goto exit + } + } +exit: } diff --git a/src/yggdrasil/admin.go b/src/yggdrasil/admin.go index bd3c9051..1c8c80e4 100644 --- a/src/yggdrasil/admin.go +++ b/src/yggdrasil/admin.go @@ -22,10 +22,11 @@ import ( // TODO: Add authentication type admin struct { - core *Core - listenaddr string - listener net.Listener - handlers []admin_handlerInfo + core *Core + reconfigure chan bool + listenaddr string + listener net.Listener + handlers []admin_handlerInfo } type admin_info map[string]interface{} @@ -53,6 +54,21 @@ func (a *admin) addHandler(name string, args []string, handler func(admin_info) // init runs the initial admin setup. func (a *admin) init(c *Core, listenaddr string) { a.core = c + a.reconfigure = make(chan bool, 1) + go func() { + for { + select { + case _ = <-a.reconfigure: + a.core.configMutex.RLock() + a.core.log.Println("Notified: admin") + if a.core.config.AdminListen != a.core.configOld.AdminListen { + a.core.log.Println("AdminListen has changed!") + } + a.core.configMutex.RUnlock() + continue + } + } + }() a.listenaddr = listenaddr a.addHandler("list", []string{}, func(in admin_info) (admin_info, error) { handlers := make(map[string]interface{}) diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index e38274fa..9e4bb628 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -7,6 +7,7 @@ import ( "log" "net" "regexp" + "sync" "github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/config" @@ -17,14 +18,26 @@ import ( var buildName string var buildVersion string +type module interface { + init(*config.NodeConfig) error + start() error +} + // The Core object represents the Yggdrasil node. You should create a Core // object for each Yggdrasil node you plan to run. type Core struct { // This is the main data structure that holds everything else for a node - boxPub crypto.BoxPubKey - boxPriv crypto.BoxPrivKey - sigPub crypto.SigPubKey - sigPriv crypto.SigPrivKey + // We're going to keep our own copy of the provided config - that way we can + // guarantee that it will be covered by the mutex + config config.NodeConfig // Active config + configOld config.NodeConfig // Previous config + configMutex sync.RWMutex // Protects both config and configOld + // Core-specific config + boxPub crypto.BoxPubKey + boxPriv crypto.BoxPrivKey + sigPub crypto.SigPubKey + sigPriv crypto.SigPrivKey + // Modules switchTable switchTable peers peers sessions sessions @@ -35,8 +48,9 @@ type Core struct { multicast multicast nodeinfo nodeinfo tcp tcpInterface - log *log.Logger - ifceExpr []*regexp.Regexp // the zone of link-local IPv6 peers must match this + // Other bits + log *log.Logger + ifceExpr []*regexp.Regexp // the zone of link-local IPv6 peers must match this } func (c *Core) init(bpub *crypto.BoxPubKey, @@ -62,8 +76,26 @@ func (c *Core) init(bpub *crypto.BoxPubKey, c.switchTable.init(c, c.sigPub) // TODO move before peers? before router? } -// Get the current build name. This is usually injected if built from git, -// or returns "unknown" otherwise. +// UpdateConfig updates the configuration in Core and then signals the +// various module goroutines to reconfigure themselves if needed +func (c *Core) UpdateConfig(config *config.NodeConfig) { + c.configMutex.Lock() + c.configOld = c.config + c.config = *config + c.configMutex.Unlock() + + c.admin.reconfigure <- true + c.searches.reconfigure <- true + c.dht.reconfigure <- true + c.sessions.reconfigure <- true + c.multicast.reconfigure <- true + c.peers.reconfigure <- true + c.router.reconfigure <- true + c.switchTable.reconfigure <- true +} + +// GetBuildName gets the current build name. This is usually injected if built +// from git, or returns "unknown" otherwise. func GetBuildName() string { if buildName == "" { return "unknown" @@ -96,6 +128,11 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error { c.log.Println("Starting up...") + c.configMutex.Lock() + c.config = *nc + c.configOld = c.config + c.configMutex.Unlock() + var boxPub crypto.BoxPubKey var boxPriv crypto.BoxPrivKey var sigPub crypto.SigPubKey diff --git a/src/yggdrasil/dht.go b/src/yggdrasil/dht.go index b52a820b..3f2debdb 100644 --- a/src/yggdrasil/dht.go +++ b/src/yggdrasil/dht.go @@ -65,11 +65,12 @@ type dhtReqKey struct { // The main DHT struct. type dht struct { - core *Core - nodeID crypto.NodeID - peers chan *dhtInfo // other goroutines put incoming dht updates here - reqs map[dhtReqKey]time.Time // Keeps track of recent outstanding requests - callbacks map[dhtReqKey]dht_callbackInfo // Search and admin lookup callbacks + core *Core + reconfigure chan bool + nodeID crypto.NodeID + peers chan *dhtInfo // other goroutines put incoming dht updates here + reqs map[dhtReqKey]time.Time // Keeps track of recent outstanding requests + callbacks map[dhtReqKey]dht_callbackInfo // Search and admin lookup callbacks // These next two could be replaced by a single linked list or similar... table map[crypto.NodeID]*dhtInfo imp []*dhtInfo @@ -78,6 +79,18 @@ type dht struct { // Initializes the DHT. func (t *dht) init(c *Core) { t.core = c + t.reconfigure = make(chan bool, 1) + go func() { + for { + select { + case _ = <-t.reconfigure: + t.core.configMutex.RLock() + t.core.log.Println("Notified: dht") + t.core.configMutex.RUnlock() + continue + } + } + }() t.nodeID = *t.core.GetNodeID() t.peers = make(chan *dhtInfo, 1024) t.callbacks = make(map[dhtReqKey]dht_callbackInfo) diff --git a/src/yggdrasil/multicast.go b/src/yggdrasil/multicast.go index 749dfcdb..3d73237f 100644 --- a/src/yggdrasil/multicast.go +++ b/src/yggdrasil/multicast.go @@ -10,13 +10,26 @@ import ( ) type multicast struct { - core *Core - sock *ipv6.PacketConn - groupAddr string + core *Core + reconfigure chan bool + sock *ipv6.PacketConn + groupAddr string } func (m *multicast) init(core *Core) { m.core = core + m.reconfigure = make(chan bool, 1) + go func() { + for { + select { + case _ = <-m.reconfigure: + m.core.configMutex.RLock() + m.core.log.Println("Notified: multicast") + m.core.configMutex.RUnlock() + continue + } + } + }() m.groupAddr = "[ff02::114]:9001" // Check if we've been given any expressions if len(m.core.ifceExpr) == 0 { diff --git a/src/yggdrasil/peer.go b/src/yggdrasil/peer.go index a2b94b67..502ea67e 100644 --- a/src/yggdrasil/peer.go +++ b/src/yggdrasil/peer.go @@ -19,6 +19,7 @@ import ( // In other cases, it's link protocol traffic used to build the spanning tree, in which case this checks signatures and passes the message along to the switch. type peers struct { core *Core + reconfigure chan bool mutex sync.Mutex // Synchronize writes to atomic ports atomic.Value //map[switchPort]*peer, use CoW semantics authMutex sync.RWMutex @@ -31,6 +32,18 @@ func (ps *peers) init(c *Core) { defer ps.mutex.Unlock() ps.putPorts(make(map[switchPort]*peer)) ps.core = c + ps.reconfigure = make(chan bool, 1) + go func() { + for { + select { + case _ = <-ps.reconfigure: + ps.core.configMutex.RLock() + ps.core.log.Println("Notified: peers") + ps.core.configMutex.RUnlock() + continue + } + } + }() ps.allowedEncryptionPublicKeys = make(map[crypto.BoxPubKey]struct{}) } diff --git a/src/yggdrasil/router.go b/src/yggdrasil/router.go index 87da8829..096a9785 100644 --- a/src/yggdrasil/router.go +++ b/src/yggdrasil/router.go @@ -37,19 +37,20 @@ import ( // The router struct has channels to/from the tun/tap device and a self peer (0), which is how messages are passed between this node and the peers/switch layer. // The router's mainLoop goroutine is responsible for managing all information related to the dht, searches, and crypto sessions. type router struct { - core *Core - addr address.Address - subnet address.Subnet - in <-chan []byte // packets we received from the network, link to peer's "out" - out func([]byte) // packets we're sending to the network, link to peer's "in" - toRecv chan router_recvPacket // packets to handle via recvPacket() - tun tunAdapter // TUN/TAP adapter - adapters []Adapter // Other adapters - recv chan<- []byte // place where the tun pulls received packets from - send <-chan []byte // place where the tun puts outgoing packets - reset chan struct{} // signal that coords changed (re-init sessions/dht) - admin chan func() // pass a lambda for the admin socket to query stuff - cryptokey cryptokey + core *Core + reconfigure chan bool + addr address.Address + subnet address.Subnet + in <-chan []byte // packets we received from the network, link to peer's "out" + out func([]byte) // packets we're sending to the network, link to peer's "in" + toRecv chan router_recvPacket // packets to handle via recvPacket() + tun tunAdapter // TUN/TAP adapter + adapters []Adapter // Other adapters + recv chan<- []byte // place where the tun pulls received packets from + send <-chan []byte // place where the tun puts outgoing packets + reset chan struct{} // signal that coords changed (re-init sessions/dht) + admin chan func() // pass a lambda for the admin socket to query stuff + cryptokey cryptokey } // Packet and session info, used to check that the packet matches a valid IP range or CKR prefix before sending to the tun. @@ -61,6 +62,7 @@ type router_recvPacket struct { // Initializes the router struct, which includes setting up channels to/from the tun/tap. func (r *router) init(core *Core) { r.core = core + r.reconfigure = make(chan bool, 1) r.addr = *address.AddrForNodeID(&r.core.dht.nodeID) r.subnet = *address.SubnetForNodeID(&r.core.dht.nodeID) in := make(chan []byte, 32) // TODO something better than this... @@ -124,6 +126,11 @@ func (r *router) mainLoop() { } case f := <-r.admin: f() + case _ = <-r.reconfigure: + r.core.configMutex.RLock() + r.core.log.Println("Notified: router") + r.core.configMutex.RUnlock() + continue } } } diff --git a/src/yggdrasil/search.go b/src/yggdrasil/search.go index c85b719c..f522d7b9 100644 --- a/src/yggdrasil/search.go +++ b/src/yggdrasil/search.go @@ -42,13 +42,26 @@ type searchInfo struct { // This stores a map of active searches. type searches struct { - core *Core - searches map[crypto.NodeID]*searchInfo + core *Core + reconfigure chan bool + searches map[crypto.NodeID]*searchInfo } // Intializes the searches struct. func (s *searches) init(core *Core) { s.core = core + s.reconfigure = make(chan bool, 1) + go func() { + for { + select { + case _ = <-s.reconfigure: + s.core.configMutex.RLock() + s.core.log.Println("Notified: searches") + s.core.configMutex.RUnlock() + continue + } + } + }() s.searches = make(map[crypto.NodeID]*searchInfo) } diff --git a/src/yggdrasil/session.go b/src/yggdrasil/session.go index 4f395b07..78b36ecd 100644 --- a/src/yggdrasil/session.go +++ b/src/yggdrasil/session.go @@ -18,6 +18,7 @@ import ( // This includes coords, permanent and ephemeral keys, handles and nonces, various sorts of timing information for timeout and maintenance, and some metadata for the admin API. type sessionInfo struct { core *Core + reconfigure chan bool theirAddr address.Address theirSubnet address.Subnet theirPermPub crypto.BoxPubKey @@ -101,6 +102,7 @@ func (s *sessionInfo) timedout() bool { // Additionally, stores maps of address/subnet onto keys, and keys onto handles. type sessions struct { core *Core + reconfigure chan bool lastCleanup time.Time // Maps known permanent keys to their shared key, used by DHT a lot permShared map[crypto.BoxPubKey]*crypto.BoxSharedKey @@ -124,6 +126,22 @@ type sessions struct { // Initializes the session struct. func (ss *sessions) init(core *Core) { ss.core = core + ss.reconfigure = make(chan bool, 1) + go func() { + for { + select { + case newConfig := <-ss.reconfigure: + ss.core.configMutex.RLock() + ss.core.log.Println("Notified: sessions") + ss.core.configMutex.RUnlock() + + for _, sinfo := range ss.sinfos { + sinfo.reconfigure <- newConfig + } + continue + } + } + }() ss.permShared = make(map[crypto.BoxPubKey]*crypto.BoxSharedKey) ss.sinfos = make(map[crypto.Handle]*sessionInfo) ss.byMySes = make(map[crypto.BoxPubKey]*crypto.Handle) @@ -271,6 +289,7 @@ func (ss *sessions) createSession(theirPermKey *crypto.BoxPubKey) *sessionInfo { } sinfo := sessionInfo{} sinfo.core = ss.core + sinfo.reconfigure = make(chan bool, 1) sinfo.theirPermPub = *theirPermKey pub, priv := crypto.NewBoxKeys() sinfo.mySesPub = *pub @@ -539,6 +558,11 @@ func (sinfo *sessionInfo) doWorker() { } else { return } + case _ = <-sinfo.reconfigure: + sinfo.core.configMutex.RLock() + sinfo.core.log.Println("Notified: sessionInfo") + sinfo.core.configMutex.RUnlock() + continue } } } diff --git a/src/yggdrasil/switch.go b/src/yggdrasil/switch.go index 3c1dae61..420392b6 100644 --- a/src/yggdrasil/switch.go +++ b/src/yggdrasil/switch.go @@ -162,6 +162,7 @@ type switchData struct { // All the information stored by the switch. type switchTable struct { core *Core + reconfigure chan bool key crypto.SigPubKey // Our own key time time.Time // Time when locator.tstamp was last updated drop map[crypto.SigPubKey]int64 // Tstamp associated with a dropped root @@ -184,6 +185,7 @@ const SwitchQueueTotalMinSize = 4 * 1024 * 1024 func (t *switchTable) init(core *Core, key crypto.SigPubKey) { now := time.Now() t.core = core + t.reconfigure = make(chan bool, 1) t.key = key locator := switchLocator{root: key, tstamp: now.Unix()} peers := make(map[switchPort]peerInfo) @@ -808,6 +810,11 @@ func (t *switchTable) doWorker() { } case f := <-t.admin: f() + case _ = <-t.reconfigure: + t.core.configMutex.RLock() + t.core.log.Println("Notified: switchTable") + t.core.configMutex.RUnlock() + continue } } } From fa7c4117b4cce9932f20b7d28399315854296dbc Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 29 Dec 2018 19:14:26 +0000 Subject: [PATCH 02/40] Use Core.config in init functions --- src/yggdrasil/admin.go | 6 ++- src/yggdrasil/core.go | 97 +++++++++++++++++++---------------------- src/yggdrasil/switch.go | 6 +-- src/yggdrasil/tcp.go | 9 ++-- 4 files changed, 56 insertions(+), 62 deletions(-) diff --git a/src/yggdrasil/admin.go b/src/yggdrasil/admin.go index 1c8c80e4..723bf8ff 100644 --- a/src/yggdrasil/admin.go +++ b/src/yggdrasil/admin.go @@ -52,7 +52,7 @@ func (a *admin) addHandler(name string, args []string, handler func(admin_info) } // init runs the initial admin setup. -func (a *admin) init(c *Core, listenaddr string) { +func (a *admin) init(c *Core) { a.core = c a.reconfigure = make(chan bool, 1) go func() { @@ -69,7 +69,9 @@ func (a *admin) init(c *Core, listenaddr string) { } } }() - a.listenaddr = listenaddr + a.core.configMutex.RLock() + a.listenaddr = a.core.config.AdminListen + a.core.configMutex.RUnlock() a.addHandler("list", []string{}, func(in admin_info) (admin_info, error) { handlers := make(map[string]interface{}) for _, handler := range a.handlers { diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index 9e4bb628..58d92b0d 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -19,7 +19,7 @@ var buildName string var buildVersion string type module interface { - init(*config.NodeConfig) error + init(*Core, *config.NodeConfig) error start() error } @@ -32,12 +32,10 @@ type Core struct { config config.NodeConfig // Active config configOld config.NodeConfig // Previous config configMutex sync.RWMutex // Protects both config and configOld - // Core-specific config - boxPub crypto.BoxPubKey - boxPriv crypto.BoxPrivKey - sigPub crypto.SigPubKey - sigPriv crypto.SigPrivKey - // Modules + boxPub crypto.BoxPubKey + boxPriv crypto.BoxPrivKey + sigPub crypto.SigPubKey + sigPriv crypto.SigPrivKey switchTable switchTable peers peers sessions sessions @@ -48,15 +46,11 @@ type Core struct { multicast multicast nodeinfo nodeinfo tcp tcpInterface - // Other bits - log *log.Logger - ifceExpr []*regexp.Regexp // the zone of link-local IPv6 peers must match this + log *log.Logger + ifceExpr []*regexp.Regexp // the zone of link-local IPv6 peers must match this } -func (c *Core) init(bpub *crypto.BoxPubKey, - bpriv *crypto.BoxPrivKey, - spub *crypto.SigPubKey, - spriv *crypto.SigPrivKey) { +func (c *Core) init() error { // TODO separate init and start functions // Init sets up structs // Start launches goroutines that depend on structs being set up @@ -64,16 +58,45 @@ func (c *Core) init(bpub *crypto.BoxPubKey, if c.log == nil { c.log = log.New(ioutil.Discard, "", 0) } - c.boxPub, c.boxPriv = *bpub, *bpriv - c.sigPub, c.sigPriv = *spub, *spriv - c.admin.core = c + + boxPubHex, err := hex.DecodeString(c.config.EncryptionPublicKey) + if err != nil { + return err + } + boxPrivHex, err := hex.DecodeString(c.config.EncryptionPrivateKey) + if err != nil { + return err + } + sigPubHex, err := hex.DecodeString(c.config.SigningPublicKey) + if err != nil { + return err + } + sigPrivHex, err := hex.DecodeString(c.config.SigningPrivateKey) + if err != nil { + return err + } + + copy(c.boxPub[:], boxPubHex) + copy(c.boxPriv[:], boxPrivHex) + copy(c.sigPub[:], sigPubHex) + copy(c.sigPriv[:], sigPrivHex) + + c.admin.init(c) + c.nodeinfo.init(c) c.searches.init(c) c.dht.init(c) c.sessions.init(c) c.multicast.init(c) c.peers.init(c) c.router.init(c) - c.switchTable.init(c, c.sigPub) // TODO move before peers? before router? + c.switchTable.init(c) // TODO move before peers? before router? + + if err := c.tcp.init(c); err != nil { + c.log.Println("Failed to start TCP interface") + return err + } + + return nil } // UpdateConfig updates the configuration in Core and then signals the @@ -133,42 +156,10 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error { c.configOld = c.config c.configMutex.Unlock() - var boxPub crypto.BoxPubKey - var boxPriv crypto.BoxPrivKey - var sigPub crypto.SigPubKey - var sigPriv crypto.SigPrivKey - boxPubHex, err := hex.DecodeString(nc.EncryptionPublicKey) - if err != nil { - return err - } - boxPrivHex, err := hex.DecodeString(nc.EncryptionPrivateKey) - if err != nil { - return err - } - sigPubHex, err := hex.DecodeString(nc.SigningPublicKey) - if err != nil { - return err - } - sigPrivHex, err := hex.DecodeString(nc.SigningPrivateKey) - if err != nil { - return err - } - copy(boxPub[:], boxPubHex) - copy(boxPriv[:], boxPrivHex) - copy(sigPub[:], sigPubHex) - copy(sigPriv[:], sigPrivHex) + c.init() - c.init(&boxPub, &boxPriv, &sigPub, &sigPriv) - c.admin.init(c, nc.AdminListen) - - c.nodeinfo.init(c) c.nodeinfo.setNodeInfo(nc.NodeInfo, nc.NodeInfoPrivacy) - if err := c.tcp.init(c, nc.Listen, nc.ReadTimeout); err != nil { - c.log.Println("Failed to start TCP interface") - return err - } - if nc.SwitchOptions.MaxTotalQueueSize >= SwitchQueueTotalMinSize { c.switchTable.queueTotalMaxSize = nc.SwitchOptions.MaxTotalQueueSize } @@ -201,7 +192,7 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error { } } for _, source := range nc.TunnelRouting.IPv6Sources { - if c.router.cryptokey.addSourceSubnet(source); err != nil { + if err := c.router.cryptokey.addSourceSubnet(source); err != nil { panic(err) } } @@ -211,7 +202,7 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error { } } for _, source := range nc.TunnelRouting.IPv4Sources { - if c.router.cryptokey.addSourceSubnet(source); err != nil { + if err := c.router.cryptokey.addSourceSubnet(source); err != nil { panic(err) } } diff --git a/src/yggdrasil/switch.go b/src/yggdrasil/switch.go index 420392b6..f3c95122 100644 --- a/src/yggdrasil/switch.go +++ b/src/yggdrasil/switch.go @@ -182,12 +182,12 @@ type switchTable struct { const SwitchQueueTotalMinSize = 4 * 1024 * 1024 // Initializes the switchTable struct. -func (t *switchTable) init(core *Core, key crypto.SigPubKey) { +func (t *switchTable) init(core *Core) { now := time.Now() t.core = core t.reconfigure = make(chan bool, 1) - t.key = key - locator := switchLocator{root: key, tstamp: now.Unix()} + t.key = t.core.sigPub + locator := switchLocator{root: t.key, tstamp: now.Unix()} peers := make(map[switchPort]peerInfo) t.data = switchData{locator: locator, peers: peers} t.updater.Store(&sync.Once{}) diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index 6d923440..c986dc62 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -40,6 +40,7 @@ type tcpInterface struct { core *Core serv net.Listener tcp_timeout time.Duration + tcp_addr string mutex sync.Mutex // Protecting the below calls map[string]struct{} conns map[tcpInfo](chan struct{}) @@ -80,15 +81,15 @@ func (iface *tcpInterface) connectSOCKS(socksaddr, peeraddr string) { } // Initializes the struct. -func (iface *tcpInterface) init(core *Core, addr string, readTimeout int32) (err error) { +func (iface *tcpInterface) init(core *Core) (err error) { iface.core = core - - iface.tcp_timeout = time.Duration(readTimeout) * time.Millisecond + iface.tcp_addr = iface.core.config.Listen + iface.tcp_timeout = time.Duration(iface.core.config.ReadTimeout) * time.Millisecond if iface.tcp_timeout >= 0 && iface.tcp_timeout < default_tcp_timeout { iface.tcp_timeout = default_tcp_timeout } - iface.serv, err = net.Listen("tcp", addr) + iface.serv, err = net.Listen("tcp", iface.tcp_addr) if err == nil { iface.calls = make(map[string]struct{}) iface.conns = make(map[tcpInfo](chan struct{})) From 2925920c703a3a743346d241097513952c80d979 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 29 Dec 2018 19:53:31 +0000 Subject: [PATCH 03/40] Use mutex in switch/tcp init --- src/yggdrasil/switch.go | 2 ++ src/yggdrasil/tcp.go | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/yggdrasil/switch.go b/src/yggdrasil/switch.go index f3c95122..10c9563d 100644 --- a/src/yggdrasil/switch.go +++ b/src/yggdrasil/switch.go @@ -186,7 +186,9 @@ func (t *switchTable) init(core *Core) { now := time.Now() t.core = core t.reconfigure = make(chan bool, 1) + t.core.configMutex.RLock() t.key = t.core.sigPub + t.core.configMutex.RUnlock() locator := switchLocator{root: t.key, tstamp: now.Unix()} peers := make(map[switchPort]peerInfo) t.data = switchData{locator: locator, peers: peers} diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index c986dc62..ad50d780 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -83,8 +83,10 @@ func (iface *tcpInterface) connectSOCKS(socksaddr, peeraddr string) { // Initializes the struct. func (iface *tcpInterface) init(core *Core) (err error) { iface.core = core + iface.core.configMutex.RLock() iface.tcp_addr = iface.core.config.Listen iface.tcp_timeout = time.Duration(iface.core.config.ReadTimeout) * time.Millisecond + iface.core.configMutex.RUnlock() if iface.tcp_timeout >= 0 && iface.tcp_timeout < default_tcp_timeout { iface.tcp_timeout = default_tcp_timeout } From 7fae1c993ab5d754625336b16e13e47a3d71a807 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 30 Dec 2018 12:04:42 +0000 Subject: [PATCH 04/40] Handle errors from reconfigure tasks --- src/yggdrasil/admin.go | 9 ++++----- src/yggdrasil/core.go | 26 ++++++++++++++++++-------- src/yggdrasil/dht.go | 11 ++++------- src/yggdrasil/multicast.go | 11 ++++------- src/yggdrasil/peer.go | 11 ++++------- src/yggdrasil/router.go | 11 ++++------- src/yggdrasil/search.go | 11 ++++------- src/yggdrasil/session.go | 35 ++++++++++++++++++----------------- src/yggdrasil/switch.go | 11 ++++------- 9 files changed, 64 insertions(+), 72 deletions(-) diff --git a/src/yggdrasil/admin.go b/src/yggdrasil/admin.go index 723bf8ff..90fb1127 100644 --- a/src/yggdrasil/admin.go +++ b/src/yggdrasil/admin.go @@ -23,7 +23,7 @@ import ( type admin struct { core *Core - reconfigure chan bool + reconfigure chan chan error listenaddr string listener net.Listener handlers []admin_handlerInfo @@ -54,18 +54,17 @@ func (a *admin) addHandler(name string, args []string, handler func(admin_info) // init runs the initial admin setup. func (a *admin) init(c *Core) { a.core = c - a.reconfigure = make(chan bool, 1) + a.reconfigure = make(chan chan error, 1) go func() { for { select { - case _ = <-a.reconfigure: + case e := <-a.reconfigure: a.core.configMutex.RLock() - a.core.log.Println("Notified: admin") if a.core.config.AdminListen != a.core.configOld.AdminListen { a.core.log.Println("AdminListen has changed!") } a.core.configMutex.RUnlock() - continue + e <- nil } } }() diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index 58d92b0d..435cd670 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -107,14 +107,24 @@ func (c *Core) UpdateConfig(config *config.NodeConfig) { c.config = *config c.configMutex.Unlock() - c.admin.reconfigure <- true - c.searches.reconfigure <- true - c.dht.reconfigure <- true - c.sessions.reconfigure <- true - c.multicast.reconfigure <- true - c.peers.reconfigure <- true - c.router.reconfigure <- true - c.switchTable.reconfigure <- true + components := []chan chan error{ + c.admin.reconfigure, + c.searches.reconfigure, + c.dht.reconfigure, + c.sessions.reconfigure, + c.multicast.reconfigure, + c.peers.reconfigure, + c.router.reconfigure, + c.switchTable.reconfigure, + } + + for _, component := range components { + response := make(chan error) + component <- response + if err := <-response; err != nil { + c.log.Println(err) + } + } } // GetBuildName gets the current build name. This is usually injected if built diff --git a/src/yggdrasil/dht.go b/src/yggdrasil/dht.go index 3f2debdb..bba6dfc7 100644 --- a/src/yggdrasil/dht.go +++ b/src/yggdrasil/dht.go @@ -66,7 +66,7 @@ type dhtReqKey struct { // The main DHT struct. type dht struct { core *Core - reconfigure chan bool + reconfigure chan chan error nodeID crypto.NodeID peers chan *dhtInfo // other goroutines put incoming dht updates here reqs map[dhtReqKey]time.Time // Keeps track of recent outstanding requests @@ -79,15 +79,12 @@ type dht struct { // Initializes the DHT. func (t *dht) init(c *Core) { t.core = c - t.reconfigure = make(chan bool, 1) + t.reconfigure = make(chan chan error, 1) go func() { for { select { - case _ = <-t.reconfigure: - t.core.configMutex.RLock() - t.core.log.Println("Notified: dht") - t.core.configMutex.RUnlock() - continue + case e := <-t.reconfigure: + e <- nil } } }() diff --git a/src/yggdrasil/multicast.go b/src/yggdrasil/multicast.go index 3d73237f..25c979c3 100644 --- a/src/yggdrasil/multicast.go +++ b/src/yggdrasil/multicast.go @@ -11,22 +11,19 @@ import ( type multicast struct { core *Core - reconfigure chan bool + reconfigure chan chan error sock *ipv6.PacketConn groupAddr string } func (m *multicast) init(core *Core) { m.core = core - m.reconfigure = make(chan bool, 1) + m.reconfigure = make(chan chan error, 1) go func() { for { select { - case _ = <-m.reconfigure: - m.core.configMutex.RLock() - m.core.log.Println("Notified: multicast") - m.core.configMutex.RUnlock() - continue + case e := <-m.reconfigure: + e <- nil } } }() diff --git a/src/yggdrasil/peer.go b/src/yggdrasil/peer.go index 502ea67e..15174b77 100644 --- a/src/yggdrasil/peer.go +++ b/src/yggdrasil/peer.go @@ -19,7 +19,7 @@ import ( // In other cases, it's link protocol traffic used to build the spanning tree, in which case this checks signatures and passes the message along to the switch. type peers struct { core *Core - reconfigure chan bool + reconfigure chan chan error mutex sync.Mutex // Synchronize writes to atomic ports atomic.Value //map[switchPort]*peer, use CoW semantics authMutex sync.RWMutex @@ -32,15 +32,12 @@ func (ps *peers) init(c *Core) { defer ps.mutex.Unlock() ps.putPorts(make(map[switchPort]*peer)) ps.core = c - ps.reconfigure = make(chan bool, 1) + ps.reconfigure = make(chan chan error, 1) go func() { for { select { - case _ = <-ps.reconfigure: - ps.core.configMutex.RLock() - ps.core.log.Println("Notified: peers") - ps.core.configMutex.RUnlock() - continue + case e := <-ps.reconfigure: + e <- nil } } }() diff --git a/src/yggdrasil/router.go b/src/yggdrasil/router.go index 096a9785..68fb025a 100644 --- a/src/yggdrasil/router.go +++ b/src/yggdrasil/router.go @@ -38,7 +38,7 @@ import ( // The router's mainLoop goroutine is responsible for managing all information related to the dht, searches, and crypto sessions. type router struct { core *Core - reconfigure chan bool + reconfigure chan chan error addr address.Address subnet address.Subnet in <-chan []byte // packets we received from the network, link to peer's "out" @@ -62,7 +62,7 @@ type router_recvPacket struct { // Initializes the router struct, which includes setting up channels to/from the tun/tap. func (r *router) init(core *Core) { r.core = core - r.reconfigure = make(chan bool, 1) + r.reconfigure = make(chan chan error, 1) r.addr = *address.AddrForNodeID(&r.core.dht.nodeID) r.subnet = *address.SubnetForNodeID(&r.core.dht.nodeID) in := make(chan []byte, 32) // TODO something better than this... @@ -126,11 +126,8 @@ func (r *router) mainLoop() { } case f := <-r.admin: f() - case _ = <-r.reconfigure: - r.core.configMutex.RLock() - r.core.log.Println("Notified: router") - r.core.configMutex.RUnlock() - continue + case e := <-r.reconfigure: + e <- nil } } } diff --git a/src/yggdrasil/search.go b/src/yggdrasil/search.go index f522d7b9..f0af61f4 100644 --- a/src/yggdrasil/search.go +++ b/src/yggdrasil/search.go @@ -43,22 +43,19 @@ type searchInfo struct { // This stores a map of active searches. type searches struct { core *Core - reconfigure chan bool + reconfigure chan chan error searches map[crypto.NodeID]*searchInfo } // Intializes the searches struct. func (s *searches) init(core *Core) { s.core = core - s.reconfigure = make(chan bool, 1) + s.reconfigure = make(chan chan error, 1) go func() { for { select { - case _ = <-s.reconfigure: - s.core.configMutex.RLock() - s.core.log.Println("Notified: searches") - s.core.configMutex.RUnlock() - continue + case e := <-s.reconfigure: + e <- nil } } }() diff --git a/src/yggdrasil/session.go b/src/yggdrasil/session.go index 78b36ecd..3c8e0132 100644 --- a/src/yggdrasil/session.go +++ b/src/yggdrasil/session.go @@ -18,7 +18,7 @@ import ( // This includes coords, permanent and ephemeral keys, handles and nonces, various sorts of timing information for timeout and maintenance, and some metadata for the admin API. type sessionInfo struct { core *Core - reconfigure chan bool + reconfigure chan chan error theirAddr address.Address theirSubnet address.Subnet theirPermPub crypto.BoxPubKey @@ -102,7 +102,7 @@ func (s *sessionInfo) timedout() bool { // Additionally, stores maps of address/subnet onto keys, and keys onto handles. type sessions struct { core *Core - reconfigure chan bool + reconfigure chan chan error lastCleanup time.Time // Maps known permanent keys to their shared key, used by DHT a lot permShared map[crypto.BoxPubKey]*crypto.BoxSharedKey @@ -126,19 +126,23 @@ type sessions struct { // Initializes the session struct. func (ss *sessions) init(core *Core) { ss.core = core - ss.reconfigure = make(chan bool, 1) + ss.reconfigure = make(chan chan error, 1) go func() { for { select { - case newConfig := <-ss.reconfigure: - ss.core.configMutex.RLock() - ss.core.log.Println("Notified: sessions") - ss.core.configMutex.RUnlock() - - for _, sinfo := range ss.sinfos { - sinfo.reconfigure <- newConfig + case e := <-ss.reconfigure: + responses := make(map[crypto.Handle]chan error) + for index, session := range ss.sinfos { + responses[index] = make(chan error) + session.reconfigure <- responses[index] } - continue + for _, response := range responses { + if err := <-response; err != nil { + e <- err + continue + } + } + e <- nil } } }() @@ -289,7 +293,7 @@ func (ss *sessions) createSession(theirPermKey *crypto.BoxPubKey) *sessionInfo { } sinfo := sessionInfo{} sinfo.core = ss.core - sinfo.reconfigure = make(chan bool, 1) + sinfo.reconfigure = make(chan chan error, 1) sinfo.theirPermPub = *theirPermKey pub, priv := crypto.NewBoxKeys() sinfo.mySesPub = *pub @@ -558,11 +562,8 @@ func (sinfo *sessionInfo) doWorker() { } else { return } - case _ = <-sinfo.reconfigure: - sinfo.core.configMutex.RLock() - sinfo.core.log.Println("Notified: sessionInfo") - sinfo.core.configMutex.RUnlock() - continue + case e := <-sinfo.reconfigure: + e <- nil } } } diff --git a/src/yggdrasil/switch.go b/src/yggdrasil/switch.go index 10c9563d..741de983 100644 --- a/src/yggdrasil/switch.go +++ b/src/yggdrasil/switch.go @@ -162,7 +162,7 @@ type switchData struct { // All the information stored by the switch. type switchTable struct { core *Core - reconfigure chan bool + reconfigure chan chan error key crypto.SigPubKey // Our own key time time.Time // Time when locator.tstamp was last updated drop map[crypto.SigPubKey]int64 // Tstamp associated with a dropped root @@ -185,7 +185,7 @@ const SwitchQueueTotalMinSize = 4 * 1024 * 1024 func (t *switchTable) init(core *Core) { now := time.Now() t.core = core - t.reconfigure = make(chan bool, 1) + t.reconfigure = make(chan chan error, 1) t.core.configMutex.RLock() t.key = t.core.sigPub t.core.configMutex.RUnlock() @@ -812,11 +812,8 @@ func (t *switchTable) doWorker() { } case f := <-t.admin: f() - case _ = <-t.reconfigure: - t.core.configMutex.RLock() - t.core.log.Println("Notified: switchTable") - t.core.configMutex.RUnlock() - continue + case e := <-t.reconfigure: + e <- nil } } } From f96747181d13480046874900898b7ede6e6c8069 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 30 Dec 2018 12:26:55 +0000 Subject: [PATCH 05/40] Allow updating AdminListen during runtime --- cmd/yggdrasil/main.go | 231 ++++++++++++++++++++++------------------- src/yggdrasil/admin.go | 4 +- 2 files changed, 125 insertions(+), 110 deletions(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index e98e6238..5ee7add6 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -76,6 +76,119 @@ func generateConfig(isAutoconf bool) *nodeConfig { return &cfg } +func readConfig(useconf *bool, useconffile *string, normaliseconf *bool) *nodeConfig { + // Use a configuration file. If -useconf, the configuration will be read + // from stdin. If -useconffile, the configuration will be read from the + // filesystem. + var config []byte + var err error + if *useconffile != "" { + // Read the file from the filesystem + config, err = ioutil.ReadFile(*useconffile) + } else { + // Read the file from stdin. + config, err = ioutil.ReadAll(os.Stdin) + } + if err != nil { + panic(err) + } + // If there's a byte order mark - which Windows 10 is now incredibly fond of + // throwing everywhere when it's converting things into UTF-16 for the hell + // of it - remove it and decode back down into UTF-8. This is necessary + // because hjson doesn't know what to do with UTF-16 and will panic + if bytes.Compare(config[0:2], []byte{0xFF, 0xFE}) == 0 || + bytes.Compare(config[0:2], []byte{0xFE, 0xFF}) == 0 { + utf := unicode.UTF16(unicode.BigEndian, unicode.UseBOM) + decoder := utf.NewDecoder() + config, err = decoder.Bytes(config) + if err != nil { + 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. + cfg := generateConfig(false) + var dat map[string]interface{} + if err := hjson.Unmarshal(config, &dat); err != nil { + panic(err) + } + confJson, err := json.Marshal(dat) + if err != nil { + panic(err) + } + json.Unmarshal(confJson, &cfg) + // For now we will do a little bit to help the user adjust their + // configuration to match the new configuration format, as some of the key + // names have changed recently. + changes := map[string]string{ + "Multicast": "", + "LinkLocal": "MulticastInterfaces", + "BoxPub": "EncryptionPublicKey", + "BoxPriv": "EncryptionPrivateKey", + "SigPub": "SigningPublicKey", + "SigPriv": "SigningPrivateKey", + "AllowedBoxPubs": "AllowedEncryptionPublicKeys", + } + // Loop over the mappings aove and see if we have anything to fix. + for from, to := range changes { + if _, ok := dat[from]; ok { + if to == "" { + if !*normaliseconf { + log.Println("Warning: Deprecated config option", from, "- please remove") + } + } else { + if !*normaliseconf { + log.Println("Warning: Deprecated config option", from, "- please rename to", to) + } + // If the configuration file doesn't already contain a line with the + // new name then set it to the old value. This makes sure that we + // don't overwrite something that was put there intentionally. + if _, ok := dat[to]; !ok { + dat[to] = dat[from] + } + } + } + } + // Check to see if the peers are in a parsable format, if not then default + // them to the TCP scheme + if peers, ok := dat["Peers"].([]interface{}); ok { + for index, peer := range peers { + uri := peer.(string) + if strings.HasPrefix(uri, "tcp://") || strings.HasPrefix(uri, "socks://") { + continue + } + if strings.HasPrefix(uri, "tcp:") { + uri = uri[4:] + } + (dat["Peers"].([]interface{}))[index] = "tcp://" + uri + } + } + // Now do the same with the interface peers + if interfacepeers, ok := dat["InterfacePeers"].(map[string]interface{}); ok { + for intf, peers := range interfacepeers { + for index, peer := range peers.([]interface{}) { + uri := peer.(string) + if strings.HasPrefix(uri, "tcp://") || strings.HasPrefix(uri, "socks://") { + continue + } + if strings.HasPrefix(uri, "tcp:") { + uri = uri[4:] + } + ((dat["InterfacePeers"].(map[string]interface{}))[intf]).([]interface{})[index] = "tcp://" + uri + } + } + } + // Overlay our newly mapped configuration onto the autoconf node config that + // we generated above. + if err = mapstructure.Decode(dat, &cfg); err != nil { + panic(err) + } + + return cfg +} + // Generates a new configuration and returns it in HJSON format. This is used // with -genconf. func doGenconf(isjson bool) string { @@ -106,6 +219,7 @@ func main() { flag.Parse() var cfg *nodeConfig + var err error switch { case *version: fmt.Println("Build name:", yggdrasil.GetBuildName()) @@ -116,114 +230,8 @@ func main() { // port numbers, and will use an automatically selected TUN/TAP interface. cfg = generateConfig(true) case *useconffile != "" || *useconf: - // Use a configuration file. If -useconf, the configuration will be read - // from stdin. If -useconffile, the configuration will be read from the - // filesystem. - var config []byte - var err error - if *useconffile != "" { - // Read the file from the filesystem - config, err = ioutil.ReadFile(*useconffile) - } else { - // Read the file from stdin. - config, err = ioutil.ReadAll(os.Stdin) - } - if err != nil { - panic(err) - } - // If there's a byte order mark - which Windows 10 is now incredibly fond of - // throwing everywhere when it's converting things into UTF-16 for the hell - // of it - remove it and decode back down into UTF-8. This is necessary - // because hjson doesn't know what to do with UTF-16 and will panic - if bytes.Compare(config[0:2], []byte{0xFF, 0xFE}) == 0 || - bytes.Compare(config[0:2], []byte{0xFE, 0xFF}) == 0 { - utf := unicode.UTF16(unicode.BigEndian, unicode.UseBOM) - decoder := utf.NewDecoder() - config, err = decoder.Bytes(config) - if err != nil { - 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. - cfg = generateConfig(false) - var dat map[string]interface{} - if err := hjson.Unmarshal(config, &dat); err != nil { - panic(err) - } - confJson, err := json.Marshal(dat) - if err != nil { - panic(err) - } - json.Unmarshal(confJson, &cfg) - // For now we will do a little bit to help the user adjust their - // configuration to match the new configuration format, as some of the key - // names have changed recently. - changes := map[string]string{ - "Multicast": "", - "LinkLocal": "MulticastInterfaces", - "BoxPub": "EncryptionPublicKey", - "BoxPriv": "EncryptionPrivateKey", - "SigPub": "SigningPublicKey", - "SigPriv": "SigningPrivateKey", - "AllowedBoxPubs": "AllowedEncryptionPublicKeys", - } - // Loop over the mappings aove and see if we have anything to fix. - for from, to := range changes { - if _, ok := dat[from]; ok { - if to == "" { - if !*normaliseconf { - log.Println("Warning: Deprecated config option", from, "- please remove") - } - } else { - if !*normaliseconf { - log.Println("Warning: Deprecated config option", from, "- please rename to", to) - } - // If the configuration file doesn't already contain a line with the - // new name then set it to the old value. This makes sure that we - // don't overwrite something that was put there intentionally. - if _, ok := dat[to]; !ok { - dat[to] = dat[from] - } - } - } - } - // Check to see if the peers are in a parsable format, if not then default - // them to the TCP scheme - if peers, ok := dat["Peers"].([]interface{}); ok { - for index, peer := range peers { - uri := peer.(string) - if strings.HasPrefix(uri, "tcp://") || strings.HasPrefix(uri, "socks://") { - continue - } - if strings.HasPrefix(uri, "tcp:") { - uri = uri[4:] - } - (dat["Peers"].([]interface{}))[index] = "tcp://" + uri - } - } - // Now do the same with the interface peers - if interfacepeers, ok := dat["InterfacePeers"].(map[string]interface{}); ok { - for intf, peers := range interfacepeers { - for index, peer := range peers.([]interface{}) { - uri := peer.(string) - if strings.HasPrefix(uri, "tcp://") || strings.HasPrefix(uri, "socks://") { - continue - } - if strings.HasPrefix(uri, "tcp:") { - uri = uri[4:] - } - ((dat["InterfacePeers"].(map[string]interface{}))[intf]).([]interface{})[index] = "tcp://" + uri - } - } - } - // Overlay our newly mapped configuration onto the autoconf node config that - // we generated above. - if err = mapstructure.Decode(dat, &cfg); err != nil { - panic(err) - } + // Read the configuration from either stdin or from the filesystem + cfg = readConfig(useconf, useconffile, normaliseconf) // If the -normaliseconf option was specified then remarshal the above // configuration and print it back to stdout. This lets the user update // their configuration file with newly mapped names (like above) or to @@ -327,7 +335,12 @@ func main() { for { select { case _ = <-r: - n.core.UpdateConfig(cfg) + if *useconffile != "" { + cfg = readConfig(useconf, useconffile, normaliseconf) + n.core.UpdateConfig(cfg) + } else { + logger.Println("Reloading config at runtime is only possible with -useconffile") + } case _ = <-c: goto exit } diff --git a/src/yggdrasil/admin.go b/src/yggdrasil/admin.go index 90fb1127..56823397 100644 --- a/src/yggdrasil/admin.go +++ b/src/yggdrasil/admin.go @@ -61,7 +61,9 @@ func (a *admin) init(c *Core) { case e := <-a.reconfigure: a.core.configMutex.RLock() if a.core.config.AdminListen != a.core.configOld.AdminListen { - a.core.log.Println("AdminListen has changed!") + a.listenaddr = a.core.config.AdminListen + a.close() + a.start() } a.core.configMutex.RUnlock() e <- nil From cb4495902bf9399b881d1a4aa6008b15715111fc Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 30 Dec 2018 15:21:09 +0000 Subject: [PATCH 06/40] Allow updating Listen during runtime --- src/yggdrasil/core.go | 13 ++++++----- src/yggdrasil/multicast.go | 16 ++++++++++--- src/yggdrasil/tcp.go | 48 +++++++++++++++++++++++++++++++++----- 3 files changed, 62 insertions(+), 15 deletions(-) diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index 435cd670..d4a22680 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -109,13 +109,14 @@ func (c *Core) UpdateConfig(config *config.NodeConfig) { components := []chan chan error{ c.admin.reconfigure, - c.searches.reconfigure, - c.dht.reconfigure, - c.sessions.reconfigure, + //c.searches.reconfigure, + //c.dht.reconfigure, + //c.sessions.reconfigure, + //c.peers.reconfigure, + //c.router.reconfigure, + //c.switchTable.reconfigure, + c.tcp.reconfigure, c.multicast.reconfigure, - c.peers.reconfigure, - c.router.reconfigure, - c.switchTable.reconfigure, } for _, component := range components { diff --git a/src/yggdrasil/multicast.go b/src/yggdrasil/multicast.go index 25c979c3..218f5162 100644 --- a/src/yggdrasil/multicast.go +++ b/src/yggdrasil/multicast.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "net" + "sync" "time" "golang.org/x/net/ipv6" @@ -14,6 +15,8 @@ type multicast struct { reconfigure chan chan error sock *ipv6.PacketConn groupAddr string + myAddr *net.TCPAddr + myAddrMutex sync.RWMutex } func (m *multicast) init(core *Core) { @@ -23,6 +26,9 @@ func (m *multicast) init(core *Core) { for { select { case e := <-m.reconfigure: + m.myAddrMutex.Lock() + m.myAddr = m.core.tcp.getAddr() + m.myAddrMutex.Unlock() e <- nil } } @@ -95,13 +101,14 @@ func (m *multicast) interfaces() []net.Interface { } func (m *multicast) announce() { + var anAddr net.TCPAddr + m.myAddrMutex.Lock() + m.myAddr = m.core.tcp.getAddr() + m.myAddrMutex.Unlock() groupAddr, err := net.ResolveUDPAddr("udp6", m.groupAddr) if err != nil { panic(err) } - var anAddr net.TCPAddr - myAddr := m.core.tcp.getAddr() - anAddr.Port = myAddr.Port destAddr, err := net.ResolveUDPAddr("udp6", m.groupAddr) if err != nil { panic(err) @@ -113,6 +120,9 @@ func (m *multicast) announce() { if err != nil { panic(err) } + m.myAddrMutex.RLock() + anAddr.Port = m.myAddr.Port + m.myAddrMutex.RUnlock() for _, addr := range addrs { addrIP, _, _ := net.ParseCIDR(addr.String()) if addrIP.To4() != nil { diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index ad50d780..224aca0f 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -38,7 +38,9 @@ const tcp_ping_interval = (default_tcp_timeout * 2 / 3) // The TCP listener and information about active TCP connections, to avoid duplication. type tcpInterface struct { core *Core + reconfigure chan chan error serv net.Listener + serv_stop chan bool tcp_timeout time.Duration tcp_addr string mutex sync.Mutex // Protecting the below @@ -83,10 +85,37 @@ func (iface *tcpInterface) connectSOCKS(socksaddr, peeraddr string) { // Initializes the struct. func (iface *tcpInterface) init(core *Core) (err error) { iface.core = core + iface.serv_stop = make(chan bool, 1) + iface.reconfigure = make(chan chan error, 1) + go func() { + for { + select { + case e := <-iface.reconfigure: + iface.core.configMutex.RLock() + updated := iface.core.config.Listen != iface.core.configOld.Listen + iface.core.configMutex.RUnlock() + if updated { + iface.serv_stop <- true + iface.serv.Close() + e <- iface.listen() + } else { + e <- nil + } + } + } + }() + + return iface.listen() +} + +func (iface *tcpInterface) listen() error { + var err error + iface.core.configMutex.RLock() iface.tcp_addr = iface.core.config.Listen iface.tcp_timeout = time.Duration(iface.core.config.ReadTimeout) * time.Millisecond iface.core.configMutex.RUnlock() + if iface.tcp_timeout >= 0 && iface.tcp_timeout < default_tcp_timeout { iface.tcp_timeout = default_tcp_timeout } @@ -96,6 +125,7 @@ func (iface *tcpInterface) init(core *Core) (err error) { iface.calls = make(map[string]struct{}) iface.conns = make(map[tcpInfo](chan struct{})) go iface.listener() + return nil } return err @@ -107,10 +137,16 @@ func (iface *tcpInterface) listener() { iface.core.log.Println("Listening for TCP on:", iface.serv.Addr().String()) for { sock, err := iface.serv.Accept() - if err != nil { - panic(err) + select { + case <-iface.serv_stop: + iface.core.log.Println("Stopping listener") + return + default: + if err != nil { + panic(err) + } + go iface.handler(sock, true) } - go iface.handler(sock, true) } } @@ -363,12 +399,12 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) { themAddr := address.AddrForNodeID(themNodeID) themAddrString := net.IP(themAddr[:]).String() themString := fmt.Sprintf("%s@%s", themAddrString, them) - iface.core.log.Println("Connected:", themString, "source", us) + iface.core.log.Printf("Connected: %s, source: %s", themString, us) err = iface.reader(sock, in) // In this goroutine, because of defers if err == nil { - iface.core.log.Println("Disconnected:", themString, "source", us) + iface.core.log.Printf("Disconnected: %s, source: %s", themString, us) } else { - iface.core.log.Println("Disconnected:", themString, "source", us, "with error:", err) + iface.core.log.Printf("Disconnected: %s, source: %s, error: %s", themString, us, err) } return } From 80c9a1bc12d90dee70411ce1f1995328a878ae46 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 30 Dec 2018 16:48:34 +0000 Subject: [PATCH 07/40] Don't track localAddr in conns as it is irrelevant --- src/yggdrasil/tcp.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index 224aca0f..4fb3026d 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -53,8 +53,8 @@ type tcpInterface struct { type tcpInfo struct { box crypto.BoxPubKey sig crypto.SigPubKey - localAddr string remoteAddr string + remotePort string } // Wrapper function to set additional options for specific connection types. @@ -313,8 +313,7 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) { } } // Check if we already have a connection to this node, close and block if yes - info.localAddr, _, _ = net.SplitHostPort(sock.LocalAddr().String()) - info.remoteAddr, _, _ = net.SplitHostPort(sock.RemoteAddr().String()) + info.remoteAddr, info.remotePort, _ = net.SplitHostPort(sock.RemoteAddr().String()) iface.mutex.Lock() if blockChan, isIn := iface.conns[info]; isIn { iface.mutex.Unlock() From cd86c338505003dc7779423e427c0e936885f727 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 30 Dec 2018 21:11:16 +0000 Subject: [PATCH 08/40] Try to tidy up a bit, move checks for if we are already calling/connected Something I noticed when working on reconfigure support for the "Listen" option is that we have some rather huge weaknesses in our multicasting design. Right now if we change our Listen address, it's not really possible for remote nodes to know whether they are still connected to us, so they start connecting in response to our changed beacons. They can't know that they already know about us until *after* the handshake but this registers in the local client log as repeated Connect/Disconnects even though the existing peerings never actually drop. --- src/yggdrasil/tcp.go | 74 +++++++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 28 deletions(-) diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index 4fb3026d..ec8bca46 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -53,8 +53,8 @@ type tcpInterface struct { type tcpInfo struct { box crypto.BoxPubKey sig crypto.SigPubKey + localAddr string remoteAddr string - remotePort string } // Wrapper function to set additional options for specific connection types. @@ -150,6 +150,22 @@ func (iface *tcpInterface) listener() { } } +// Checks if we already have a connection to this node +func (iface *tcpInterface) isAlreadyConnected(info tcpInfo) bool { + iface.mutex.Lock() + defer iface.mutex.Unlock() + _, isIn := iface.conns[info] + return isIn +} + +// Checks if we already are calling this address +func (iface *tcpInterface) isAlreadyCalling(saddr string) bool { + iface.mutex.Lock() + defer iface.mutex.Unlock() + _, isIn := iface.calls[saddr] + return isIn +} + // Checks if a connection already exists. // If not, it adds it to the list of active outgoing calls (to block future attempts) and dials the address. // If the dial is successful, it launches the handler. @@ -161,25 +177,18 @@ func (iface *tcpInterface) call(saddr string, socksaddr *string, sintf string) { if sintf != "" { callname = fmt.Sprintf("%s/%s", saddr, sintf) } - quit := false - iface.mutex.Lock() - if _, isIn := iface.calls[callname]; isIn { - quit = true - } else { - iface.calls[callname] = struct{}{} - defer func() { - // Block new calls for a little while, to mitigate livelock scenarios - time.Sleep(default_tcp_timeout) - time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond) - iface.mutex.Lock() - delete(iface.calls, callname) - iface.mutex.Unlock() - }() - } - iface.mutex.Unlock() - if quit { + if iface.isAlreadyCalling(saddr) { return } + iface.calls[callname] = struct{}{} + defer func() { + // Block new calls for a little while, to mitigate livelock scenarios + time.Sleep(default_tcp_timeout) + time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond) + iface.mutex.Lock() + delete(iface.calls, callname) + iface.mutex.Unlock() + }() var conn net.Conn var err error if socksaddr != nil { @@ -284,9 +293,19 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) { // TODO? Block forever to prevent future connection attempts? suppress future messages about the same node? return } + remoteAddr, _, e1 := net.SplitHostPort(sock.RemoteAddr().String()) + localAddr, _, e2 := net.SplitHostPort(sock.LocalAddr().String()) + if e1 != nil || e2 != nil { + return + } info := tcpInfo{ // used as a map key, so don't include ephemeral link key - box: meta.box, - sig: meta.sig, + box: meta.box, + sig: meta.sig, + localAddr: localAddr, + remoteAddr: remoteAddr, + } + if iface.isAlreadyConnected(info) { + return } // Quit the parent call if this is a connection to ourself equiv := func(k1, k2 []byte) bool { @@ -297,14 +316,14 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) { } return true } - if equiv(info.box[:], iface.core.boxPub[:]) { + if equiv(meta.box[:], iface.core.boxPub[:]) { return } - if equiv(info.sig[:], iface.core.sigPub[:]) { + if equiv(meta.sig[:], iface.core.sigPub[:]) { return } // Check if we're authorized to connect to this key / IP - if incoming && !iface.core.peers.isAllowedEncryptionPublicKey(&info.box) { + if incoming && !iface.core.peers.isAllowedEncryptionPublicKey(&meta.box) { // Allow unauthorized peers if they're link-local raddrStr, _, _ := net.SplitHostPort(sock.RemoteAddr().String()) raddr := net.ParseIP(raddrStr) @@ -313,14 +332,13 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) { } } // Check if we already have a connection to this node, close and block if yes - info.remoteAddr, info.remotePort, _ = net.SplitHostPort(sock.RemoteAddr().String()) iface.mutex.Lock() - if blockChan, isIn := iface.conns[info]; isIn { + /*if blockChan, isIn := iface.conns[info]; isIn { iface.mutex.Unlock() sock.Close() <-blockChan return - } + }*/ blockChan := make(chan struct{}) iface.conns[info] = blockChan iface.mutex.Unlock() @@ -332,7 +350,7 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) { }() // Note that multiple connections to the same node are allowed // E.g. over different interfaces - p := iface.core.peers.newPeer(&info.box, &info.sig, crypto.GetSharedKey(myLinkPriv, &meta.link), sock.RemoteAddr().String()) + p := iface.core.peers.newPeer(&meta.box, &meta.sig, crypto.GetSharedKey(myLinkPriv, &meta.link), sock.RemoteAddr().String()) p.linkOut = make(chan []byte, 1) in := func(bs []byte) { p.handlePacket(bs) @@ -394,7 +412,7 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) { }() us, _, _ := net.SplitHostPort(sock.LocalAddr().String()) them, _, _ := net.SplitHostPort(sock.RemoteAddr().String()) - themNodeID := crypto.GetNodeID(&info.box) + themNodeID := crypto.GetNodeID(&meta.box) themAddr := address.AddrForNodeID(themNodeID) themAddrString := net.IP(themAddr[:]).String() themString := fmt.Sprintf("%s@%s", themAddrString, them) From 1e29465af12fdd430815cc5d9b6d83469340f834 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 31 Dec 2018 12:08:15 +0000 Subject: [PATCH 09/40] Fix debug builds (hopefully) --- src/yggdrasil/debug.go | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/src/yggdrasil/debug.go b/src/yggdrasil/debug.go index 4a32eb64..6bd54302 100644 --- a/src/yggdrasil/debug.go +++ b/src/yggdrasil/debug.go @@ -16,6 +16,7 @@ import "fmt" import "net" import "log" import "regexp" +import "encoding/hex" import _ "net/http/pprof" import "net/http" @@ -23,6 +24,7 @@ import "runtime" import "os" import "github.com/yggdrasil-network/yggdrasil-go/src/address" +import "github.com/yggdrasil-network/yggdrasil-go/src/config" import "github.com/yggdrasil-network/yggdrasil-go/src/crypto" import "github.com/yggdrasil-network/yggdrasil-go/src/defaults" @@ -52,7 +54,17 @@ func StartProfiler(log *log.Logger) error { func (c *Core) Init() { bpub, bpriv := crypto.NewBoxKeys() spub, spriv := crypto.NewSigKeys() - c.init(bpub, bpriv, spub, spriv) + hbpub := hex.EncodeToString(bpub[:]) + hbpriv := hex.EncodeToString(bpriv[:]) + hspub := hex.EncodeToString(spub[:]) + hspriv := hex.EncodeToString(spriv[:]) + c.config = config.NodeConfig{ + EncryptionPublicKey: hbpub, + EncryptionPrivateKey: hbpriv, + SigningPublicKey: hspub, + SigningPrivateKey: hspriv, + } + c.init( /*bpub, bpriv, spub, spriv*/ ) c.switchTable.start() c.router.start() } @@ -350,7 +362,7 @@ func (c *Core) DEBUG_init(bpub []byte, bpriv []byte, spub []byte, spriv []byte) { - var boxPub crypto.BoxPubKey + /*var boxPub crypto.BoxPubKey var boxPriv crypto.BoxPrivKey var sigPub crypto.SigPubKey var sigPriv crypto.SigPrivKey @@ -358,7 +370,18 @@ func (c *Core) DEBUG_init(bpub []byte, copy(boxPriv[:], bpriv) copy(sigPub[:], spub) copy(sigPriv[:], spriv) - c.init(&boxPub, &boxPriv, &sigPub, &sigPriv) + c.init(&boxPub, &boxPriv, &sigPub, &sigPriv)*/ + hbpub := hex.EncodeToString(bpub[:]) + hbpriv := hex.EncodeToString(bpriv[:]) + hspub := hex.EncodeToString(spub[:]) + hspriv := hex.EncodeToString(spriv[:]) + c.config = config.NodeConfig{ + EncryptionPublicKey: hbpub, + EncryptionPrivateKey: hbpriv, + SigningPublicKey: hspub, + SigningPrivateKey: hspriv, + } + c.init( /*bpub, bpriv, spub, spriv*/ ) if err := c.router.start(); err != nil { panic(err) @@ -427,7 +450,8 @@ func (c *Core) DEBUG_addSOCKSConn(socksaddr, peeraddr string) { //* func (c *Core) DEBUG_setupAndStartGlobalTCPInterface(addrport string) { - if err := c.tcp.init(c, addrport, 0); err != nil { + c.config.Listen = addrport + if err := c.tcp.init(c /*, addrport, 0*/); err != nil { c.log.Println("Failed to start TCP interface:", err) panic(err) } @@ -474,7 +498,8 @@ func (c *Core) DEBUG_addKCPConn(saddr string) { func (c *Core) DEBUG_setupAndStartAdminInterface(addrport string) { a := admin{} - a.init(c, addrport) + c.config.AdminListen = addrport + a.init(c /*, addrport*/) c.admin = a } From ab4be3424b35822c06ce38f771c39351615412cf Mon Sep 17 00:00:00 2001 From: "Tristan B. Kildaire" Date: Wed, 9 Jan 2019 11:42:07 +0200 Subject: [PATCH 10/40] Spelling fixes for peer.go --- src/yggdrasil/peer.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/yggdrasil/peer.go b/src/yggdrasil/peer.go index 333561e5..fd045419 100644 --- a/src/yggdrasil/peer.go +++ b/src/yggdrasil/peer.go @@ -14,7 +14,7 @@ import ( ) // The peers struct represents peers with an active connection. -// Incomping packets are passed to the corresponding peer, which handles them somehow. +// Incoming packets are passed to the corresponding peer, which handles them somehow. // In most cases, this involves passing the packet to the handler for outgoing traffic to another peer. // In other cases, it's link protocol traffic used to build the spanning tree, in which case this checks signatures and passes the message along to the switch. type peers struct { @@ -97,7 +97,7 @@ type peer struct { close func() // Called when a peer is removed, to close the underlying connection, or via admin api } -// Creates a new peer with the specified box, sig, and linkShared keys, using the lowest unocupied port number. +// Creates a new peer with the specified box, sig, and linkShared keys, using the lowest unoccupied port number. func (ps *peers) newPeer(box *crypto.BoxPubKey, sig *crypto.SigPubKey, linkShared *crypto.BoxSharedKey, endpoint string) *peer { now := time.Now() p := peer{box: *box, @@ -342,7 +342,7 @@ func (p *peer) handleSwitchMsg(packet []byte) { } // This generates the bytes that we sign or check the signature of for a switchMsg. -// It begins with the next node's key, followed by the root and the timetsamp, followed by coords being advertised to the next node. +// It begins with the next node's key, followed by the root and the timestamp, followed by coords being advertised to the next node. func getBytesForSig(next *crypto.SigPubKey, msg *switchMsg) []byte { var loc switchLocator for _, hop := range msg.Hops { From 345979b502fdc45e1007ef12ecf34a80c573427c Mon Sep 17 00:00:00 2001 From: "Tristan B. Kildaire" Date: Wed, 9 Jan 2019 11:44:45 +0200 Subject: [PATCH 11/40] Spelling fixes for search.go --- src/yggdrasil/search.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yggdrasil/search.go b/src/yggdrasil/search.go index c85b719c..756ad278 100644 --- a/src/yggdrasil/search.go +++ b/src/yggdrasil/search.go @@ -30,7 +30,7 @@ const search_MAX_SEARCH_SIZE = 16 const search_RETRY_TIME = time.Second // Information about an ongoing search. -// Includes the targed NodeID, the bitmask to match it to an IP, and the list of nodes to visit / already visited. +// Includes the target NodeID, the bitmask to match it to an IP, and the list of nodes to visit / already visited. type searchInfo struct { dest crypto.NodeID mask crypto.NodeID From 08a71af2d82883649c2f210cb1720f0251fe17a7 Mon Sep 17 00:00:00 2001 From: "Tristan B. Kildaire" Date: Wed, 9 Jan 2019 11:49:12 +0200 Subject: [PATCH 12/40] Spelling fixes for switch.go --- src/yggdrasil/switch.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yggdrasil/switch.go b/src/yggdrasil/switch.go index 3c1dae61..2b0d5d37 100644 --- a/src/yggdrasil/switch.go +++ b/src/yggdrasil/switch.go @@ -4,7 +4,7 @@ package yggdrasil // It routes packets based on distance on the spanning tree // In general, this is *not* equivalent to routing on the tree // It falls back to the tree in the worst case, but it can take shortcuts too -// This is the part that makse routing reasonably efficient on scale-free graphs +// This is the part that makes routing reasonably efficient on scale-free graphs // TODO document/comment everything in a lot more detail From aed3c7e7845fbde73f720d6c82f55453038fe460 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 14 Jan 2019 14:25:52 +0000 Subject: [PATCH 13/40] Give nodeconfig to tun --- src/yggdrasil/adapter.go | 8 +++++--- src/yggdrasil/core.go | 17 ++++++++--------- src/yggdrasil/tun.go | 27 ++++++++++++++++++++++++++- 3 files changed, 39 insertions(+), 13 deletions(-) diff --git a/src/yggdrasil/adapter.go b/src/yggdrasil/adapter.go index 7fb6a19e..3ce80d2b 100644 --- a/src/yggdrasil/adapter.go +++ b/src/yggdrasil/adapter.go @@ -3,9 +3,10 @@ package yggdrasil // Defines the minimum required struct members for an adapter type (this is // now the base type for tunAdapter in tun.go) type Adapter struct { - core *Core - send chan<- []byte - recv <-chan []byte + core *Core + send chan<- []byte + recv <-chan []byte + reconfigure chan chan error } // Initialises the adapter. @@ -13,4 +14,5 @@ func (adapter *Adapter) init(core *Core, send chan<- []byte, recv <-chan []byte) adapter.core = core adapter.send = send adapter.recv = recv + adapter.reconfigure = make(chan chan error, 1) } diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index 7e10dbca..bee09acc 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -2,7 +2,6 @@ package yggdrasil import ( "encoding/hex" - "fmt" "io/ioutil" "log" "net" @@ -110,12 +109,13 @@ func (c *Core) UpdateConfig(config *config.NodeConfig) { components := []chan chan error{ c.admin.reconfigure, - //c.searches.reconfigure, - //c.dht.reconfigure, - //c.sessions.reconfigure, - //c.peers.reconfigure, - //c.router.reconfigure, - //c.switchTable.reconfigure, + c.searches.reconfigure, + c.dht.reconfigure, + c.sessions.reconfigure, + c.peers.reconfigure, + c.router.reconfigure, + c.router.tun.reconfigure, + c.switchTable.reconfigure, c.tcp.reconfigure, c.multicast.reconfigure, } @@ -240,8 +240,7 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error { return err } - ip := net.IP(c.router.addr[:]).String() - if err := c.router.tun.start(nc.IfName, nc.IfTAPMode, fmt.Sprintf("%s/%d", ip, 8*len(address.GetPrefix())-1), nc.IfMTU); err != nil { + if err := c.router.tun.start(); err != nil { c.log.Println("Failed to start TUN/TAP") return err } diff --git a/src/yggdrasil/tun.go b/src/yggdrasil/tun.go index 8c0f91d5..0bda3125 100644 --- a/src/yggdrasil/tun.go +++ b/src/yggdrasil/tun.go @@ -5,6 +5,8 @@ package yggdrasil import ( "bytes" "errors" + "fmt" + "net" "sync" "time" @@ -42,11 +44,34 @@ func getSupportedMTU(mtu int) int { func (tun *tunAdapter) init(core *Core, send chan<- []byte, recv <-chan []byte) { tun.Adapter.init(core, send, recv) tun.icmpv6.init(tun) + go func() { + for { + select { + case e := <-tun.reconfigure: + tun.core.configMutex.RLock() + updated := tun.core.config.IfName != tun.core.configOld.IfName || + tun.core.config.IfTAPMode != tun.core.configOld.IfTAPMode || + tun.core.config.IfMTU != tun.core.configOld.IfMTU + tun.core.configMutex.RUnlock() + if updated { + e <- nil + } else { + e <- nil + } + } + } + }() } // Starts the setup process for the TUN/TAP adapter, and if successful, starts // the read/write goroutines to handle packets on that interface. -func (tun *tunAdapter) start(ifname string, iftapmode bool, addr string, mtu int) error { +func (tun *tunAdapter) start() error { + tun.core.configMutex.RLock() + ifname := tun.core.config.IfName + iftapmode := tun.core.config.IfTAPMode + addr := fmt.Sprintf("%s/%d", net.IP(tun.core.router.addr[:]).String(), 8*len(address.GetPrefix())-1) + mtu := tun.core.config.IfMTU + tun.core.configMutex.RUnlock() if ifname != "none" { if err := tun.setup(ifname, iftapmode, addr, mtu); err != nil { return err From 87d393bd9f163039f01446ba968cc3e6d77b5e30 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 14 Jan 2019 17:21:15 +0000 Subject: [PATCH 14/40] Move add peer loop into Core, refresh it from active config --- cmd/yggdrasil/main.go | 22 ---------------------- src/yggdrasil/core.go | 38 +++++++++++++++++++++++++++++++++----- 2 files changed, 33 insertions(+), 27 deletions(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 262fb682..cc54d3dd 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -12,7 +12,6 @@ import ( "regexp" "strings" "syscall" - "time" "golang.org/x/text/encoding/unicode" @@ -243,27 +242,6 @@ func main() { for _, pBoxStr := range cfg.AllowedEncryptionPublicKeys { n.core.AddAllowedEncryptionPublicKey(pBoxStr) } - // If any static peers were provided in the configuration above then we should - // configure them. The loop ensures that disconnected peers will eventually - // be reconnected with. - go func() { - if len(cfg.Peers) == 0 && len(cfg.InterfacePeers) == 0 { - return - } - for { - for _, peer := range cfg.Peers { - n.core.AddPeer(peer, "") - time.Sleep(time.Second) - } - for intf, intfpeers := range cfg.InterfacePeers { - for _, peer := range intfpeers { - n.core.AddPeer(peer, intf) - time.Sleep(time.Second) - } - } - time.Sleep(time.Minute) - } - }() // The Stop function ensures that the TUN/TAP adapter is correctly shut down // before the program exits. defer func() { diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index bee09acc..a53449b5 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -7,6 +7,7 @@ import ( "net" "regexp" "sync" + "time" "github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/config" @@ -91,14 +92,39 @@ func (c *Core) init() error { c.router.init(c) c.switchTable.init(c) // TODO move before peers? before router? - if err := c.tcp.init(c); err != nil { - c.log.Println("Failed to start TCP interface") - return err - } - return nil } +// If any static peers were provided in the configuration above then we should +// configure them. The loop ensures that disconnected peers will eventually +// be reconnected with. +func (c *Core) addPeerLoop() { + for { + // Get the peers from the config - these could change! + c.configMutex.RLock() + peers := c.config.Peers + interfacepeers := c.config.InterfacePeers + c.configMutex.RUnlock() + + // Add peers from the Peers section + for _, peer := range peers { + c.AddPeer(peer, "") + time.Sleep(time.Second) + } + + // Add peers from the InterfacePeers section + for intf, intfpeers := range interfacepeers { + for _, peer := range intfpeers { + c.AddPeer(peer, intf) + time.Sleep(time.Second) + } + } + + // Sit for a while + time.Sleep(time.Minute) + } +} + // UpdateConfig updates the configuration in Core and then signals the // various module goroutines to reconfigure themselves if needed func (c *Core) UpdateConfig(config *config.NodeConfig) { @@ -245,6 +271,8 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error { return err } + go c.addPeerLoop() + c.log.Println("Startup complete") return nil } From 28072c9fe2f7587ec02ca212016c6e42809fd6d2 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 14 Jan 2019 17:41:08 +0000 Subject: [PATCH 15/40] Make CKR thread-safe --- src/yggdrasil/ckr.go | 66 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 58 insertions(+), 8 deletions(-) diff --git a/src/yggdrasil/ckr.go b/src/yggdrasil/ckr.go index a3df891e..84d60e36 100644 --- a/src/yggdrasil/ckr.go +++ b/src/yggdrasil/ckr.go @@ -7,6 +7,7 @@ import ( "fmt" "net" "sort" + "sync" "github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/crypto" @@ -16,14 +17,19 @@ import ( // allow traffic for non-Yggdrasil ranges to be routed over Yggdrasil. type cryptokey struct { - core *Core - enabled bool - ipv4routes []cryptokey_route - ipv6routes []cryptokey_route - ipv4cache map[address.Address]cryptokey_route - ipv6cache map[address.Address]cryptokey_route - ipv4sources []net.IPNet - ipv6sources []net.IPNet + core *Core + enabled bool + reconfigure chan chan error + ipv4routes []cryptokey_route + ipv6routes []cryptokey_route + ipv4cache map[address.Address]cryptokey_route + ipv6cache map[address.Address]cryptokey_route + ipv4sources []net.IPNet + ipv6sources []net.IPNet + mutexenabled sync.RWMutex // protects enabled + mutexroutes sync.RWMutex // protects ipv4routes, ipv6routes + mutexcache sync.RWMutex // protects ipv4cache, ipv6cache + mutexsources sync.RWMutex // protects ipv4sources, ipv6sources } type cryptokey_route struct { @@ -34,21 +40,43 @@ type cryptokey_route struct { // Initialise crypto-key routing. This must be done before any other CKR calls. func (c *cryptokey) init(core *Core) { c.core = core + c.reconfigure = make(chan chan error, 1) + go func() { + for { + select { + case e := <-c.reconfigure: + e <- nil + } + } + }() + + c.mutexroutes.Lock() c.ipv4routes = make([]cryptokey_route, 0) c.ipv6routes = make([]cryptokey_route, 0) + c.mutexroutes.Unlock() + + c.mutexcache.Lock() c.ipv4cache = make(map[address.Address]cryptokey_route, 0) c.ipv6cache = make(map[address.Address]cryptokey_route, 0) + c.mutexcache.Unlock() + + c.mutexsources.Lock() c.ipv4sources = make([]net.IPNet, 0) c.ipv6sources = make([]net.IPNet, 0) + c.mutexsources.Unlock() } // Enable or disable crypto-key routing. func (c *cryptokey) setEnabled(enabled bool) { + c.mutexenabled.Lock() + defer c.mutexenabled.Unlock() c.enabled = enabled } // Check if crypto-key routing is enabled. func (c *cryptokey) isEnabled() bool { + c.mutexenabled.RLock() + defer c.mutexenabled.RUnlock() return c.enabled } @@ -72,6 +100,9 @@ func (c *cryptokey) isValidSource(addr address.Address, addrlen int) bool { // Does it match a configured CKR source? if c.isEnabled() { + c.mutexsources.RLock() + defer c.mutexsources.RUnlock() + // Build our references to the routing sources var routingsources *[]net.IPNet @@ -98,6 +129,9 @@ func (c *cryptokey) isValidSource(addr address.Address, addrlen int) bool { // Adds a source subnet, which allows traffic with these source addresses to // be tunnelled using crypto-key routing. func (c *cryptokey) addSourceSubnet(cidr string) error { + c.mutexsources.Lock() + defer c.mutexsources.Unlock() + // Is the CIDR we've been given valid? _, ipnet, err := net.ParseCIDR(cidr) if err != nil { @@ -135,6 +169,9 @@ func (c *cryptokey) addSourceSubnet(cidr string) error { // Adds a destination route for the given CIDR to be tunnelled to the node // with the given BoxPubKey. func (c *cryptokey) addRoute(cidr string, dest string) error { + c.mutexroutes.Lock() + defer c.mutexroutes.Unlock() + // Is the CIDR we've been given valid? ipaddr, ipnet, err := net.ParseCIDR(cidr) if err != nil { @@ -209,6 +246,11 @@ func (c *cryptokey) addRoute(cidr string, dest string) error { // length specified in bytes) from the crypto-key routing table. An error is // returned if the address is not suitable or no route was found. func (c *cryptokey) getPublicKeyForAddress(addr address.Address, addrlen int) (crypto.BoxPubKey, error) { + c.mutexroutes.RLock() + c.mutexcache.RLock() + defer c.mutexroutes.RUnlock() + defer c.mutexcache.RUnlock() + // Check if the address is a valid Yggdrasil address - if so it // is exempt from all CKR checking if addr.IsValid() { @@ -269,6 +311,9 @@ func (c *cryptokey) getPublicKeyForAddress(addr address.Address, addrlen int) (c // Removes a source subnet, which allows traffic with these source addresses to // be tunnelled using crypto-key routing. func (c *cryptokey) removeSourceSubnet(cidr string) error { + c.mutexsources.Lock() + defer c.mutexsources.Unlock() + // Is the CIDR we've been given valid? _, ipnet, err := net.ParseCIDR(cidr) if err != nil { @@ -304,6 +349,11 @@ func (c *cryptokey) removeSourceSubnet(cidr string) error { // Removes a destination route for the given CIDR to be tunnelled to the node // with the given BoxPubKey. func (c *cryptokey) removeRoute(cidr string, dest string) error { + c.mutexroutes.Lock() + c.mutexcache.Lock() + defer c.mutexroutes.Unlock() + defer c.mutexcache.Unlock() + // Is the CIDR we've been given valid? _, ipnet, err := net.ParseCIDR(cidr) if err != nil { From bd04124e43ff42d80740bb93b413fc04245198c8 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 14 Jan 2019 18:06:41 +0000 Subject: [PATCH 16/40] Reconfigure support for crypto-key routing --- src/yggdrasil/ckr.go | 60 ++++++++++++++++++++++++++++++++++++----- src/yggdrasil/core.go | 25 ----------------- src/yggdrasil/router.go | 8 ++++++ 3 files changed, 62 insertions(+), 31 deletions(-) diff --git a/src/yggdrasil/ckr.go b/src/yggdrasil/ckr.go index 84d60e36..bf569fbb 100644 --- a/src/yggdrasil/ckr.go +++ b/src/yggdrasil/ckr.go @@ -45,25 +45,73 @@ func (c *cryptokey) init(core *Core) { for { select { case e := <-c.reconfigure: - e <- nil + e <- c.configure() } } }() + if err := c.configure(); err != nil { + c.core.log.Println("CKR configuration failed:", err) + } +} + +// Configure the CKR routes +func (c *cryptokey) configure() error { + c.core.configMutex.RLock() + defer c.core.configMutex.RUnlock() + + // Set enabled/disabled state + c.setEnabled(c.core.config.TunnelRouting.Enable) + + // Clear out existing routes c.mutexroutes.Lock() - c.ipv4routes = make([]cryptokey_route, 0) c.ipv6routes = make([]cryptokey_route, 0) + c.ipv4routes = make([]cryptokey_route, 0) c.mutexroutes.Unlock() + // Add IPv6 routes + for ipv6, pubkey := range c.core.config.TunnelRouting.IPv6Destinations { + if err := c.addRoute(ipv6, pubkey); err != nil { + return err + } + } + + // Add IPv4 routes + for ipv4, pubkey := range c.core.config.TunnelRouting.IPv4Destinations { + if err := c.addRoute(ipv4, pubkey); err != nil { + return err + } + } + + // Clear out existing sources + c.mutexsources.Lock() + c.ipv6sources = make([]net.IPNet, 0) + c.ipv4sources = make([]net.IPNet, 0) + c.mutexsources.Unlock() + + // Add IPv6 sources + c.ipv6sources = make([]net.IPNet, 0) + for _, source := range c.core.config.TunnelRouting.IPv6Sources { + if err := c.addSourceSubnet(source); err != nil { + return err + } + } + + // Add IPv4 sources + c.ipv4sources = make([]net.IPNet, 0) + for _, source := range c.core.config.TunnelRouting.IPv4Sources { + if err := c.addSourceSubnet(source); err != nil { + return err + } + } + + // Wipe the caches c.mutexcache.Lock() c.ipv4cache = make(map[address.Address]cryptokey_route, 0) c.ipv6cache = make(map[address.Address]cryptokey_route, 0) c.mutexcache.Unlock() - c.mutexsources.Lock() - c.ipv4sources = make([]net.IPNet, 0) - c.ipv6sources = make([]net.IPNet, 0) - c.mutexsources.Unlock() + return nil } // Enable or disable crypto-key routing. diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index a53449b5..4b00fc37 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -231,31 +231,6 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error { return err } - c.router.cryptokey.setEnabled(nc.TunnelRouting.Enable) - if c.router.cryptokey.isEnabled() { - c.log.Println("Crypto-key routing enabled") - for ipv6, pubkey := range nc.TunnelRouting.IPv6Destinations { - if err := c.router.cryptokey.addRoute(ipv6, pubkey); err != nil { - panic(err) - } - } - for _, source := range nc.TunnelRouting.IPv6Sources { - if err := c.router.cryptokey.addSourceSubnet(source); err != nil { - panic(err) - } - } - for ipv4, pubkey := range nc.TunnelRouting.IPv4Destinations { - if err := c.router.cryptokey.addRoute(ipv4, pubkey); err != nil { - panic(err) - } - } - for _, source := range nc.TunnelRouting.IPv4Sources { - if err := c.router.cryptokey.addSourceSubnet(source); err != nil { - panic(err) - } - } - } - if err := c.admin.start(); err != nil { c.log.Println("Failed to start admin socket") return err diff --git a/src/yggdrasil/router.go b/src/yggdrasil/router.go index 68fb025a..74fff3fd 100644 --- a/src/yggdrasil/router.go +++ b/src/yggdrasil/router.go @@ -127,6 +127,14 @@ func (r *router) mainLoop() { case f := <-r.admin: f() case e := <-r.reconfigure: + // Send reconfigure notification to cryptokey + response := make(chan error) + r.cryptokey.reconfigure <- response + if err := <-response; err != nil { + e <- err + } + + // Anything else to do? e <- nil } } From 51026d762ef9958353327f0180770b3e60e1555c Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 14 Jan 2019 18:24:35 +0000 Subject: [PATCH 17/40] Make session firewall thread-safe for config updates --- src/yggdrasil/session.go | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/yggdrasil/session.go b/src/yggdrasil/session.go index 3c8e0132..3cd1cf7b 100644 --- a/src/yggdrasil/session.go +++ b/src/yggdrasil/session.go @@ -7,6 +7,7 @@ package yggdrasil import ( "bytes" "encoding/hex" + "sync" "time" "github.com/yggdrasil-network/yggdrasil-go/src/address" @@ -115,6 +116,7 @@ type sessions struct { addrToPerm map[address.Address]*crypto.BoxPubKey subnetToPerm map[address.Subnet]*crypto.BoxPubKey // Options from the session firewall + sessionFirewallMutex sync.RWMutex sessionFirewallEnabled bool sessionFirewallAllowsDirect bool sessionFirewallAllowsRemote bool @@ -157,12 +159,16 @@ func (ss *sessions) init(core *Core) { // Enable or disable the session firewall func (ss *sessions) setSessionFirewallState(enabled bool) { + ss.sessionFirewallMutex.Lock() + defer ss.sessionFirewallMutex.Unlock() ss.sessionFirewallEnabled = enabled } // Set the session firewall defaults (first parameter is whether to allow // sessions from direct peers, second is whether to allow from remote nodes). func (ss *sessions) setSessionFirewallDefaults(allowsDirect bool, allowsRemote bool, alwaysAllowsOutbound bool) { + ss.sessionFirewallMutex.Lock() + defer ss.sessionFirewallMutex.Unlock() ss.sessionFirewallAllowsDirect = allowsDirect ss.sessionFirewallAllowsRemote = allowsRemote ss.sessionFirewallAlwaysAllowsOutbound = alwaysAllowsOutbound @@ -170,17 +176,24 @@ func (ss *sessions) setSessionFirewallDefaults(allowsDirect bool, allowsRemote b // Set the session firewall whitelist - nodes always allowed to open sessions. func (ss *sessions) setSessionFirewallWhitelist(whitelist []string) { + ss.sessionFirewallMutex.Lock() + defer ss.sessionFirewallMutex.Unlock() ss.sessionFirewallWhitelist = whitelist } // Set the session firewall blacklist - nodes never allowed to open sessions. func (ss *sessions) setSessionFirewallBlacklist(blacklist []string) { + ss.sessionFirewallMutex.Lock() + defer ss.sessionFirewallMutex.Unlock() ss.sessionFirewallBlacklist = blacklist } // Determines whether the session with a given publickey is allowed based on // session firewall rules. func (ss *sessions) isSessionAllowed(pubkey *crypto.BoxPubKey, initiator bool) bool { + ss.sessionFirewallMutex.RLock() + defer ss.sessionFirewallMutex.RUnlock() + // Allow by default if the session firewall is disabled if !ss.sessionFirewallEnabled { return true @@ -286,10 +299,8 @@ func (ss *sessions) getByTheirSubnet(snet *address.Subnet) (*sessionInfo, bool) // Creates a new session and lazily cleans up old/timedout existing sessions. // This includse initializing session info to sane defaults (e.g. lowest supported MTU). func (ss *sessions) createSession(theirPermKey *crypto.BoxPubKey) *sessionInfo { - if ss.sessionFirewallEnabled { - if !ss.isSessionAllowed(theirPermKey, true) { - return nil - } + if !ss.isSessionAllowed(theirPermKey, true) { + return nil } sinfo := sessionInfo{} sinfo.core = ss.core @@ -465,11 +476,14 @@ func (ss *sessions) handlePing(ping *sessionPing) { // Get the corresponding session (or create a new session) sinfo, isIn := ss.getByTheirPerm(&ping.SendPermPub) // Check the session firewall + ss.sessionFirewallMutex.RLock() if !isIn && ss.sessionFirewallEnabled { if !ss.isSessionAllowed(&ping.SendPermPub, false) { + ss.sessionFirewallMutex.RUnlock() return } } + ss.sessionFirewallMutex.RUnlock() if !isIn || sinfo.timedout() { if isIn { sinfo.close() From 9e186bdd6730d6c55c61858da429e9bf5bca410e Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 14 Jan 2019 18:34:15 +0000 Subject: [PATCH 18/40] Remove mutexes from CKR and use router goroutine/doAdmin for update config --- src/yggdrasil/ckr.go | 64 +++++++++++------------------------------ src/yggdrasil/core.go | 1 + src/yggdrasil/router.go | 8 ------ 3 files changed, 17 insertions(+), 56 deletions(-) diff --git a/src/yggdrasil/ckr.go b/src/yggdrasil/ckr.go index bf569fbb..14464f64 100644 --- a/src/yggdrasil/ckr.go +++ b/src/yggdrasil/ckr.go @@ -7,7 +7,6 @@ import ( "fmt" "net" "sort" - "sync" "github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/crypto" @@ -17,19 +16,15 @@ import ( // allow traffic for non-Yggdrasil ranges to be routed over Yggdrasil. type cryptokey struct { - core *Core - enabled bool - reconfigure chan chan error - ipv4routes []cryptokey_route - ipv6routes []cryptokey_route - ipv4cache map[address.Address]cryptokey_route - ipv6cache map[address.Address]cryptokey_route - ipv4sources []net.IPNet - ipv6sources []net.IPNet - mutexenabled sync.RWMutex // protects enabled - mutexroutes sync.RWMutex // protects ipv4routes, ipv6routes - mutexcache sync.RWMutex // protects ipv4cache, ipv6cache - mutexsources sync.RWMutex // protects ipv4sources, ipv6sources + core *Core + enabled bool + reconfigure chan chan error + ipv4routes []cryptokey_route + ipv6routes []cryptokey_route + ipv4cache map[address.Address]cryptokey_route + ipv6cache map[address.Address]cryptokey_route + ipv4sources []net.IPNet + ipv6sources []net.IPNet } type cryptokey_route struct { @@ -45,7 +40,11 @@ func (c *cryptokey) init(core *Core) { for { select { case e := <-c.reconfigure: - e <- c.configure() + var err error + c.core.router.doAdmin(func() { + err = c.core.router.cryptokey.configure() + }) + e <- err } } }() @@ -55,7 +54,8 @@ func (c *cryptokey) init(core *Core) { } } -// Configure the CKR routes +// Configure the CKR routes - this must only ever be called from the router +// goroutine, e.g. through router.doAdmin func (c *cryptokey) configure() error { c.core.configMutex.RLock() defer c.core.configMutex.RUnlock() @@ -64,10 +64,8 @@ func (c *cryptokey) configure() error { c.setEnabled(c.core.config.TunnelRouting.Enable) // Clear out existing routes - c.mutexroutes.Lock() c.ipv6routes = make([]cryptokey_route, 0) c.ipv4routes = make([]cryptokey_route, 0) - c.mutexroutes.Unlock() // Add IPv6 routes for ipv6, pubkey := range c.core.config.TunnelRouting.IPv6Destinations { @@ -84,10 +82,8 @@ func (c *cryptokey) configure() error { } // Clear out existing sources - c.mutexsources.Lock() c.ipv6sources = make([]net.IPNet, 0) c.ipv4sources = make([]net.IPNet, 0) - c.mutexsources.Unlock() // Add IPv6 sources c.ipv6sources = make([]net.IPNet, 0) @@ -106,25 +102,19 @@ func (c *cryptokey) configure() error { } // Wipe the caches - c.mutexcache.Lock() c.ipv4cache = make(map[address.Address]cryptokey_route, 0) c.ipv6cache = make(map[address.Address]cryptokey_route, 0) - c.mutexcache.Unlock() return nil } // Enable or disable crypto-key routing. func (c *cryptokey) setEnabled(enabled bool) { - c.mutexenabled.Lock() - defer c.mutexenabled.Unlock() c.enabled = enabled } // Check if crypto-key routing is enabled. func (c *cryptokey) isEnabled() bool { - c.mutexenabled.RLock() - defer c.mutexenabled.RUnlock() return c.enabled } @@ -148,9 +138,6 @@ func (c *cryptokey) isValidSource(addr address.Address, addrlen int) bool { // Does it match a configured CKR source? if c.isEnabled() { - c.mutexsources.RLock() - defer c.mutexsources.RUnlock() - // Build our references to the routing sources var routingsources *[]net.IPNet @@ -177,9 +164,6 @@ func (c *cryptokey) isValidSource(addr address.Address, addrlen int) bool { // Adds a source subnet, which allows traffic with these source addresses to // be tunnelled using crypto-key routing. func (c *cryptokey) addSourceSubnet(cidr string) error { - c.mutexsources.Lock() - defer c.mutexsources.Unlock() - // Is the CIDR we've been given valid? _, ipnet, err := net.ParseCIDR(cidr) if err != nil { @@ -217,9 +201,6 @@ func (c *cryptokey) addSourceSubnet(cidr string) error { // Adds a destination route for the given CIDR to be tunnelled to the node // with the given BoxPubKey. func (c *cryptokey) addRoute(cidr string, dest string) error { - c.mutexroutes.Lock() - defer c.mutexroutes.Unlock() - // Is the CIDR we've been given valid? ipaddr, ipnet, err := net.ParseCIDR(cidr) if err != nil { @@ -294,11 +275,6 @@ func (c *cryptokey) addRoute(cidr string, dest string) error { // length specified in bytes) from the crypto-key routing table. An error is // returned if the address is not suitable or no route was found. func (c *cryptokey) getPublicKeyForAddress(addr address.Address, addrlen int) (crypto.BoxPubKey, error) { - c.mutexroutes.RLock() - c.mutexcache.RLock() - defer c.mutexroutes.RUnlock() - defer c.mutexcache.RUnlock() - // Check if the address is a valid Yggdrasil address - if so it // is exempt from all CKR checking if addr.IsValid() { @@ -359,9 +335,6 @@ func (c *cryptokey) getPublicKeyForAddress(addr address.Address, addrlen int) (c // Removes a source subnet, which allows traffic with these source addresses to // be tunnelled using crypto-key routing. func (c *cryptokey) removeSourceSubnet(cidr string) error { - c.mutexsources.Lock() - defer c.mutexsources.Unlock() - // Is the CIDR we've been given valid? _, ipnet, err := net.ParseCIDR(cidr) if err != nil { @@ -397,11 +370,6 @@ func (c *cryptokey) removeSourceSubnet(cidr string) error { // Removes a destination route for the given CIDR to be tunnelled to the node // with the given BoxPubKey. func (c *cryptokey) removeRoute(cidr string, dest string) error { - c.mutexroutes.Lock() - c.mutexcache.Lock() - defer c.mutexroutes.Unlock() - defer c.mutexcache.Unlock() - // Is the CIDR we've been given valid? _, ipnet, err := net.ParseCIDR(cidr) if err != nil { diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index 4b00fc37..c6d6f4a6 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -141,6 +141,7 @@ func (c *Core) UpdateConfig(config *config.NodeConfig) { c.peers.reconfigure, c.router.reconfigure, c.router.tun.reconfigure, + c.router.cryptokey.reconfigure, c.switchTable.reconfigure, c.tcp.reconfigure, c.multicast.reconfigure, diff --git a/src/yggdrasil/router.go b/src/yggdrasil/router.go index 74fff3fd..68fb025a 100644 --- a/src/yggdrasil/router.go +++ b/src/yggdrasil/router.go @@ -127,14 +127,6 @@ func (r *router) mainLoop() { case f := <-r.admin: f() case e := <-r.reconfigure: - // Send reconfigure notification to cryptokey - response := make(chan error) - r.cryptokey.reconfigure <- response - if err := <-response; err != nil { - e <- err - } - - // Anything else to do? e <- nil } } From 5cde3b5efc5ef4b72ae7e6fcba941b68f171a522 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 14 Jan 2019 18:51:49 +0000 Subject: [PATCH 19/40] Update nodeinfo in router reconfigure --- src/yggdrasil/admin.go | 5 ++++- src/yggdrasil/router.go | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/yggdrasil/admin.go b/src/yggdrasil/admin.go index 4c864b3a..a44044ad 100644 --- a/src/yggdrasil/admin.go +++ b/src/yggdrasil/admin.go @@ -350,7 +350,10 @@ func (a *admin) init(c *Core) { } var box_pub_key, coords string if in["box_pub_key"] == nil && in["coords"] == nil { - nodeinfo := []byte(a.core.nodeinfo.getNodeInfo()) + var nodeinfo []byte + a.core.router.doAdmin(func() { + nodeinfo = []byte(a.core.nodeinfo.getNodeInfo()) + }) var jsoninfo interface{} if err := json.Unmarshal(nodeinfo, &jsoninfo); err != nil { return admin_info{}, err diff --git a/src/yggdrasil/router.go b/src/yggdrasil/router.go index 68fb025a..b4e16a3a 100644 --- a/src/yggdrasil/router.go +++ b/src/yggdrasil/router.go @@ -127,7 +127,9 @@ func (r *router) mainLoop() { case f := <-r.admin: f() case e := <-r.reconfigure: - e <- nil + r.core.configMutex.RLock() + e <- r.core.nodeinfo.setNodeInfo(r.core.config.NodeInfo, r.core.config.NodeInfoPrivacy) + r.core.configMutex.RUnlock() } } } From 9e486ed4fe7ea68e4aa5618611a2eaf659c004ce Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 14 Jan 2019 19:05:16 +0000 Subject: [PATCH 20/40] Move nodeinfo into router --- src/yggdrasil/admin.go | 8 ++++---- src/yggdrasil/core.go | 8 ++------ src/yggdrasil/nodeinfo.go | 2 +- src/yggdrasil/router.go | 8 ++++++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/yggdrasil/admin.go b/src/yggdrasil/admin.go index a44044ad..db856296 100644 --- a/src/yggdrasil/admin.go +++ b/src/yggdrasil/admin.go @@ -352,7 +352,7 @@ func (a *admin) init(c *Core) { if in["box_pub_key"] == nil && in["coords"] == nil { var nodeinfo []byte a.core.router.doAdmin(func() { - nodeinfo = []byte(a.core.nodeinfo.getNodeInfo()) + nodeinfo = []byte(a.core.router.nodeinfo.getNodeInfo()) }) var jsoninfo interface{} if err := json.Unmarshal(nodeinfo, &jsoninfo); err != nil { @@ -864,7 +864,7 @@ func (a *admin) admin_getNodeInfo(keyString, coordString string, nocache bool) ( copy(key[:], keyBytes) } if !nocache { - if response, err := a.core.nodeinfo.getCachedNodeInfo(key); err == nil { + if response, err := a.core.router.nodeinfo.getCachedNodeInfo(key); err == nil { return response, nil } } @@ -882,14 +882,14 @@ func (a *admin) admin_getNodeInfo(keyString, coordString string, nocache bool) ( } response := make(chan *nodeinfoPayload, 1) sendNodeInfoRequest := func() { - a.core.nodeinfo.addCallback(key, func(nodeinfo *nodeinfoPayload) { + a.core.router.nodeinfo.addCallback(key, func(nodeinfo *nodeinfoPayload) { defer func() { recover() }() select { case response <- nodeinfo: default: } }) - a.core.nodeinfo.sendNodeInfo(key, coords, false) + a.core.router.nodeinfo.sendNodeInfo(key, coords, false) } a.core.router.doAdmin(sendNodeInfoRequest) go func() { diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index c6d6f4a6..dc1b8f01 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -44,7 +44,6 @@ type Core struct { admin admin searches searches multicast multicast - nodeinfo nodeinfo tcp tcpInterface awdl awdl log *log.Logger @@ -83,7 +82,6 @@ func (c *Core) init() error { copy(c.sigPriv[:], sigPrivHex) c.admin.init(c) - c.nodeinfo.init(c) c.searches.init(c) c.dht.init(c) c.sessions.init(c) @@ -197,8 +195,6 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error { c.init() - c.nodeinfo.setNodeInfo(nc.NodeInfo, nc.NodeInfoPrivacy) - if err := c.tcp.init(c); err != nil { c.log.Println("Failed to start TCP interface") return err @@ -297,12 +293,12 @@ func (c *Core) GetSubnet() *net.IPNet { // Gets the nodeinfo. func (c *Core) GetNodeInfo() nodeinfoPayload { - return c.nodeinfo.getNodeInfo() + return c.router.nodeinfo.getNodeInfo() } // Sets the nodeinfo. func (c *Core) SetNodeInfo(nodeinfo interface{}, nodeinfoprivacy bool) { - c.nodeinfo.setNodeInfo(nodeinfo, nodeinfoprivacy) + c.router.nodeinfo.setNodeInfo(nodeinfo, nodeinfoprivacy) } // Sets the output logger of the Yggdrasil node after startup. This may be diff --git a/src/yggdrasil/nodeinfo.go b/src/yggdrasil/nodeinfo.go index b9076328..963a2fc2 100644 --- a/src/yggdrasil/nodeinfo.go +++ b/src/yggdrasil/nodeinfo.go @@ -170,7 +170,7 @@ func (m *nodeinfo) sendNodeInfo(key crypto.BoxPubKey, coords []byte, isResponse nodeinfo := nodeinfoReqRes{ SendCoords: table.self.getCoords(), IsResponse: isResponse, - NodeInfo: m.core.nodeinfo.getNodeInfo(), + NodeInfo: m.getNodeInfo(), } bs := nodeinfo.encode() shared := m.core.sessions.getSharedKey(&m.core.boxPriv, &key) diff --git a/src/yggdrasil/router.go b/src/yggdrasil/router.go index b4e16a3a..11509d44 100644 --- a/src/yggdrasil/router.go +++ b/src/yggdrasil/router.go @@ -51,6 +51,7 @@ type router struct { reset chan struct{} // signal that coords changed (re-init sessions/dht) admin chan func() // pass a lambda for the admin socket to query stuff cryptokey cryptokey + nodeinfo nodeinfo } // Packet and session info, used to check that the packet matches a valid IP range or CKR prefix before sending to the tun. @@ -85,6 +86,9 @@ func (r *router) init(core *Core) { r.send = send r.reset = make(chan struct{}, 1) r.admin = make(chan func(), 32) + r.core.configMutex.RLock() + r.nodeinfo.setNodeInfo(r.core.config.NodeInfo, r.core.config.NodeInfoPrivacy) + r.core.configMutex.RUnlock() r.cryptokey.init(r.core) r.tun.init(r.core, send, recv) } @@ -128,7 +132,7 @@ func (r *router) mainLoop() { f() case e := <-r.reconfigure: r.core.configMutex.RLock() - e <- r.core.nodeinfo.setNodeInfo(r.core.config.NodeInfo, r.core.config.NodeInfoPrivacy) + e <- r.nodeinfo.setNodeInfo(r.core.config.NodeInfo, r.core.config.NodeInfoPrivacy) r.core.configMutex.RUnlock() } } @@ -469,7 +473,7 @@ func (r *router) handleNodeInfo(bs []byte, fromKey *crypto.BoxPubKey) { return } req.SendPermPub = *fromKey - r.core.nodeinfo.handleNodeInfo(&req) + r.nodeinfo.handleNodeInfo(&req) } // Passed a function to call. From f6b663c2578ae5855e5c915233b55cb4342baa7f Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 14 Jan 2019 19:27:13 +0000 Subject: [PATCH 21/40] Make multicasting use config instead of ifceExpr in Core --- cmd/yggdrasil/main.go | 10 ---------- src/yggdrasil/core.go | 9 --------- src/yggdrasil/multicast.go | 21 ++++++++++++++------- 3 files changed, 14 insertions(+), 26 deletions(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index cc54d3dd..c3add0ce 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -9,7 +9,6 @@ import ( "log" "os" "os/signal" - "regexp" "strings" "syscall" @@ -221,15 +220,6 @@ func main() { // Setup the Yggdrasil node itself. The node{} type includes a Core, so we // don't need to create this manually. n := node{} - // Check to see if any multicast interface expressions were provided in the - // config. If they were then set them now. - for _, ll := range cfg.MulticastInterfaces { - ifceExpr, err := regexp.Compile(ll) - if err != nil { - panic(err) - } - n.core.AddMulticastInterfaceExpr(ifceExpr) - } // Now that we have a working configuration, we can now actually start // Yggdrasil. This will start the router, switch, DHT node, TCP and UDP // sockets, TUN/TAP adapter and multicast discovery port. diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index dc1b8f01..3382562e 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -5,7 +5,6 @@ import ( "io/ioutil" "log" "net" - "regexp" "sync" "time" @@ -47,7 +46,6 @@ type Core struct { tcp tcpInterface awdl awdl log *log.Logger - ifceExpr []*regexp.Regexp // the zone of link-local IPv6 peers must match this } func (c *Core) init() error { @@ -313,13 +311,6 @@ func (c *Core) AddPeer(addr string, sintf string) error { return c.admin.addPeer(addr, sintf) } -// Adds an expression to select multicast interfaces for peer discovery. This -// should be done before calling Start. This function can be called multiple -// times to add multiple search expressions. -func (c *Core) AddMulticastInterfaceExpr(expr *regexp.Regexp) { - c.ifceExpr = append(c.ifceExpr, expr) -} - // Adds an allowed public key. This allow peerings to be restricted only to // keys that you have selected. func (c *Core) AddAllowedEncryptionPublicKey(boxStr string) error { diff --git a/src/yggdrasil/multicast.go b/src/yggdrasil/multicast.go index cf84fed6..40878951 100644 --- a/src/yggdrasil/multicast.go +++ b/src/yggdrasil/multicast.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "net" + "regexp" "sync" "time" @@ -35,15 +36,13 @@ func (m *multicast) init(core *Core) { }() m.groupAddr = "[ff02::114]:9001" // Check if we've been given any expressions - if len(m.core.ifceExpr) == 0 { - return + if count := len(m.interfaces()); count != 0 { + m.core.log.Println("Found", count, "multicast interface(s)") } - // Ask the system for network interfaces - m.core.log.Println("Found", len(m.interfaces()), "multicast interface(s)") } func (m *multicast) start() error { - if len(m.core.ifceExpr) == 0 { + if len(m.interfaces()) == 0 { m.core.log.Println("Multicast discovery is disabled") } else { m.core.log.Println("Multicast discovery is enabled") @@ -71,6 +70,10 @@ func (m *multicast) start() error { } func (m *multicast) interfaces() []net.Interface { + // Get interface expressions from config + m.core.configMutex.RLock() + exprs := m.core.config.MulticastInterfaces + m.core.configMutex.RUnlock() // Ask the system for network interfaces var interfaces []net.Interface allifaces, err := net.Interfaces() @@ -91,8 +94,12 @@ func (m *multicast) interfaces() []net.Interface { // Ignore point-to-point interfaces continue } - for _, expr := range m.core.ifceExpr { - if expr.MatchString(iface.Name) { + for _, expr := range exprs { + e, err := regexp.Compile(expr) + if err != nil { + panic(err) + } + if e.MatchString(iface.Name) { interfaces = append(interfaces, iface) } } From d9ddf30faf2998c1ad814253a1dadf927527eeee Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 14 Jan 2019 19:29:22 +0000 Subject: [PATCH 22/40] Fix debug builds --- src/yggdrasil/debug.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yggdrasil/debug.go b/src/yggdrasil/debug.go index 6bd54302..7c6c7570 100644 --- a/src/yggdrasil/debug.go +++ b/src/yggdrasil/debug.go @@ -517,7 +517,7 @@ func (c *Core) DEBUG_setLogger(log *log.Logger) { } func (c *Core) DEBUG_setIfceExpr(expr *regexp.Regexp) { - c.ifceExpr = append(c.ifceExpr, expr) + c.log.Println("DEBUG_setIfceExpr no longer implemented") } func (c *Core) DEBUG_addAllowedEncryptionPublicKey(boxStr string) { From 88cf6b768446e5b4f21fd3f5ea546d230deb3cd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Christian=20Gr=C3=BCnhage?= Date: Tue, 15 Jan 2019 02:28:27 +0100 Subject: [PATCH 23/40] add ansible key generator --- contrib/ansible/genkeys.go | 123 +++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 contrib/ansible/genkeys.go diff --git a/contrib/ansible/genkeys.go b/contrib/ansible/genkeys.go new file mode 100644 index 00000000..15785754 --- /dev/null +++ b/contrib/ansible/genkeys.go @@ -0,0 +1,123 @@ +/* + +This file generates crypto keys for [ansible-yggdrasil](https://github.com/jcgruenhage/ansible-yggdrasil/) + +*/ +package main + +import ( + "encoding/hex" + "flag" + "fmt" + "net" + "os" + + "github.com/yggdrasil-network/yggdrasil-go/src/address" + "github.com/yggdrasil-network/yggdrasil-go/src/crypto" +) + +var numHosts = flag.Int("hosts", 1, "number of host vars to generate") +var keyTries = flag.Int("tries", 1000, "number of tries before taking the best keys") + +type keySet struct { + priv []byte + pub []byte + id []byte + ip string +} + +func main() { + flag.Parse() + + if *numHosts > *keyTries { + println("Can't generate less keys than hosts.") + return + } + + var encryptionKeys []keySet + for i := 0; i < *numHosts + 1; i++ { + encryptionKeys = append(encryptionKeys, newBoxKey()) + } + encryptionKeys = sortKeySetArray(encryptionKeys) + for i := 0; i < *keyTries - *numHosts - 1; i++ { + encryptionKeys[0] = newBoxKey(); + encryptionKeys = bubbleUpTo(encryptionKeys, 0) + } + + var signatureKeys []keySet + for i := 0; i < *numHosts + 1; i++ { + signatureKeys = append(signatureKeys, newSigKey()) + } + signatureKeys = sortKeySetArray(signatureKeys) + for i := 0; i < *keyTries - *numHosts - 1; i++ { + signatureKeys[0] = newSigKey(); + signatureKeys = bubbleUpTo(signatureKeys, 0) + } + + os.MkdirAll("ansible/host_vars", 0755) + + for i := 1; i <= *numHosts; i++ { + os.MkdirAll(fmt.Sprintf("ansible/host_vars/%x", i), 0755) + file, err := os.Create(fmt.Sprintf("ansible/host_vars/%x/vars", i)) + if err != nil { + return + } + defer file.Close() + file.WriteString(fmt.Sprintf("yggdrasil_encryption_public_key: %v\n", hex.EncodeToString(encryptionKeys[i].pub))) + file.WriteString("yggdrasil_encryption_private_key: \"{{ vault_yggdrasil_encryption_private_key }}\"\n") + file.WriteString(fmt.Sprintf("yggdrasil_signing_public_key: %v\n", hex.EncodeToString(signatureKeys[i].pub))) + file.WriteString("yggdrasil_signing_public_key: \"{{ vault_yggdrasil_signing_private_key }}\"\n") + file.WriteString(fmt.Sprintf("ansible_host: %v\n", encryptionKeys[i].ip)) + + file, err = os.Create(fmt.Sprintf("ansible/host_vars/%x/vault", i)) + if err != nil { + return + } + defer file.Close() + file.WriteString(fmt.Sprintf("vault_yggdrasil_encryption_private_key: %v\n", hex.EncodeToString(encryptionKeys[i].priv))) + file.WriteString(fmt.Sprintf("vault_yggdrasil_signing_private_key: %v\n", hex.EncodeToString(signatureKeys[i].priv))) + } +} + +func newBoxKey() keySet { + pub, priv := crypto.NewBoxKeys() + id := crypto.GetNodeID(pub) + ip := net.IP(address.AddrForNodeID(id)[:]).String() + return keySet{priv[:], pub[:], id[:], ip} +} + +func newSigKey() keySet { + pub, priv := crypto.NewSigKeys() + id := crypto.GetTreeID(pub) + return keySet{priv[:], pub[:], id[:], ""} +} + +func isBetter(oldID, newID []byte) bool { + for idx := range oldID { + if newID[idx] > oldID[idx] { + return true + } + if newID[idx] < oldID[idx] { + return false + } + } + return false +} + +func sortKeySetArray(sets []keySet) []keySet { + for i := 0; i < len(sets); i++ { + sets = bubbleUpTo(sets, i) + } + return sets +} + +func bubbleUpTo(sets []keySet, num int) []keySet { + for i := 0; i < len(sets) - num - 1; i++ { + if isBetter(sets[i + 1].id, sets[i].id) { + var tmp = sets[i] + sets[i] = sets[i + 1] + sets[i + 1] = tmp + } + } + return sets +} From 39567bed83308cc1431943eb91ea321fb5e33d14 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 15 Jan 2019 08:44:33 +0000 Subject: [PATCH 24/40] Address some comments --- src/yggdrasil/ckr.go | 14 ++++++-------- src/yggdrasil/tun.go | 1 + 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/yggdrasil/ckr.go b/src/yggdrasil/ckr.go index 14464f64..a5ed4552 100644 --- a/src/yggdrasil/ckr.go +++ b/src/yggdrasil/ckr.go @@ -38,14 +38,12 @@ func (c *cryptokey) init(core *Core) { c.reconfigure = make(chan chan error, 1) go func() { for { - select { - case e := <-c.reconfigure: - var err error - c.core.router.doAdmin(func() { - err = c.core.router.cryptokey.configure() - }) - e <- err - } + e := <-c.reconfigure + var err error + c.core.router.doAdmin(func() { + err = c.core.router.cryptokey.configure() + }) + e <- err } }() diff --git a/src/yggdrasil/tun.go b/src/yggdrasil/tun.go index 0bda3125..46fabbc7 100644 --- a/src/yggdrasil/tun.go +++ b/src/yggdrasil/tun.go @@ -54,6 +54,7 @@ func (tun *tunAdapter) init(core *Core, send chan<- []byte, recv <-chan []byte) tun.core.config.IfMTU != tun.core.configOld.IfMTU tun.core.configMutex.RUnlock() if updated { + tun.core.log.Println("Reconfiguring TUN/TAP is not supported yet") e <- nil } else { e <- nil From 2cd373fc1e41a115299dc8f56bd2d4e6b0b40ab6 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 15 Jan 2019 08:51:19 +0000 Subject: [PATCH 25/40] Remove unnecessary selects --- src/yggdrasil/admin.go | 18 ++++++++---------- src/yggdrasil/dht.go | 6 ++---- src/yggdrasil/multicast.go | 12 +++++------- src/yggdrasil/peer.go | 6 ++---- src/yggdrasil/search.go | 6 ++---- src/yggdrasil/session.go | 26 ++++++++++++-------------- src/yggdrasil/tcp.go | 22 ++++++++++------------ src/yggdrasil/tun.go | 24 +++++++++++------------- 8 files changed, 52 insertions(+), 68 deletions(-) diff --git a/src/yggdrasil/admin.go b/src/yggdrasil/admin.go index db856296..5524fe25 100644 --- a/src/yggdrasil/admin.go +++ b/src/yggdrasil/admin.go @@ -57,17 +57,15 @@ func (a *admin) init(c *Core) { a.reconfigure = make(chan chan error, 1) go func() { for { - select { - case e := <-a.reconfigure: - a.core.configMutex.RLock() - if a.core.config.AdminListen != a.core.configOld.AdminListen { - a.listenaddr = a.core.config.AdminListen - a.close() - a.start() - } - a.core.configMutex.RUnlock() - e <- nil + e := <-a.reconfigure + a.core.configMutex.RLock() + if a.core.config.AdminListen != a.core.configOld.AdminListen { + a.listenaddr = a.core.config.AdminListen + a.close() + a.start() } + a.core.configMutex.RUnlock() + e <- nil } }() a.core.configMutex.RLock() diff --git a/src/yggdrasil/dht.go b/src/yggdrasil/dht.go index bba6dfc7..5427aca9 100644 --- a/src/yggdrasil/dht.go +++ b/src/yggdrasil/dht.go @@ -82,10 +82,8 @@ func (t *dht) init(c *Core) { t.reconfigure = make(chan chan error, 1) go func() { for { - select { - case e := <-t.reconfigure: - e <- nil - } + e := <-t.reconfigure + e <- nil } }() t.nodeID = *t.core.GetNodeID() diff --git a/src/yggdrasil/multicast.go b/src/yggdrasil/multicast.go index 40878951..08f0954e 100644 --- a/src/yggdrasil/multicast.go +++ b/src/yggdrasil/multicast.go @@ -25,13 +25,11 @@ func (m *multicast) init(core *Core) { m.reconfigure = make(chan chan error, 1) go func() { for { - select { - case e := <-m.reconfigure: - m.myAddrMutex.Lock() - m.myAddr = m.core.tcp.getAddr() - m.myAddrMutex.Unlock() - e <- nil - } + e := <-m.reconfigure + m.myAddrMutex.Lock() + m.myAddr = m.core.tcp.getAddr() + m.myAddrMutex.Unlock() + e <- nil } }() m.groupAddr = "[ff02::114]:9001" diff --git a/src/yggdrasil/peer.go b/src/yggdrasil/peer.go index 98cbe02f..c83504fc 100644 --- a/src/yggdrasil/peer.go +++ b/src/yggdrasil/peer.go @@ -35,10 +35,8 @@ func (ps *peers) init(c *Core) { ps.reconfigure = make(chan chan error, 1) go func() { for { - select { - case e := <-ps.reconfigure: - e <- nil - } + e := <-ps.reconfigure + e <- nil } }() ps.allowedEncryptionPublicKeys = make(map[crypto.BoxPubKey]struct{}) diff --git a/src/yggdrasil/search.go b/src/yggdrasil/search.go index 8106fb70..c391dda2 100644 --- a/src/yggdrasil/search.go +++ b/src/yggdrasil/search.go @@ -53,10 +53,8 @@ func (s *searches) init(core *Core) { s.reconfigure = make(chan chan error, 1) go func() { for { - select { - case e := <-s.reconfigure: - e <- nil - } + e := <-s.reconfigure + e <- nil } }() s.searches = make(map[crypto.NodeID]*searchInfo) diff --git a/src/yggdrasil/session.go b/src/yggdrasil/session.go index 3cd1cf7b..e29cd4fa 100644 --- a/src/yggdrasil/session.go +++ b/src/yggdrasil/session.go @@ -131,21 +131,19 @@ func (ss *sessions) init(core *Core) { ss.reconfigure = make(chan chan error, 1) go func() { for { - select { - case e := <-ss.reconfigure: - responses := make(map[crypto.Handle]chan error) - for index, session := range ss.sinfos { - responses[index] = make(chan error) - session.reconfigure <- responses[index] - } - for _, response := range responses { - if err := <-response; err != nil { - e <- err - continue - } - } - e <- nil + e := <-ss.reconfigure + responses := make(map[crypto.Handle]chan error) + for index, session := range ss.sinfos { + responses[index] = make(chan error) + session.reconfigure <- responses[index] } + for _, response := range responses { + if err := <-response; err != nil { + e <- err + continue + } + } + e <- nil } }() ss.permShared = make(map[crypto.BoxPubKey]*crypto.BoxSharedKey) diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index b47b5531..9cab9ea2 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -90,18 +90,16 @@ func (iface *tcpInterface) init(core *Core) (err error) { iface.reconfigure = make(chan chan error, 1) go func() { for { - select { - case e := <-iface.reconfigure: - iface.core.configMutex.RLock() - updated := iface.core.config.Listen != iface.core.configOld.Listen - iface.core.configMutex.RUnlock() - if updated { - iface.serv_stop <- true - iface.serv.Close() - e <- iface.listen() - } else { - e <- nil - } + e := <-iface.reconfigure + iface.core.configMutex.RLock() + updated := iface.core.config.Listen != iface.core.configOld.Listen + iface.core.configMutex.RUnlock() + if updated { + iface.serv_stop <- true + iface.serv.Close() + e <- iface.listen() + } else { + e <- nil } } }() diff --git a/src/yggdrasil/tun.go b/src/yggdrasil/tun.go index 46fabbc7..c0a71396 100644 --- a/src/yggdrasil/tun.go +++ b/src/yggdrasil/tun.go @@ -46,19 +46,17 @@ func (tun *tunAdapter) init(core *Core, send chan<- []byte, recv <-chan []byte) tun.icmpv6.init(tun) go func() { for { - select { - case e := <-tun.reconfigure: - tun.core.configMutex.RLock() - updated := tun.core.config.IfName != tun.core.configOld.IfName || - tun.core.config.IfTAPMode != tun.core.configOld.IfTAPMode || - tun.core.config.IfMTU != tun.core.configOld.IfMTU - tun.core.configMutex.RUnlock() - if updated { - tun.core.log.Println("Reconfiguring TUN/TAP is not supported yet") - e <- nil - } else { - e <- nil - } + e := <-tun.reconfigure + tun.core.configMutex.RLock() + updated := tun.core.config.IfName != tun.core.configOld.IfName || + tun.core.config.IfTAPMode != tun.core.configOld.IfTAPMode || + tun.core.config.IfMTU != tun.core.configOld.IfMTU + tun.core.configMutex.RUnlock() + if updated { + tun.core.log.Println("Reconfiguring TUN/TAP is not supported yet") + e <- nil + } else { + e <- nil } } }() From 53be1b02f3a8685bf56c1d7371fbf0822af954b7 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 15 Jan 2019 08:53:57 +0000 Subject: [PATCH 26/40] Check if accepting socket produced an error --- src/yggdrasil/tcp.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index 9cab9ea2..c90c3ffb 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -140,6 +140,10 @@ func (iface *tcpInterface) listener() { iface.core.log.Println("Listening for TCP on:", iface.serv.Addr().String()) for { sock, err := iface.serv.Accept() + if err != nil { + iface.core.log.Println("Failed to accept connection:", err) + return + } select { case <-iface.serv_stop: iface.core.log.Println("Stopping listener") From 21306532c1a6e49abe255cc6c809e6bfecf03c09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Christian=20Gr=C3=BCnhage?= Date: Tue, 15 Jan 2019 16:48:25 +0100 Subject: [PATCH 27/40] update ansible key generator It's probably easier to use without it having an ansible folder prefix, if people want to put it into a different dir then they should change their workdir. Apart from that, this fixes a bug where I defined yggdrasil_signing_public_key twice, but never the private key. --- contrib/ansible/genkeys.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/contrib/ansible/genkeys.go b/contrib/ansible/genkeys.go index 15785754..22418a0c 100644 --- a/contrib/ansible/genkeys.go +++ b/contrib/ansible/genkeys.go @@ -54,11 +54,11 @@ func main() { signatureKeys = bubbleUpTo(signatureKeys, 0) } - os.MkdirAll("ansible/host_vars", 0755) + os.MkdirAll("host_vars", 0755) for i := 1; i <= *numHosts; i++ { - os.MkdirAll(fmt.Sprintf("ansible/host_vars/%x", i), 0755) - file, err := os.Create(fmt.Sprintf("ansible/host_vars/%x/vars", i)) + os.MkdirAll(fmt.Sprintf("host_vars/%x", i), 0755) + file, err := os.Create(fmt.Sprintf("host_vars/%x/vars", i)) if err != nil { return } @@ -66,10 +66,10 @@ func main() { file.WriteString(fmt.Sprintf("yggdrasil_encryption_public_key: %v\n", hex.EncodeToString(encryptionKeys[i].pub))) file.WriteString("yggdrasil_encryption_private_key: \"{{ vault_yggdrasil_encryption_private_key }}\"\n") file.WriteString(fmt.Sprintf("yggdrasil_signing_public_key: %v\n", hex.EncodeToString(signatureKeys[i].pub))) - file.WriteString("yggdrasil_signing_public_key: \"{{ vault_yggdrasil_signing_private_key }}\"\n") + file.WriteString("yggdrasil_signing_private_key: \"{{ vault_yggdrasil_signing_private_key }}\"\n") file.WriteString(fmt.Sprintf("ansible_host: %v\n", encryptionKeys[i].ip)) - file, err = os.Create(fmt.Sprintf("ansible/host_vars/%x/vault", i)) + file, err = os.Create(fmt.Sprintf("host_vars/%x/vault", i)) if err != nil { return } From 4fba5586380b59ba063fd5adaded1dec7c6f9cbf Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 16 Jan 2019 13:20:12 +0000 Subject: [PATCH 28/40] Fix concurrent map write in tcp.go --- src/yggdrasil/tcp.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index c90c3ffb..1ebf0b69 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -125,8 +125,10 @@ func (iface *tcpInterface) listen() error { } iface.serv, err = lc.Listen(ctx, "tcp", iface.tcp_addr) if err == nil { + iface.mutex.Lock() iface.calls = make(map[string]struct{}) iface.conns = make(map[tcpInfo](chan struct{})) + iface.mutex.Unlock() go iface.listener() return nil } @@ -187,7 +189,9 @@ func (iface *tcpInterface) call(saddr string, socksaddr *string, sintf string) { if iface.isAlreadyCalling(saddr) { return } + iface.mutex.Lock() iface.calls[callname] = struct{}{} + iface.mutex.Unlock() defer func() { // Block new calls for a little while, to mitigate livelock scenarios time.Sleep(default_tcp_timeout) From c85dbaea958bd2be603dcbb1012c1bb3845664a5 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 16 Jan 2019 13:23:26 +0000 Subject: [PATCH 29/40] Fix missing nodeinfo.init --- src/yggdrasil/router.go | 1 + 1 file changed, 1 insertion(+) diff --git a/src/yggdrasil/router.go b/src/yggdrasil/router.go index 11509d44..31eefbb2 100644 --- a/src/yggdrasil/router.go +++ b/src/yggdrasil/router.go @@ -86,6 +86,7 @@ func (r *router) init(core *Core) { r.send = send r.reset = make(chan struct{}, 1) r.admin = make(chan func(), 32) + r.nodeinfo.init(r.core) r.core.configMutex.RLock() r.nodeinfo.setNodeInfo(r.core.config.NodeInfo, r.core.config.NodeInfoPrivacy) r.core.configMutex.RUnlock() From 8baf593b62454b612c145179560ed1d312637e3e Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 16 Jan 2019 14:52:27 +0000 Subject: [PATCH 30/40] Update source address selection when sintf specified --- src/yggdrasil/tcp.go | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index 1ebf0b69..ca9ea09b 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -230,35 +230,38 @@ func (iface *tcpInterface) call(saddr string, socksaddr *string, sintf string) { ief, err := net.InterfaceByName(sintf) if err != nil { return - } else { - if ief.Flags&net.FlagUp == 0 { + } + if ief.Flags&net.FlagUp == 0 { + return + } + addrs, err := ief.Addrs() + if err == nil { + dst, err := net.ResolveTCPAddr("tcp", saddr) + if err != nil { return } - addrs, err := ief.Addrs() - if err == nil { - dst, err := net.ResolveTCPAddr("tcp", saddr) + for addrindex, addr := range addrs { + src, _, err := net.ParseCIDR(addr.String()) if err != nil { - return + continue } - for _, addr := range addrs { - src, _, err := net.ParseCIDR(addr.String()) - if err != nil { - continue - } - if (src.To4() != nil) == (dst.IP.To4() != nil) && src.IsGlobalUnicast() { + if (src.To4() != nil) == (dst.IP.To4() != nil) { + if addrindex == len(addrs)-1 || src.IsGlobalUnicast() { dialer.LocalAddr = &net.TCPAddr{ IP: src, Port: 0, + Zone: sintf, } break } } - if dialer.LocalAddr == nil { - return - } + } + if dialer.LocalAddr == nil { + return } } } + conn, err = dialer.Dial("tcp", saddr) if err != nil { return From fdf300a1ffab2139a19ec13610fff40a669f9958 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 16 Jan 2019 20:26:39 +0000 Subject: [PATCH 31/40] Handle AllowedEncryptionPublicKeys internally --- cmd/yggdrasil/main.go | 5 ---- src/yggdrasil/admin.go | 25 ++++------------- src/yggdrasil/peer.go | 63 +++++++++++++++++++++++------------------- 3 files changed, 40 insertions(+), 53 deletions(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index c3add0ce..f11bbc0d 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -227,11 +227,6 @@ func main() { logger.Println("An error occurred during startup") panic(err) } - // Check to see if any allowed encryption keys were provided in the config. - // If they were then set them now. - for _, pBoxStr := range cfg.AllowedEncryptionPublicKeys { - n.core.AddAllowedEncryptionPublicKey(pBoxStr) - } // The Stop function ensures that the TUN/TAP adapter is correctly shut down // before the program exits. defer func() { diff --git a/src/yggdrasil/admin.go b/src/yggdrasil/admin.go index 5524fe25..f601776a 100644 --- a/src/yggdrasil/admin.go +++ b/src/yggdrasil/admin.go @@ -765,35 +765,20 @@ func (a *admin) getData_getSessions() []admin_nodeInfo { // getAllowedEncryptionPublicKeys returns the public keys permitted for incoming peer connections. func (a *admin) getAllowedEncryptionPublicKeys() []string { - pubs := a.core.peers.getAllowedEncryptionPublicKeys() - var out []string - for _, pub := range pubs { - out = append(out, hex.EncodeToString(pub[:])) - } - return out + return a.core.peers.getAllowedEncryptionPublicKeys() } // addAllowedEncryptionPublicKey whitelists a key for incoming peer connections. func (a *admin) addAllowedEncryptionPublicKey(bstr string) (err error) { - boxBytes, err := hex.DecodeString(bstr) - if err == nil { - var box crypto.BoxPubKey - copy(box[:], boxBytes) - a.core.peers.addAllowedEncryptionPublicKey(&box) - } - return + a.core.peers.addAllowedEncryptionPublicKey(bstr) + return nil } // removeAllowedEncryptionPublicKey removes a key from the whitelist for incoming peer connections. // If none are set, an empty list permits all incoming connections. func (a *admin) removeAllowedEncryptionPublicKey(bstr string) (err error) { - boxBytes, err := hex.DecodeString(bstr) - if err == nil { - var box crypto.BoxPubKey - copy(box[:], boxBytes) - a.core.peers.removeAllowedEncryptionPublicKey(&box) - } - return + a.core.peers.removeAllowedEncryptionPublicKey(bstr) + return nil } // Send a DHT ping to the node with the provided key and coords, optionally looking up the specified target NodeID. diff --git a/src/yggdrasil/peer.go b/src/yggdrasil/peer.go index c83504fc..2cd1afe8 100644 --- a/src/yggdrasil/peer.go +++ b/src/yggdrasil/peer.go @@ -5,6 +5,7 @@ package yggdrasil // Live code should be better commented import ( + "encoding/hex" "sync" "sync/atomic" "time" @@ -18,12 +19,10 @@ import ( // In most cases, this involves passing the packet to the handler for outgoing traffic to another peer. // In other cases, it's link protocol traffic used to build the spanning tree, in which case this checks signatures and passes the message along to the switch. type peers struct { - core *Core - reconfigure chan chan error - mutex sync.Mutex // Synchronize writes to atomic - ports atomic.Value //map[switchPort]*peer, use CoW semantics - authMutex sync.RWMutex - allowedEncryptionPublicKeys map[crypto.BoxPubKey]struct{} + core *Core + reconfigure chan chan error + mutex sync.Mutex // Synchronize writes to atomic + ports atomic.Value //map[switchPort]*peer, use CoW semantics } // Initializes the peers struct. @@ -39,40 +38,48 @@ func (ps *peers) init(c *Core) { e <- nil } }() - ps.allowedEncryptionPublicKeys = make(map[crypto.BoxPubKey]struct{}) } -// Returns true if an incoming peer connection to a key is allowed, either because the key is in the whitelist or because the whitelist is empty. +// Returns true if an incoming peer connection to a key is allowed, either +// because the key is in the whitelist or because the whitelist is empty. func (ps *peers) isAllowedEncryptionPublicKey(box *crypto.BoxPubKey) bool { - ps.authMutex.RLock() - defer ps.authMutex.RUnlock() - _, isIn := ps.allowedEncryptionPublicKeys[*box] - return isIn || len(ps.allowedEncryptionPublicKeys) == 0 + boxstr := hex.EncodeToString(box[:]) + ps.core.configMutex.RLock() + defer ps.core.configMutex.RUnlock() + for _, v := range ps.core.config.AllowedEncryptionPublicKeys { + if v == boxstr { + return true + } + } + return len(ps.core.config.AllowedEncryptionPublicKeys) == 0 } // Adds a key to the whitelist. -func (ps *peers) addAllowedEncryptionPublicKey(box *crypto.BoxPubKey) { - ps.authMutex.Lock() - defer ps.authMutex.Unlock() - ps.allowedEncryptionPublicKeys[*box] = struct{}{} +func (ps *peers) addAllowedEncryptionPublicKey(box string) { + ps.core.configMutex.RLock() + defer ps.core.configMutex.RUnlock() + ps.core.config.AllowedEncryptionPublicKeys = + append(ps.core.config.AllowedEncryptionPublicKeys, box) } // Removes a key from the whitelist. -func (ps *peers) removeAllowedEncryptionPublicKey(box *crypto.BoxPubKey) { - ps.authMutex.Lock() - defer ps.authMutex.Unlock() - delete(ps.allowedEncryptionPublicKeys, *box) +func (ps *peers) removeAllowedEncryptionPublicKey(box string) { + ps.core.configMutex.RLock() + defer ps.core.configMutex.RUnlock() + for k, v := range ps.core.config.AllowedEncryptionPublicKeys { + if v == box { + ps.core.config.AllowedEncryptionPublicKeys = + append(ps.core.config.AllowedEncryptionPublicKeys[:k], + ps.core.config.AllowedEncryptionPublicKeys[k+1:]...) + } + } } // Gets the whitelist of allowed keys for incoming connections. -func (ps *peers) getAllowedEncryptionPublicKeys() []crypto.BoxPubKey { - ps.authMutex.RLock() - defer ps.authMutex.RUnlock() - keys := make([]crypto.BoxPubKey, 0, len(ps.allowedEncryptionPublicKeys)) - for key := range ps.allowedEncryptionPublicKeys { - keys = append(keys, key) - } - return keys +func (ps *peers) getAllowedEncryptionPublicKeys() []string { + ps.core.configMutex.RLock() + defer ps.core.configMutex.RUnlock() + return ps.core.config.AllowedEncryptionPublicKeys } // Atomically gets a map[switchPort]*peer of known peers. From 9d5085492ee045c658ee7d9eb3777b17828a6826 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 16 Jan 2019 20:38:51 +0000 Subject: [PATCH 32/40] Handle session firewall using central config --- src/yggdrasil/core.go | 9 ------ src/yggdrasil/session.go | 63 +++++++++------------------------------- 2 files changed, 14 insertions(+), 58 deletions(-) diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index 3382562e..ed075813 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -212,15 +212,6 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error { return err } - c.sessions.setSessionFirewallState(nc.SessionFirewall.Enable) - c.sessions.setSessionFirewallDefaults( - nc.SessionFirewall.AllowFromDirect, - nc.SessionFirewall.AllowFromRemote, - nc.SessionFirewall.AlwaysAllowOutbound, - ) - c.sessions.setSessionFirewallWhitelist(nc.SessionFirewall.WhitelistEncryptionPublicKeys) - c.sessions.setSessionFirewallBlacklist(nc.SessionFirewall.BlacklistEncryptionPublicKeys) - if err := c.router.start(); err != nil { c.log.Println("Failed to start router") return err diff --git a/src/yggdrasil/session.go b/src/yggdrasil/session.go index e29cd4fa..4f632fad 100644 --- a/src/yggdrasil/session.go +++ b/src/yggdrasil/session.go @@ -7,7 +7,6 @@ package yggdrasil import ( "bytes" "encoding/hex" - "sync" "time" "github.com/yggdrasil-network/yggdrasil-go/src/address" @@ -115,14 +114,6 @@ type sessions struct { byTheirPerm map[crypto.BoxPubKey]*crypto.Handle addrToPerm map[address.Address]*crypto.BoxPubKey subnetToPerm map[address.Subnet]*crypto.BoxPubKey - // Options from the session firewall - sessionFirewallMutex sync.RWMutex - sessionFirewallEnabled bool - sessionFirewallAllowsDirect bool - sessionFirewallAllowsRemote bool - sessionFirewallAlwaysAllowsOutbound bool - sessionFirewallWhitelist []string - sessionFirewallBlacklist []string } // Initializes the session struct. @@ -155,51 +146,28 @@ func (ss *sessions) init(core *Core) { ss.lastCleanup = time.Now() } -// Enable or disable the session firewall -func (ss *sessions) setSessionFirewallState(enabled bool) { - ss.sessionFirewallMutex.Lock() - defer ss.sessionFirewallMutex.Unlock() - ss.sessionFirewallEnabled = enabled -} +// Determines whether the session firewall is enabled. +func (ss *sessions) isSessionFirewallEnabled() bool { + ss.core.configMutex.RLock() + defer ss.core.configMutex.RUnlock() -// Set the session firewall defaults (first parameter is whether to allow -// sessions from direct peers, second is whether to allow from remote nodes). -func (ss *sessions) setSessionFirewallDefaults(allowsDirect bool, allowsRemote bool, alwaysAllowsOutbound bool) { - ss.sessionFirewallMutex.Lock() - defer ss.sessionFirewallMutex.Unlock() - ss.sessionFirewallAllowsDirect = allowsDirect - ss.sessionFirewallAllowsRemote = allowsRemote - ss.sessionFirewallAlwaysAllowsOutbound = alwaysAllowsOutbound -} - -// Set the session firewall whitelist - nodes always allowed to open sessions. -func (ss *sessions) setSessionFirewallWhitelist(whitelist []string) { - ss.sessionFirewallMutex.Lock() - defer ss.sessionFirewallMutex.Unlock() - ss.sessionFirewallWhitelist = whitelist -} - -// Set the session firewall blacklist - nodes never allowed to open sessions. -func (ss *sessions) setSessionFirewallBlacklist(blacklist []string) { - ss.sessionFirewallMutex.Lock() - defer ss.sessionFirewallMutex.Unlock() - ss.sessionFirewallBlacklist = blacklist + return ss.core.config.SessionFirewall.Enable } // Determines whether the session with a given publickey is allowed based on // session firewall rules. func (ss *sessions) isSessionAllowed(pubkey *crypto.BoxPubKey, initiator bool) bool { - ss.sessionFirewallMutex.RLock() - defer ss.sessionFirewallMutex.RUnlock() + ss.core.configMutex.RLock() + defer ss.core.configMutex.RUnlock() // Allow by default if the session firewall is disabled - if !ss.sessionFirewallEnabled { + if !ss.isSessionFirewallEnabled() { return true } // Prepare for checking whitelist/blacklist var box crypto.BoxPubKey // Reject blacklisted nodes - for _, b := range ss.sessionFirewallBlacklist { + for _, b := range ss.core.config.SessionFirewall.BlacklistEncryptionPublicKeys { key, err := hex.DecodeString(b) if err == nil { copy(box[:crypto.BoxPubKeyLen], key) @@ -209,7 +177,7 @@ func (ss *sessions) isSessionAllowed(pubkey *crypto.BoxPubKey, initiator bool) b } } // Allow whitelisted nodes - for _, b := range ss.sessionFirewallWhitelist { + for _, b := range ss.core.config.SessionFirewall.WhitelistEncryptionPublicKeys { key, err := hex.DecodeString(b) if err == nil { copy(box[:crypto.BoxPubKeyLen], key) @@ -219,7 +187,7 @@ func (ss *sessions) isSessionAllowed(pubkey *crypto.BoxPubKey, initiator bool) b } } // Allow outbound sessions if appropriate - if ss.sessionFirewallAlwaysAllowsOutbound { + if ss.core.config.SessionFirewall.AlwaysAllowOutbound { if initiator { return true } @@ -233,11 +201,11 @@ func (ss *sessions) isSessionAllowed(pubkey *crypto.BoxPubKey, initiator bool) b } } // Allow direct peers if appropriate - if ss.sessionFirewallAllowsDirect && isDirectPeer { + if ss.core.config.SessionFirewall.AllowFromDirect && isDirectPeer { return true } // Allow remote nodes if appropriate - if ss.sessionFirewallAllowsRemote && !isDirectPeer { + if ss.core.config.SessionFirewall.AllowFromRemote && !isDirectPeer { return true } // Finally, default-deny if not matching any of the above rules @@ -474,14 +442,11 @@ func (ss *sessions) handlePing(ping *sessionPing) { // Get the corresponding session (or create a new session) sinfo, isIn := ss.getByTheirPerm(&ping.SendPermPub) // Check the session firewall - ss.sessionFirewallMutex.RLock() - if !isIn && ss.sessionFirewallEnabled { + if !isIn && ss.isSessionFirewallEnabled() { if !ss.isSessionAllowed(&ping.SendPermPub, false) { - ss.sessionFirewallMutex.RUnlock() return } } - ss.sessionFirewallMutex.RUnlock() if !isIn || sinfo.timedout() { if isIn { sinfo.close() From c839012580cdf84a46121dc453a73811dd86d688 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 17 Jan 2019 23:06:59 +0000 Subject: [PATCH 33/40] Fix source address selection --- src/yggdrasil/tcp.go | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index ca9ea09b..78e39efc 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -245,15 +245,27 @@ func (iface *tcpInterface) call(saddr string, socksaddr *string, sintf string) { if err != nil { continue } - if (src.To4() != nil) == (dst.IP.To4() != nil) { - if addrindex == len(addrs)-1 || src.IsGlobalUnicast() { - dialer.LocalAddr = &net.TCPAddr{ - IP: src, - Port: 0, - Zone: sintf, - } - break + if src.Equal(dst.IP) { + continue + } + if !src.IsGlobalUnicast() && !src.IsLinkLocalUnicast() { + continue + } + bothglobal := src.IsGlobalUnicast() == dst.IP.IsGlobalUnicast() + bothlinklocal := src.IsLinkLocalUnicast() == dst.IP.IsLinkLocalUnicast() + if !bothglobal && !bothlinklocal { + continue + } + if (src.To4() != nil) != (dst.IP.To4() != nil) { + continue + } + if bothglobal || bothlinklocal || addrindex == len(addrs)-1 { + dialer.LocalAddr = &net.TCPAddr{ + IP: src, + Port: 0, + Zone: sintf, } + break } } if dialer.LocalAddr == nil { From cdfb930703e5b1b81ccba962b9110524698cf6fa Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 21 Jan 2019 12:27:29 +0000 Subject: [PATCH 34/40] Update switch flow separation for IPv4 --- src/yggdrasil/session.go | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/src/yggdrasil/session.go b/src/yggdrasil/session.go index 4f632fad..b756ec99 100644 --- a/src/yggdrasil/session.go +++ b/src/yggdrasil/session.go @@ -554,20 +554,33 @@ func (sinfo *sessionInfo) doSend(bs []byte) { } // code isn't multithreaded so appending to this is safe coords := sinfo.coords - // Read IPv6 flowlabel field (20 bits). - // Assumes packet at least contains IPv6 header. - flowkey := uint64(bs[1]&0x0f)<<16 | uint64(bs[2])<<8 | uint64(bs[3]) - // Check if the flowlabel was specified + var flowkey uint64 + // Try to read IPv6 flowlabel field (20 bits) + if bs[0]&0xf0 == 0x60 { + flowkey = uint64(bs[1]&0x0f)<<16 | uint64(bs[2])<<8 | uint64(bs[3]) + } + // Check if the flowlabel was specified. If not then try to use known + // protocols' ports: protokey: proto | sport | dport if flowkey == 0 { - // Does the packet meet the minimum UDP packet size? (others are bigger) - if len(bs) >= 48 { - // Is the protocol TCP, UDP, SCTP? - if bs[6] == 0x06 || bs[6] == 0x11 || bs[6] == 0x84 { - // if flowlabel was unspecified (0), try to use known protocols' ports - // protokey: proto | sport | dport - flowkey = uint64(bs[6])<<32 /* proto */ | - uint64(bs[40])<<24 | uint64(bs[41])<<16 /* sport */ | - uint64(bs[42])<<8 | uint64(bs[43]) /* dport */ + // Is the protocol TCP, UDP, SCTP? + switch bs[0] & 0xf0 { + case 0x40: // IPv4 packet + if len(bs) >= 24 { + if bs[9] == 0x06 || bs[9] == 0x11 || bs[9] == 0x84 { + ihl := bs[0] & 0x0f * 4 // Header length + flowkey = uint64(bs[9])<<32 /* proto */ | + uint64(bs[ihl+0])<<24 | uint64(bs[ihl+1])<<16 /* sport */ | + uint64(bs[ihl+2])<<8 | uint64(bs[ihl+3]) /* dport */ + } + } + case 0x60: // IPv6 packet + // Does the packet meet the minimum UDP packet size? (others are bigger) + if len(bs) >= 48 { + if bs[6] == 0x06 || bs[6] == 0x11 || bs[6] == 0x84 { + flowkey = uint64(bs[6])<<32 /* proto */ | + uint64(bs[40])<<24 | uint64(bs[41])<<16 /* sport */ | + uint64(bs[42])<<8 | uint64(bs[43]) /* dport */ + } } } } From d3f67ad0170cba1e8f4c9d0e668875864fdcb3a5 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 21 Jan 2019 16:22:49 +0000 Subject: [PATCH 35/40] Improve command flow --- src/yggdrasil/session.go | 48 +++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/src/yggdrasil/session.go b/src/yggdrasil/session.go index b756ec99..23b27a3e 100644 --- a/src/yggdrasil/session.go +++ b/src/yggdrasil/session.go @@ -554,33 +554,31 @@ func (sinfo *sessionInfo) doSend(bs []byte) { } // code isn't multithreaded so appending to this is safe coords := sinfo.coords + // Work out the flowkey - this is used to determine which switch queue + // traffic will be pushed to in the event of congestion var flowkey uint64 - // Try to read IPv6 flowlabel field (20 bits) - if bs[0]&0xf0 == 0x60 { - flowkey = uint64(bs[1]&0x0f)<<16 | uint64(bs[2])<<8 | uint64(bs[3]) - } - // Check if the flowlabel was specified. If not then try to use known - // protocols' ports: protokey: proto | sport | dport - if flowkey == 0 { - // Is the protocol TCP, UDP, SCTP? - switch bs[0] & 0xf0 { - case 0x40: // IPv4 packet - if len(bs) >= 24 { - if bs[9] == 0x06 || bs[9] == 0x11 || bs[9] == 0x84 { - ihl := bs[0] & 0x0f * 4 // Header length - flowkey = uint64(bs[9])<<32 /* proto */ | - uint64(bs[ihl+0])<<24 | uint64(bs[ihl+1])<<16 /* sport */ | - uint64(bs[ihl+2])<<8 | uint64(bs[ihl+3]) /* dport */ - } + switch bs[0] & 0xf0 { + case 0x40: // IPv4 packet + // Check the packet meets minimum UDP packet length + if len(bs) >= 24 { + if bs[9] == 0x06 || bs[9] == 0x11 || bs[9] == 0x84 { + ihl := bs[0] & 0x0f * 4 // Header length + flowkey = uint64(bs[9])<<32 /* proto */ | + uint64(bs[ihl+0])<<24 | uint64(bs[ihl+1])<<16 /* sport */ | + uint64(bs[ihl+2])<<8 | uint64(bs[ihl+3]) /* dport */ } - case 0x60: // IPv6 packet - // Does the packet meet the minimum UDP packet size? (others are bigger) - if len(bs) >= 48 { - if bs[6] == 0x06 || bs[6] == 0x11 || bs[6] == 0x84 { - flowkey = uint64(bs[6])<<32 /* proto */ | - uint64(bs[40])<<24 | uint64(bs[41])<<16 /* sport */ | - uint64(bs[42])<<8 | uint64(bs[43]) /* dport */ - } + } + case 0x60: // IPv6 packet + // Check if the flowlabel was specified in the packet header + flowkey = uint64(bs[1]&0x0f)<<16 | uint64(bs[2])<<8 | uint64(bs[3]) + // If the flowlabel isn't present, make protokey from proto | sport | dport + // if the packet meets minimum UDP packet length + if flowkey == 0 && len(bs) >= 48 { + // Is the protocol TCP, UDP or SCTP? + if bs[6] == 0x06 || bs[6] == 0x11 || bs[6] == 0x84 { + flowkey = uint64(bs[6])<<32 /* proto */ | + uint64(bs[40])<<24 | uint64(bs[41])<<16 /* sport */ | + uint64(bs[42])<<8 | uint64(bs[43]) /* dport */ } } } From 62d4d62a775ca2ff624f76d2e02ea46c1628dc2d Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 21 Jan 2019 16:24:29 +0000 Subject: [PATCH 36/40] Update comments --- src/yggdrasil/session.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/yggdrasil/session.go b/src/yggdrasil/session.go index 23b27a3e..6b7f62d2 100644 --- a/src/yggdrasil/session.go +++ b/src/yggdrasil/session.go @@ -557,10 +557,12 @@ func (sinfo *sessionInfo) doSend(bs []byte) { // Work out the flowkey - this is used to determine which switch queue // traffic will be pushed to in the event of congestion var flowkey uint64 + // Get the IP protocol version from the packet switch bs[0] & 0xf0 { case 0x40: // IPv4 packet // Check the packet meets minimum UDP packet length if len(bs) >= 24 { + // Is the protocol TCP, UDP or SCTP? if bs[9] == 0x06 || bs[9] == 0x11 || bs[9] == 0x84 { ihl := bs[0] & 0x0f * 4 // Header length flowkey = uint64(bs[9])<<32 /* proto */ | From 2baedc9bcc6fa4bd4863d84019ce652ef1037e6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Christian=20Gr=C3=BCnhage?= Date: Fri, 25 Jan 2019 21:28:22 +0100 Subject: [PATCH 37/40] make ansible key generator a tiny bit faster --- contrib/ansible/genkeys.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contrib/ansible/genkeys.go b/contrib/ansible/genkeys.go index 22418a0c..7df2e588 100644 --- a/contrib/ansible/genkeys.go +++ b/contrib/ansible/genkeys.go @@ -117,6 +117,8 @@ func bubbleUpTo(sets []keySet, num int) []keySet { var tmp = sets[i] sets[i] = sets[i + 1] sets[i + 1] = tmp + } else { + break } } return sets From bca69df1f6da415c97d15c5b18a770b404ffa190 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sat, 26 Jan 2019 14:07:18 -0600 Subject: [PATCH 38/40] possible workaround to a deadlock --- src/yggdrasil/session.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/yggdrasil/session.go b/src/yggdrasil/session.go index 6b7f62d2..cdabaf2b 100644 --- a/src/yggdrasil/session.go +++ b/src/yggdrasil/session.go @@ -625,5 +625,8 @@ func (sinfo *sessionInfo) doRecv(p *wire_trafficPacket) { sinfo.updateNonce(&p.Nonce) sinfo.time = time.Now() sinfo.bytesRecvd += uint64(len(bs)) - sinfo.core.router.toRecv <- router_recvPacket{bs, sinfo} + select { + case sinfo.core.router.toRecv <- router_recvPacket{bs, sinfo}: + default: // avoid deadlocks, maybe do this somewhere else?... + } } From 0838928668de232beb00c595149b28cc31ff5a52 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 27 Jan 2019 13:31:43 +0000 Subject: [PATCH 39/40] Add support for flexible logging levels --- cmd/yggdrasil/main.go | 25 ++++++++++++++++++++----- go.mod | 1 + go.sum | 2 ++ src/yggdrasil/admin.go | 12 ++++++------ src/yggdrasil/awdl.go | 12 ++++++------ src/yggdrasil/ckr.go | 10 +++++----- src/yggdrasil/core.go | 27 ++++++++++++++------------- src/yggdrasil/mobile.go | 2 -- src/yggdrasil/multicast.go | 6 +++--- src/yggdrasil/router.go | 2 +- src/yggdrasil/switch.go | 2 +- src/yggdrasil/tcp.go | 16 ++++++++-------- src/yggdrasil/tun.go | 6 +++--- src/yggdrasil/tun_bsd.go | 22 +++++++++++----------- src/yggdrasil/tun_darwin.go | 12 ++++++------ src/yggdrasil/tun_linux.go | 6 +++--- src/yggdrasil/tun_other.go | 2 +- src/yggdrasil/tun_windows.go | 28 ++++++++++++++-------------- 18 files changed, 105 insertions(+), 88 deletions(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index f11bbc0d..f67bbcef 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -6,7 +6,6 @@ import ( "flag" "fmt" "io/ioutil" - "log" "os" "os/signal" "strings" @@ -14,6 +13,7 @@ import ( "golang.org/x/text/encoding/unicode" + "github.com/gologme/log" "github.com/hjson/hjson-go" "github.com/kardianos/minwinsvc" "github.com/mitchellh/mapstructure" @@ -169,6 +169,7 @@ func main() { confjson := flag.Bool("json", false, "print configuration from -genconf or -normaliseconf as JSON instead of HJSON") autoconf := flag.Bool("autoconf", false, "automatic mode (dynamic IP, peer with IPv6 neighbors)") version := flag.Bool("version", false, "prints the version of this build") + logging := flag.String("logging", "info,warn,error", "comma-separated list of logging levels to enable") flag.Parse() var cfg *nodeConfig @@ -217,6 +218,20 @@ func main() { } // Create a new logger that logs output to stdout. logger := log.New(os.Stdout, "", log.Flags()) + //logger.EnableLevel("error") + //logger.EnableLevel("warn") + //logger.EnableLevel("info") + if levels := strings.Split(*logging, ","); len(levels) > 0 { + for _, level := range levels { + l := strings.TrimSpace(level) + switch l { + case "error", "warn", "info", "trace", "debug": + logger.EnableLevel(l) + default: + continue + } + } + } // Setup the Yggdrasil node itself. The node{} type includes a Core, so we // don't need to create this manually. n := node{} @@ -224,7 +239,7 @@ func main() { // Yggdrasil. This will start the router, switch, DHT node, TCP and UDP // sockets, TUN/TAP adapter and multicast discovery port. if err := n.core.Start(cfg, logger); err != nil { - logger.Println("An error occurred during startup") + logger.Errorln("An error occurred during startup") panic(err) } // The Stop function ensures that the TUN/TAP adapter is correctly shut down @@ -236,8 +251,8 @@ func main() { // This is just logged to stdout for the user. address := n.core.GetAddress() subnet := n.core.GetSubnet() - logger.Printf("Your IPv6 address is %s", address.String()) - logger.Printf("Your IPv6 subnet is %s", subnet.String()) + logger.Infof("Your IPv6 address is %s", address.String()) + logger.Infof("Your IPv6 subnet is %s", subnet.String()) // Catch interrupts from the operating system to exit gracefully. c := make(chan os.Signal, 1) r := make(chan os.Signal, 1) @@ -257,7 +272,7 @@ func main() { cfg = readConfig(useconf, useconffile, normaliseconf) n.core.UpdateConfig(cfg) } else { - logger.Println("Reloading config at runtime is only possible with -useconffile") + logger.Errorln("Reloading config at runtime is only possible with -useconffile") } case _ = <-c: goto exit diff --git a/go.mod b/go.mod index 53a5a2b9..3e8db512 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ require ( github.com/hjson/hjson-go v0.0.0-20181010104306-a25ecf6bd222 github.com/kardianos/minwinsvc v0.0.0-20151122163309-cad6b2b879b0 github.com/mitchellh/mapstructure v1.1.2 + github.com/gologme/log v0.0.0-20181207131047-4e5d8ccb38e8 github.com/songgao/packets v0.0.0-20160404182456-549a10cd4091 github.com/yggdrasil-network/water v0.0.0-20180615095340-f732c88f34ae golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 diff --git a/go.sum b/go.sum index 1695daff..17b1017a 100644 --- a/go.sum +++ b/go.sum @@ -18,3 +18,5 @@ golang.org/x/sys v0.0.0-20181206074257-70b957f3b65e h1:njOxP/wVblhCLIUhjHXf6X+dz golang.org/x/sys v0.0.0-20181206074257-70b957f3b65e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +github.com/gologme/log v0.0.0-20181207131047-4e5d8ccb38e8 h1:WD8iJ37bRNwvETMfVTusVSAi0WdXTpfNVGY2aHycNKY= +github.com/gologme/log v0.0.0-20181207131047-4e5d8ccb38e8/go.mod h1:gq31gQ8wEHkR+WekdWsqDuf8pXTUZA9BnnzTuPz1Y9U= diff --git a/src/yggdrasil/admin.go b/src/yggdrasil/admin.go index f601776a..b54ac5cf 100644 --- a/src/yggdrasil/admin.go +++ b/src/yggdrasil/admin.go @@ -398,7 +398,7 @@ func (a *admin) listen() { switch strings.ToLower(u.Scheme) { case "unix": if _, err := os.Stat(a.listenaddr[7:]); err == nil { - a.core.log.Println("WARNING:", a.listenaddr[7:], "already exists and may be in use by another process") + a.core.log.Warnln("WARNING:", a.listenaddr[7:], "already exists and may be in use by another process") } a.listener, err = net.Listen("unix", a.listenaddr[7:]) if err == nil { @@ -406,7 +406,7 @@ func (a *admin) listen() { case "@": // maybe abstract namespace default: if err := os.Chmod(a.listenaddr[7:], 0660); err != nil { - a.core.log.Println("WARNING:", a.listenaddr[:7], "may have unsafe permissions!") + a.core.log.Warnln("WARNING:", a.listenaddr[:7], "may have unsafe permissions!") } } } @@ -420,10 +420,10 @@ func (a *admin) listen() { a.listener, err = net.Listen("tcp", a.listenaddr) } if err != nil { - a.core.log.Printf("Admin socket failed to listen: %v", err) + a.core.log.Errorf("Admin socket failed to listen: %v", err) os.Exit(1) } - a.core.log.Printf("%s admin socket listening on %s", + a.core.log.Infof("%s admin socket listening on %s", strings.ToUpper(a.listener.Addr().Network()), a.listener.Addr().String()) defer a.listener.Close() @@ -450,9 +450,9 @@ func (a *admin) handleRequest(conn net.Conn) { "status": "error", "error": "Unrecoverable error, possibly as a result of invalid input types or malformed syntax", } - fmt.Println("Admin socket error:", r) + a.core.log.Errorln("Admin socket error:", r) if err := encoder.Encode(&send); err != nil { - fmt.Println("Admin socket JSON encode error:", err) + a.core.log.Errorln("Admin socket JSON encode error:", err) } conn.Close() } diff --git a/src/yggdrasil/awdl.go b/src/yggdrasil/awdl.go index 633d5f9c..190d0258 100644 --- a/src/yggdrasil/awdl.go +++ b/src/yggdrasil/awdl.go @@ -50,24 +50,24 @@ func (l *awdl) create(fromAWDL chan []byte, toAWDL chan []byte /*boxPubKey *cryp meta.sig = l.core.sigPub meta.link = *myLinkPub metaBytes := meta.encode() - l.core.log.Println("toAWDL <- metaBytes") + l.core.log.Traceln("toAWDL <- metaBytes") toAWDL <- metaBytes - l.core.log.Println("metaBytes = <-fromAWDL") + l.core.log.Traceln("metaBytes = <-fromAWDL") metaBytes = <-fromAWDL - l.core.log.Println("version_metadata{}") + l.core.log.Traceln("version_metadata{}") meta = version_metadata{} if !meta.decode(metaBytes) || !meta.check() { return nil, errors.New("Metadata decode failure") } - l.core.log.Println("version_getBaseMetadata{}") + l.core.log.Traceln("version_getBaseMetadata{}") base := version_getBaseMetadata() if meta.ver > base.ver || meta.ver == base.ver && meta.minorVer > base.minorVer { return nil, errors.New("Failed to connect to node: " + name + " version: " + fmt.Sprintf("%d.%d", meta.ver, meta.minorVer)) } - l.core.log.Println("crypto.GetSharedKey") + l.core.log.Traceln("crypto.GetSharedKey") shared := crypto.GetSharedKey(myLinkPriv, &meta.link) //shared := crypto.GetSharedKey(&l.core.boxPriv, boxPubKey) - l.core.log.Println("l.core.peers.newPeer") + l.core.log.Traceln("l.core.peers.newPeer") intf.peer = l.core.peers.newPeer(&meta.box, &meta.sig, shared, name) if intf.peer != nil { intf.peer.linkOut = make(chan []byte, 1) // protocol traffic diff --git a/src/yggdrasil/ckr.go b/src/yggdrasil/ckr.go index a5ed4552..03bc5718 100644 --- a/src/yggdrasil/ckr.go +++ b/src/yggdrasil/ckr.go @@ -48,7 +48,7 @@ func (c *cryptokey) init(core *Core) { }() if err := c.configure(); err != nil { - c.core.log.Println("CKR configuration failed:", err) + c.core.log.Errorln("CKR configuration failed:", err) } } @@ -192,7 +192,7 @@ func (c *cryptokey) addSourceSubnet(cidr string) error { // Add the source subnet *routingsources = append(*routingsources, *ipnet) - c.core.log.Println("Added CKR source subnet", cidr) + c.core.log.Infoln("Added CKR source subnet", cidr) return nil } @@ -264,7 +264,7 @@ func (c *cryptokey) addRoute(cidr string, dest string) error { delete(*routingcache, k) } - c.core.log.Println("Added CKR destination subnet", cidr) + c.core.log.Infoln("Added CKR destination subnet", cidr) return nil } } @@ -358,7 +358,7 @@ func (c *cryptokey) removeSourceSubnet(cidr string) error { for idx, subnet := range *routingsources { if subnet.String() == ipnet.String() { *routingsources = append((*routingsources)[:idx], (*routingsources)[idx+1:]...) - c.core.log.Println("Removed CKR source subnet", cidr) + c.core.log.Infoln("Removed CKR source subnet", cidr) return nil } } @@ -407,7 +407,7 @@ func (c *cryptokey) removeRoute(cidr string, dest string) error { for k := range *routingcache { delete(*routingcache, k) } - c.core.log.Printf("Removed CKR destination subnet %s via %s\n", cidr, dest) + c.core.log.Infoln("Removed CKR destination subnet %s via %s\n", cidr, dest) return nil } } diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index ed075813..c78bc1fb 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -3,11 +3,12 @@ package yggdrasil import ( "encoding/hex" "io/ioutil" - "log" "net" "sync" "time" + "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" @@ -178,13 +179,13 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error { c.log = log if name := GetBuildName(); name != "unknown" { - c.log.Println("Build name:", name) + c.log.Infoln("Build name:", name) } if version := GetBuildVersion(); version != "unknown" { - c.log.Println("Build version:", version) + c.log.Infoln("Build version:", version) } - c.log.Println("Starting up...") + c.log.Infoln("Starting up...") c.configMutex.Lock() c.config = *nc @@ -194,12 +195,12 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error { c.init() if err := c.tcp.init(c); err != nil { - c.log.Println("Failed to start TCP interface") + c.log.Errorln("Failed to start TCP interface") return err } if err := c.awdl.init(c); err != nil { - c.log.Println("Failed to start AWDL interface") + c.log.Errorln("Failed to start AWDL interface") return err } @@ -208,39 +209,39 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error { } if err := c.switchTable.start(); err != nil { - c.log.Println("Failed to start switch") + c.log.Errorln("Failed to start switch") return err } if err := c.router.start(); err != nil { - c.log.Println("Failed to start router") + c.log.Errorln("Failed to start router") return err } if err := c.admin.start(); err != nil { - c.log.Println("Failed to start admin socket") + c.log.Errorln("Failed to start admin socket") return err } if err := c.multicast.start(); err != nil { - c.log.Println("Failed to start multicast interface") + c.log.Errorln("Failed to start multicast interface") return err } if err := c.router.tun.start(); err != nil { - c.log.Println("Failed to start TUN/TAP") + c.log.Errorln("Failed to start TUN/TAP") return err } go c.addPeerLoop() - c.log.Println("Startup complete") + c.log.Infoln("Startup complete") return nil } // Stops the Yggdrasil node. func (c *Core) Stop() { - c.log.Println("Stopping...") + c.log.Infoln("Stopping...") c.router.tun.close() c.admin.close() } diff --git a/src/yggdrasil/mobile.go b/src/yggdrasil/mobile.go index 2ffeffb9..220e6ca7 100644 --- a/src/yggdrasil/mobile.go +++ b/src/yggdrasil/mobile.go @@ -77,9 +77,7 @@ func (c *Core) StartJSON(configjson []byte) error { return err } nc.IfName = "dummy" - //c.log.Println(nc.MulticastInterfaces) for _, ll := range nc.MulticastInterfaces { - //c.log.Println("Processing MC", ll) ifceExpr, err := regexp.Compile(ll) if err != nil { panic(err) diff --git a/src/yggdrasil/multicast.go b/src/yggdrasil/multicast.go index 08f0954e..42651deb 100644 --- a/src/yggdrasil/multicast.go +++ b/src/yggdrasil/multicast.go @@ -35,15 +35,15 @@ func (m *multicast) init(core *Core) { m.groupAddr = "[ff02::114]:9001" // Check if we've been given any expressions if count := len(m.interfaces()); count != 0 { - m.core.log.Println("Found", count, "multicast interface(s)") + m.core.log.Infoln("Found", count, "multicast interface(s)") } } func (m *multicast) start() error { if len(m.interfaces()) == 0 { - m.core.log.Println("Multicast discovery is disabled") + m.core.log.Infoln("Multicast discovery is disabled") } else { - m.core.log.Println("Multicast discovery is enabled") + m.core.log.Infoln("Multicast discovery is enabled") addr, err := net.ResolveUDPAddr("udp", m.groupAddr) if err != nil { return err diff --git a/src/yggdrasil/router.go b/src/yggdrasil/router.go index 31eefbb2..d5059369 100644 --- a/src/yggdrasil/router.go +++ b/src/yggdrasil/router.go @@ -96,7 +96,7 @@ func (r *router) init(core *Core) { // Starts the mainLoop goroutine. func (r *router) start() error { - r.core.log.Println("Starting router") + r.core.log.Infoln("Starting router") go r.mainLoop() return nil } diff --git a/src/yggdrasil/switch.go b/src/yggdrasil/switch.go index 2a4e7e46..f2adf3fb 100644 --- a/src/yggdrasil/switch.go +++ b/src/yggdrasil/switch.go @@ -563,7 +563,7 @@ func (t *switchTable) getTable() lookupTable { // Starts the switch worker func (t *switchTable) start() error { - t.core.log.Println("Starting switch") + t.core.log.Infoln("Starting switch") go t.doWorker() return nil } diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index 78e39efc..a83213d4 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -139,16 +139,16 @@ func (iface *tcpInterface) listen() error { // Runs the listener, which spawns off goroutines for incoming connections. func (iface *tcpInterface) listener() { defer iface.serv.Close() - iface.core.log.Println("Listening for TCP on:", iface.serv.Addr().String()) + iface.core.log.Infoln("Listening for TCP on:", iface.serv.Addr().String()) for { sock, err := iface.serv.Accept() if err != nil { - iface.core.log.Println("Failed to accept connection:", err) + iface.core.log.Errorln("Failed to accept connection:", err) return } select { case <-iface.serv_stop: - iface.core.log.Println("Stopping listener") + iface.core.log.Errorln("Stopping listener") return default: if err != nil { @@ -313,9 +313,9 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) { base := version_getBaseMetadata() if meta.meta == base.meta { if meta.ver > base.ver { - iface.core.log.Println("Failed to connect to node:", sock.RemoteAddr().String(), "version:", meta.ver) + iface.core.log.Errorln("Failed to connect to node:", sock.RemoteAddr().String(), "version:", meta.ver) } else if meta.ver == base.ver && meta.minorVer > base.minorVer { - iface.core.log.Println("Failed to connect to node:", sock.RemoteAddr().String(), "version:", fmt.Sprintf("%d.%d", meta.ver, meta.minorVer)) + iface.core.log.Errorln("Failed to connect to node:", sock.RemoteAddr().String(), "version:", fmt.Sprintf("%d.%d", meta.ver, meta.minorVer)) } } // TODO? Block forever to prevent future connection attempts? suppress future messages about the same node? @@ -444,12 +444,12 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) { themAddr := address.AddrForNodeID(themNodeID) themAddrString := net.IP(themAddr[:]).String() themString := fmt.Sprintf("%s@%s", themAddrString, them) - iface.core.log.Printf("Connected: %s, source: %s", themString, us) + iface.core.log.Infof("Connected: %s, source: %s", themString, us) err = iface.reader(sock, in) // In this goroutine, because of defers if err == nil { - iface.core.log.Printf("Disconnected: %s, source: %s", themString, us) + iface.core.log.Infof("Disconnected: %s, source: %s", themString, us) } else { - iface.core.log.Printf("Disconnected: %s, source: %s, error: %s", themString, us, err) + iface.core.log.Infof("Disconnected: %s, source: %s, error: %s", themString, us, err) } return } diff --git a/src/yggdrasil/tun.go b/src/yggdrasil/tun.go index c0a71396..465cbb1c 100644 --- a/src/yggdrasil/tun.go +++ b/src/yggdrasil/tun.go @@ -53,7 +53,7 @@ func (tun *tunAdapter) init(core *Core, send chan<- []byte, recv <-chan []byte) tun.core.config.IfMTU != tun.core.configOld.IfMTU tun.core.configMutex.RUnlock() if updated { - tun.core.log.Println("Reconfiguring TUN/TAP is not supported yet") + tun.core.log.Warnln("Reconfiguring TUN/TAP is not supported yet") e <- nil } else { e <- nil @@ -82,8 +82,8 @@ func (tun *tunAdapter) start() error { tun.mutex.Lock() tun.isOpen = true tun.mutex.Unlock() - go func() { tun.core.log.Println("WARNING: tun.read() exited with error:", tun.read()) }() - go func() { tun.core.log.Println("WARNING: tun.write() exited with error:", tun.write()) }() + go func() { tun.core.log.Errorln("WARNING: tun.read() exited with error:", tun.read()) }() + go func() { tun.core.log.Errorln("WARNING: tun.write() exited with error:", tun.write()) }() if iftapmode { go func() { for { diff --git a/src/yggdrasil/tun_bsd.go b/src/yggdrasil/tun_bsd.go index 620c79db..81e2c46c 100644 --- a/src/yggdrasil/tun_bsd.go +++ b/src/yggdrasil/tun_bsd.go @@ -114,9 +114,9 @@ func (tun *tunAdapter) setupAddress(addr string) error { } // Friendly output - tun.core.log.Printf("Interface name: %s", tun.iface.Name()) - tun.core.log.Printf("Interface IPv6: %s", addr) - tun.core.log.Printf("Interface MTU: %d", tun.mtu) + tun.core.log.Infof("Interface name: %s", tun.iface.Name()) + tun.core.log.Infof("Interface IPv6: %s", addr) + tun.core.log.Infof("Interface MTU: %d", tun.mtu) // Create the MTU request var ir in6_ifreq_mtu @@ -126,15 +126,15 @@ func (tun *tunAdapter) setupAddress(addr string) error { // Set the MTU if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(sfd), uintptr(syscall.SIOCSIFMTU), uintptr(unsafe.Pointer(&ir))); errno != 0 { err = errno - tun.core.log.Printf("Error in SIOCSIFMTU: %v", errno) + tun.core.log.Errorf("Error in SIOCSIFMTU: %v", errno) // Fall back to ifconfig to set the MTU cmd := exec.Command("ifconfig", tun.iface.Name(), "mtu", string(tun.mtu)) - tun.core.log.Printf("Using ifconfig as fallback: %v", strings.Join(cmd.Args, " ")) + tun.core.log.Warnf("Using ifconfig as fallback: %v", strings.Join(cmd.Args, " ")) output, err := cmd.CombinedOutput() if err != nil { - tun.core.log.Printf("SIOCSIFMTU fallback failed: %v.", err) - tun.core.log.Println(string(output)) + tun.core.log.Errorf("SIOCSIFMTU fallback failed: %v.", err) + tun.core.log.Traceln(string(output)) } } @@ -155,15 +155,15 @@ func (tun *tunAdapter) setupAddress(addr string) error { // Set the interface address if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(sfd), uintptr(SIOCSIFADDR_IN6), uintptr(unsafe.Pointer(&ar))); errno != 0 { err = errno - tun.core.log.Printf("Error in SIOCSIFADDR_IN6: %v", errno) + tun.core.log.Errorf("Error in SIOCSIFADDR_IN6: %v", errno) // Fall back to ifconfig to set the address cmd := exec.Command("ifconfig", tun.iface.Name(), "inet6", addr) - tun.core.log.Printf("Using ifconfig as fallback: %v", strings.Join(cmd.Args, " ")) + tun.core.log.Warnf("Using ifconfig as fallback: %v", strings.Join(cmd.Args, " ")) output, err := cmd.CombinedOutput() if err != nil { - tun.core.log.Printf("SIOCSIFADDR_IN6 fallback failed: %v.", err) - tun.core.log.Println(string(output)) + tun.core.log.Errorf("SIOCSIFADDR_IN6 fallback failed: %v.", err) + tun.core.log.Traceln(string(output)) } } diff --git a/src/yggdrasil/tun_darwin.go b/src/yggdrasil/tun_darwin.go index 828c01ea..7ec1b8b9 100644 --- a/src/yggdrasil/tun_darwin.go +++ b/src/yggdrasil/tun_darwin.go @@ -18,7 +18,7 @@ import ( // Configures the "utun" adapter with the correct IPv6 address and MTU. func (tun *tunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error { if iftapmode { - tun.core.log.Printf("TAP mode is not supported on this platform, defaulting to TUN") + tun.core.log.Warnln("TAP mode is not supported on this platform, defaulting to TUN") } config := water.Config{DeviceType: water.TUN} iface, err := water.New(config) @@ -98,19 +98,19 @@ func (tun *tunAdapter) setupAddress(addr string) error { copy(ir.ifr_name[:], tun.iface.Name()) ir.ifru_mtu = uint32(tun.mtu) - tun.core.log.Printf("Interface name: %s", ar.ifra_name) - tun.core.log.Printf("Interface IPv6: %s", addr) - tun.core.log.Printf("Interface MTU: %d", ir.ifru_mtu) + tun.core.log.Infof("Interface name: %s", ar.ifra_name) + tun.core.log.Infof("Interface IPv6: %s", addr) + tun.core.log.Infof("Interface MTU: %d", ir.ifru_mtu) if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(darwin_SIOCAIFADDR_IN6), uintptr(unsafe.Pointer(&ar))); errno != 0 { err = errno - tun.core.log.Printf("Error in darwin_SIOCAIFADDR_IN6: %v", errno) + tun.core.log.Errorf("Error in darwin_SIOCAIFADDR_IN6: %v", errno) return err } if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(unix.SIOCSIFMTU), uintptr(unsafe.Pointer(&ir))); errno != 0 { err = errno - tun.core.log.Printf("Error in SIOCSIFMTU: %v", errno) + tun.core.log.Errorf("Error in SIOCSIFMTU: %v", errno) return err } diff --git a/src/yggdrasil/tun_linux.go b/src/yggdrasil/tun_linux.go index 8ccdd30b..30ada235 100644 --- a/src/yggdrasil/tun_linux.go +++ b/src/yggdrasil/tun_linux.go @@ -40,9 +40,9 @@ func (tun *tunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int } } // Friendly output - tun.core.log.Printf("Interface name: %s", tun.iface.Name()) - tun.core.log.Printf("Interface IPv6: %s", addr) - tun.core.log.Printf("Interface MTU: %d", tun.mtu) + tun.core.log.Infof("Interface name: %s", tun.iface.Name()) + tun.core.log.Infof("Interface IPv6: %s", addr) + tun.core.log.Infof("Interface MTU: %d", tun.mtu) return tun.setupAddress(addr) } diff --git a/src/yggdrasil/tun_other.go b/src/yggdrasil/tun_other.go index 22058c11..07ec25fd 100644 --- a/src/yggdrasil/tun_other.go +++ b/src/yggdrasil/tun_other.go @@ -28,6 +28,6 @@ func (tun *tunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int // We don't know how to set the IPv6 address on an unknown platform, therefore // write about it to stdout and don't try to do anything further. func (tun *tunAdapter) setupAddress(addr string) error { - tun.core.log.Println("Platform not supported, you must set the address of", tun.iface.Name(), "to", addr) + tun.core.log.Warnln("Platform not supported, you must set the address of", tun.iface.Name(), "to", addr) return nil } diff --git a/src/yggdrasil/tun_windows.go b/src/yggdrasil/tun_windows.go index 150a9766..1c89a437 100644 --- a/src/yggdrasil/tun_windows.go +++ b/src/yggdrasil/tun_windows.go @@ -15,7 +15,7 @@ import ( // delegate the hard work to "netsh". func (tun *tunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error { if !iftapmode { - tun.core.log.Printf("TUN mode is not supported on this platform, defaulting to TAP") + tun.core.log.Warnln("TUN mode is not supported on this platform, defaulting to TAP") } config := water.Config{DeviceType: water.TAP} config.PlatformSpecificParams.ComponentID = "tap0901" @@ -34,16 +34,16 @@ func (tun *tunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int tun.core.log.Printf("netsh command: %v", strings.Join(cmd.Args, " ")) output, err := cmd.CombinedOutput() if err != nil { - tun.core.log.Printf("Windows netsh failed: %v.", err) - tun.core.log.Println(string(output)) + tun.core.log.Errorf("Windows netsh failed: %v.", err) + tun.core.log.Traceln(string(output)) return err } cmd = exec.Command("netsh", "interface", "set", "interface", iface.Name(), "admin=ENABLED") tun.core.log.Printf("netsh command: %v", strings.Join(cmd.Args, " ")) output, err = cmd.CombinedOutput() if err != nil { - tun.core.log.Printf("Windows netsh failed: %v.", err) - tun.core.log.Println(string(output)) + tun.core.log.Errorf("Windows netsh failed: %v.", err) + tun.core.log.Traceln(string(output)) return err } // Get a new iface @@ -58,9 +58,9 @@ func (tun *tunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int panic(err) } // Friendly output - tun.core.log.Printf("Interface name: %s", tun.iface.Name()) - tun.core.log.Printf("Interface IPv6: %s", addr) - tun.core.log.Printf("Interface MTU: %d", tun.mtu) + tun.core.log.Infof("Interface name: %s", tun.iface.Name()) + tun.core.log.Infof("Interface IPv6: %s", addr) + tun.core.log.Infof("Interface MTU: %d", tun.mtu) return tun.setupAddress(addr) } @@ -71,11 +71,11 @@ func (tun *tunAdapter) setupMTU(mtu int) error { fmt.Sprintf("interface=%s", tun.iface.Name()), fmt.Sprintf("mtu=%d", mtu), "store=active") - tun.core.log.Printf("netsh command: %v", strings.Join(cmd.Args, " ")) + tun.core.log.Debugln("netsh command: %v", strings.Join(cmd.Args, " ")) output, err := cmd.CombinedOutput() if err != nil { - tun.core.log.Printf("Windows netsh failed: %v.", err) - tun.core.log.Println(string(output)) + tun.core.log.Errorf("Windows netsh failed: %v.", err) + tun.core.log.Traceln(string(output)) return err } return nil @@ -88,11 +88,11 @@ func (tun *tunAdapter) setupAddress(addr string) error { fmt.Sprintf("interface=%s", tun.iface.Name()), fmt.Sprintf("addr=%s", addr), "store=active") - tun.core.log.Printf("netsh command: %v", strings.Join(cmd.Args, " ")) + tun.core.log.Debugln("netsh command: %v", strings.Join(cmd.Args, " ")) output, err := cmd.CombinedOutput() if err != nil { - tun.core.log.Printf("Windows netsh failed: %v.", err) - tun.core.log.Println(string(output)) + tun.core.log.Errorf("Windows netsh failed: %v.", err) + tun.core.log.Traceln(string(output)) return err } return nil From 22d2e0e4fe305a47f3cc35b68daae98f10659b43 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 27 Jan 2019 13:33:32 +0000 Subject: [PATCH 40/40] Fix debug builds --- src/yggdrasil/debug.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/yggdrasil/debug.go b/src/yggdrasil/debug.go index 7c6c7570..f7319e46 100644 --- a/src/yggdrasil/debug.go +++ b/src/yggdrasil/debug.go @@ -14,7 +14,6 @@ import _ "golang.org/x/net/ipv6" // TODO put this somewhere better import "fmt" import "net" -import "log" import "regexp" import "encoding/hex" @@ -23,6 +22,8 @@ import "net/http" import "runtime" import "os" +import "github.com/gologme/log" + import "github.com/yggdrasil-network/yggdrasil-go/src/address" import "github.com/yggdrasil-network/yggdrasil-go/src/config" import "github.com/yggdrasil-network/yggdrasil-go/src/crypto"