diff --git a/src/util/util.go b/src/util/util.go index df15ff2c..d669fa5e 100644 --- a/src/util/util.go +++ b/src/util/util.go @@ -76,3 +76,20 @@ func FuncTimeout(f func(), timeout time.Duration) bool { return false } } + +// This calculates the difference between two arrays and returns items +// that appear in A but not in B - useful somewhat when reconfiguring +// and working out what configuration items changed +func Difference(a, b []string) []string { + ab := []string{} + mb := map[string]bool{} + for _, x := range b { + mb[x] = true + } + for _, x := range a { + if _, ok := mb[x]; !ok { + ab = append(ab, x) + } + } + return ab +} diff --git a/src/yggdrasil/awdl.go b/src/yggdrasil/awdl.go index 42366888..fe64e8bd 100644 --- a/src/yggdrasil/awdl.go +++ b/src/yggdrasil/awdl.go @@ -7,9 +7,10 @@ import ( ) type awdl struct { - link *link - mutex sync.RWMutex // protects interfaces below - interfaces map[string]*awdlInterface + link *link + reconfigure chan chan error + mutex sync.RWMutex // protects interfaces below + interfaces map[string]*awdlInterface } type awdlInterface struct { @@ -49,8 +50,16 @@ func (a *awdl) init(l *link) error { a.link = l a.mutex.Lock() a.interfaces = make(map[string]*awdlInterface) + a.reconfigure = make(chan chan error, 1) a.mutex.Unlock() + go func() { + for { + e := <-a.reconfigure + e <- nil + } + }() + return nil } diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index b2a85ec5..12ff14fc 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -143,7 +143,7 @@ func (c *Core) UpdateConfig(config *config.NodeConfig) { c.router.tun.reconfigure, c.router.cryptokey.reconfigure, c.switchTable.reconfigure, - // c.link.reconfigure, + c.link.reconfigure, c.multicast.reconfigure, } diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index 277f24c0..d040aac6 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -18,12 +18,13 @@ import ( ) type link struct { - core *Core - mutex sync.RWMutex // protects interfaces below - interfaces map[linkInfo]*linkInterface - handlers map[string]linkListener - awdl awdl // AWDL interface support - tcp tcp // TCP interface support + core *Core + reconfigure chan chan error + mutex sync.RWMutex // protects interfaces below + interfaces map[linkInfo]*linkInterface + handlers map[string]linkListener + awdl awdl // AWDL interface support + tcp tcp // TCP interface support // TODO timeout (to remove from switch), read from config.ReadTimeout } @@ -63,6 +64,7 @@ func (l *link) init(c *Core) error { l.core = c l.mutex.Lock() l.interfaces = make(map[linkInfo]*linkInterface) + l.reconfigure = make(chan chan error) l.mutex.Unlock() if err := l.tcp.init(l); err != nil { @@ -75,6 +77,23 @@ func (l *link) init(c *Core) error { return err } + go func() { + for { + e := <-l.reconfigure + tcpresponse := make(chan error) + awdlresponse := make(chan error) + l.tcp.reconfigure <- tcpresponse + l.awdl.reconfigure <- awdlresponse + if err := <-tcpresponse; err != nil { + e <- err + } + if err := <-awdlresponse; err != nil { + e <- err + } + e <- nil + } + }() + return nil } diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index 45b15f94..bacb346f 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -25,6 +25,7 @@ import ( "golang.org/x/net/proxy" "github.com/yggdrasil-network/yggdrasil-go/src/crypto" + "github.com/yggdrasil-network/yggdrasil-go/src/util" ) const default_timeout = 6 * time.Second @@ -32,13 +33,13 @@ const tcp_ping_interval = (default_timeout * 2 / 3) // The TCP listener and information about active TCP connections, to avoid duplication. type tcp struct { - link *link - reconfigure chan chan error - stop chan bool - mutex sync.Mutex // Protecting the below - listeners map[string]net.Listener - calls map[string]struct{} - conns map[tcpInfo](chan struct{}) + link *link + reconfigure chan chan error + mutex sync.Mutex // Protecting the below + listeners map[string]net.Listener + listenerstops map[string]chan bool + calls map[string]struct{} + conns map[tcpInfo](chan struct{}) } // This is used as the key to a map that tracks existing connections, to prevent multiple connections to the same keys and local/remote address pair from occuring. @@ -81,22 +82,38 @@ func (t *tcp) connectSOCKS(socksaddr, peeraddr string) { // Initializes the struct. func (t *tcp) init(l *link) error { t.link = l - t.stop = make(chan bool, 1) t.reconfigure = make(chan chan error, 1) go func() { for { e := <-t.reconfigure t.link.core.configMutex.RLock() - //updated := t.link.core.config.Listen != t.link.core.configOld.Listen - updated := false + added := util.Difference(t.link.core.config.Listen, t.link.core.configOld.Listen) + deleted := util.Difference(t.link.core.configOld.Listen, t.link.core.config.Listen) + updated := len(added) > 0 || len(deleted) > 0 t.link.core.configMutex.RUnlock() if updated { - /* t.stop <- true - for _, listener := range t.listeners { + for _, add := range added { + if add[:6] != "tcp://" { + continue + } + if err := t.listen(add[6:]); err != nil { + e <- err + continue + } + } + for _, delete := range deleted { + t.link.core.log.Warnln("Removing listener", delete, "not currently implemented") + /*t.mutex.Lock() + if listener, ok := t.listeners[delete]; ok { listener.Close() } - e <- t.listen() */ + if listener, ok := t.listenerstops[delete]; ok { + listener <- true + } + t.mutex.Unlock()*/ + } + e <- nil } else { e <- nil } @@ -107,6 +124,7 @@ func (t *tcp) init(l *link) error { t.calls = make(map[string]struct{}) t.conns = make(map[tcpInfo](chan struct{})) t.listeners = make(map[string]net.Listener) + t.listenerstops = make(map[string]chan bool) t.mutex.Unlock() t.link.core.configMutex.RLock() @@ -134,6 +152,7 @@ func (t *tcp) listen(listenaddr string) error { if err == nil { t.mutex.Lock() t.listeners[listenaddr] = listener + t.listenerstops[listenaddr] = make(chan bool, 1) t.mutex.Unlock() go t.listener(listenaddr) return nil @@ -149,17 +168,25 @@ func (t *tcp) listener(listenaddr string) { t.link.core.log.Errorln("Tried to start TCP listener for", listenaddr, "which doesn't exist") return } + reallistenaddr := listener.Addr().String() defer listener.Close() - t.link.core.log.Infoln("Listening for TCP on:", listener.Addr().String()) + t.link.core.log.Infoln("Listening for TCP on:", reallistenaddr) for { - sock, err := listener.Accept() - if err != nil { - t.link.core.log.Errorln("Failed to accept connection:", err) - return - } + var sock net.Conn + var err error + accepted := make(chan bool) + go func() { + sock, err = listener.Accept() + accepted <- true + }() select { - case <-t.stop: - t.link.core.log.Errorln("Stopping listener") + case <-accepted: + if err != nil { + t.link.core.log.Errorln("Failed to accept connection:", err) + return + } + case <-t.listenerstops[listenaddr]: + t.link.core.log.Errorln("Stopping TCP listener on:", reallistenaddr) return default: if err != nil {