Merge pull request #548 from neilalexander/bugfixes

Bugfixes
This commit is contained in:
Neil Alexander 2019-09-20 09:48:54 +01:00 committed by GitHub
commit 6432eaa9f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 202 additions and 157 deletions

View File

@ -292,11 +292,10 @@ exit:
} }
func (n *node) shutdown() { func (n *node) shutdown() {
n.core.Stop()
n.admin.Stop() n.admin.Stop()
n.multicast.Stop() n.multicast.Stop()
n.tuntap.Stop() n.tuntap.Stop()
os.Exit(0) n.core.Stop()
} }
func (n *node) sessionFirewall(pubkey *crypto.BoxPubKey, initiator bool) bool { func (n *node) sessionFirewall(pubkey *crypto.BoxPubKey, initiator bool) bool {

View File

@ -7,6 +7,7 @@ import (
"regexp" "regexp"
"time" "time"
"github.com/Arceliar/phony"
"github.com/gologme/log" "github.com/gologme/log"
"github.com/yggdrasil-network/yggdrasil-go/src/config" "github.com/yggdrasil-network/yggdrasil-go/src/config"
@ -19,14 +20,17 @@ import (
// configured multicast interface, Yggdrasil will attempt to peer with that node // configured multicast interface, Yggdrasil will attempt to peer with that node
// automatically. // automatically.
type Multicast struct { type Multicast struct {
core *yggdrasil.Core phony.Inbox
config *config.NodeState core *yggdrasil.Core
log *log.Logger config *config.NodeState
sock *ipv6.PacketConn log *log.Logger
groupAddr string sock *ipv6.PacketConn
listeners map[string]*yggdrasil.TcpListener groupAddr string
listenPort uint16 listeners map[string]*yggdrasil.TcpListener
isOpen bool listenPort uint16
isOpen bool
announcer *time.Timer
platformhandler *time.Timer
} }
// Init prepares the multicast interface for use. // Init prepares the multicast interface for use.
@ -63,9 +67,9 @@ func (m *Multicast) Start() error {
} }
m.isOpen = true m.isOpen = true
go m.multicastStarted()
go m.listen() go m.listen()
go m.announce() m.Act(m, m.multicastStarted)
m.Act(m, m.announce)
return nil return nil
} }
@ -73,6 +77,12 @@ func (m *Multicast) Start() error {
// Stop is not implemented for multicast yet. // Stop is not implemented for multicast yet.
func (m *Multicast) Stop() error { func (m *Multicast) Stop() error {
m.isOpen = false m.isOpen = false
if m.announcer != nil {
m.announcer.Stop()
}
if m.platformhandler != nil {
m.platformhandler.Stop()
}
m.sock.Close() m.sock.Close()
return nil return nil
} }
@ -136,108 +146,108 @@ func (m *Multicast) announce() {
if err != nil { if err != nil {
panic(err) panic(err)
} }
for { interfaces := m.Interfaces()
interfaces := m.Interfaces() // There might be interfaces that we configured listeners for but are no
// There might be interfaces that we configured listeners for but are no // longer up - if that's the case then we should stop the listeners
// longer up - if that's the case then we should stop the listeners for name, listener := range m.listeners {
for name, listener := range m.listeners { // Prepare our stop function!
// Prepare our stop function! stop := func() {
stop := func() { listener.Stop()
listener.Stop <- true delete(m.listeners, name)
delete(m.listeners, name) m.log.Debugln("No longer multicasting on", name)
m.log.Debugln("No longer multicasting on", name) }
} // If the interface is no longer visible on the system then stop the
// If the interface is no longer visible on the system then stop the // listener, as another one will be started further down
// listener, as another one will be started further down if _, ok := interfaces[name]; !ok {
if _, ok := interfaces[name]; !ok { stop()
stop() continue
continue }
} // It's possible that the link-local listener address has changed so if
// It's possible that the link-local listener address has changed so if // that is the case then we should clean up the interface listener
// that is the case then we should clean up the interface listener found := false
found := false listenaddr, err := net.ResolveTCPAddr("tcp6", listener.Listener.Addr().String())
listenaddr, err := net.ResolveTCPAddr("tcp6", listener.Listener.Addr().String()) if err != nil {
if err != nil { stop()
stop() continue
continue }
} // Find the interface that matches the listener
// Find the interface that matches the listener if intf, err := net.InterfaceByName(name); err == nil {
if intf, err := net.InterfaceByName(name); err == nil { if addrs, err := intf.Addrs(); err == nil {
if addrs, err := intf.Addrs(); err == nil { // Loop through the addresses attached to that listener and see if any
// Loop through the addresses attached to that listener and see if any // of them match the current address of the listener
// of them match the current address of the listener for _, addr := range addrs {
for _, addr := range addrs { if ip, _, err := net.ParseCIDR(addr.String()); err == nil {
if ip, _, err := net.ParseCIDR(addr.String()); err == nil { // Does the interface address match our listener address?
// Does the interface address match our listener address? if ip.Equal(listenaddr.IP) {
if ip.Equal(listenaddr.IP) { found = true
found = true break
break
}
} }
} }
} }
} }
// If the address has not been found on the adapter then we should stop
// and clean up the TCP listener. A new one will be created below if a
// suitable link-local address is found
if !found {
stop()
}
} }
// Now that we have a list of valid interfaces from the operating system, // If the address has not been found on the adapter then we should stop
// we can start checking if we can send multicasts on them // and clean up the TCP listener. A new one will be created below if a
for _, iface := range interfaces { // suitable link-local address is found
// Find interface addresses if !found {
addrs, err := iface.Addrs() stop()
if err != nil {
panic(err)
}
for _, addr := range addrs {
addrIP, _, _ := net.ParseCIDR(addr.String())
// Ignore IPv4 addresses
if addrIP.To4() != nil {
continue
}
// Ignore non-link-local addresses
if !addrIP.IsLinkLocalUnicast() {
continue
}
// Join the multicast group
m.sock.JoinGroup(&iface, groupAddr)
// Try and see if we already have a TCP listener for this interface
var listener *yggdrasil.TcpListener
if l, ok := m.listeners[iface.Name]; !ok || l.Listener == nil {
// No listener was found - let's create one
listenaddr := fmt.Sprintf("[%s%%%s]:%d", addrIP, iface.Name, m.listenPort)
if li, err := m.core.ListenTCP(listenaddr); err == nil {
m.log.Debugln("Started multicasting on", iface.Name)
// Store the listener so that we can stop it later if needed
m.listeners[iface.Name] = li
listener = li
} else {
m.log.Warnln("Not multicasting on", iface.Name, "due to error:", err)
}
} else {
// An existing listener was found
listener = m.listeners[iface.Name]
}
// Make sure nothing above failed for some reason
if listener == nil {
continue
}
// Get the listener details and construct the multicast beacon
lladdr := listener.Listener.Addr().String()
if a, err := net.ResolveTCPAddr("tcp6", lladdr); err == nil {
a.Zone = ""
destAddr.Zone = iface.Name
msg := []byte(a.String())
m.sock.WriteTo(msg, nil, destAddr)
}
break
}
} }
time.Sleep(time.Second * 15)
} }
// Now that we have a list of valid interfaces from the operating system,
// we can start checking if we can send multicasts on them
for _, iface := range interfaces {
// Find interface addresses
addrs, err := iface.Addrs()
if err != nil {
panic(err)
}
for _, addr := range addrs {
addrIP, _, _ := net.ParseCIDR(addr.String())
// Ignore IPv4 addresses
if addrIP.To4() != nil {
continue
}
// Ignore non-link-local addresses
if !addrIP.IsLinkLocalUnicast() {
continue
}
// Join the multicast group
m.sock.JoinGroup(&iface, groupAddr)
// Try and see if we already have a TCP listener for this interface
var listener *yggdrasil.TcpListener
if l, ok := m.listeners[iface.Name]; !ok || l.Listener == nil {
// No listener was found - let's create one
listenaddr := fmt.Sprintf("[%s%%%s]:%d", addrIP, iface.Name, m.listenPort)
if li, err := m.core.ListenTCP(listenaddr); err == nil {
m.log.Debugln("Started multicasting on", iface.Name)
// Store the listener so that we can stop it later if needed
m.listeners[iface.Name] = li
listener = li
} else {
m.log.Warnln("Not multicasting on", iface.Name, "due to error:", err)
}
} else {
// An existing listener was found
listener = m.listeners[iface.Name]
}
// Make sure nothing above failed for some reason
if listener == nil {
continue
}
// Get the listener details and construct the multicast beacon
lladdr := listener.Listener.Addr().String()
if a, err := net.ResolveTCPAddr("tcp6", lladdr); err == nil {
a.Zone = ""
destAddr.Zone = iface.Name
msg := []byte(a.String())
m.sock.WriteTo(msg, nil, destAddr)
}
break
}
}
m.announcer = time.AfterFunc(time.Second*15, func() {
m.Act(m, m.announce)
})
} }
func (m *Multicast) listen() { func (m *Multicast) listen() {

View File

@ -32,21 +32,16 @@ import (
var awdlGoroutineStarted bool var awdlGoroutineStarted bool
func (m *Multicast) multicastStarted() { func (m *Multicast) multicastStarted() {
if awdlGoroutineStarted { C.StopAWDLBrowsing()
return for intf := range m.Interfaces() {
} if intf == "awdl0" {
awdlGoroutineStarted = true C.StartAWDLBrowsing()
for { break
C.StopAWDLBrowsing()
for intf := range m.Interfaces() {
if intf == "awdl0" {
m.log.Infoln("Multicast discovery is using AWDL discovery")
C.StartAWDLBrowsing()
break
}
} }
time.Sleep(time.Minute)
} }
m.platformhandler = time.AfterFunc(time.Minute, func() {
m.Act(m, m.multicastStarted)
})
} }
func (m *Multicast) multicastReuse(network string, address string, c syscall.RawConn) error { func (m *Multicast) multicastReuse(network string, address string, c syscall.RawConn) error {

View File

@ -102,11 +102,12 @@ func (c *Conn) search() error {
if sinfo != nil { if sinfo != nil {
// Need to clean up to avoid a session leak // Need to clean up to avoid a session leak
sinfo.cancel.Cancel(nil) sinfo.cancel.Cancel(nil)
sinfo.sessions.removeSession(sinfo)
} }
default: default:
if sinfo != nil { if sinfo != nil {
// Finish initializing the session // Finish initializing the session
sinfo.conn = c sinfo.setConn(nil, c)
} }
c.session = sinfo c.session = sinfo
err = e err = e

View File

@ -96,7 +96,7 @@ func (c *Core) _addPeerLoop() {
if err := c.CallPeer(peer, intf); err != nil { if err := c.CallPeer(peer, intf); err != nil {
c.log.Errorln("Failed to add peer:", err) c.log.Errorln("Failed to add peer:", err)
} }
}(peer, "") }(peer, "") // TODO: this should be acted and not in a goroutine?
} }
// Add peers from the InterfacePeers section // Add peers from the InterfacePeers section
@ -106,13 +106,12 @@ func (c *Core) _addPeerLoop() {
if err := c.CallPeer(peer, intf); err != nil { if err := c.CallPeer(peer, intf); err != nil {
c.log.Errorln("Failed to add peer:", err) c.log.Errorln("Failed to add peer:", err)
} }
}(peer, intf) }(peer, intf) // TODO: this should be acted and not in a goroutine?
} }
} }
// Sit for a while
c.addPeerTimer = time.AfterFunc(time.Minute, func() { c.addPeerTimer = time.AfterFunc(time.Minute, func() {
c.Act(c, c._addPeerLoop) c.Act(nil, c._addPeerLoop)
}) })
} }
@ -192,5 +191,12 @@ func (c *Core) Stop() {
// This function is unsafe and should only be ran by the core actor. // This function is unsafe and should only be ran by the core actor.
func (c *Core) _stop() { func (c *Core) _stop() {
c.log.Infoln("Stopping...") c.log.Infoln("Stopping...")
c.addPeerTimer.Stop() if c.addPeerTimer != nil {
c.addPeerTimer.Stop()
}
c.link.stop()
for _, peer := range c.GetPeers() {
c.DisconnectPeer(peer.Port)
}
c.log.Infoln("Stopped")
} }

View File

@ -25,6 +25,7 @@ type link struct {
mutex sync.RWMutex // protects interfaces below mutex sync.RWMutex // protects interfaces below
interfaces map[linkInfo]*linkInterface interfaces map[linkInfo]*linkInterface
tcp tcp // TCP interface support tcp tcp // TCP interface support
stopped chan struct{}
// TODO timeout (to remove from switch), read from config.ReadTimeout // TODO timeout (to remove from switch), read from config.ReadTimeout
} }
@ -70,6 +71,7 @@ func (l *link) init(c *Core) error {
l.mutex.Lock() l.mutex.Lock()
l.interfaces = make(map[linkInfo]*linkInterface) l.interfaces = make(map[linkInfo]*linkInterface)
l.mutex.Unlock() l.mutex.Unlock()
l.stopped = make(chan struct{})
if err := l.tcp.init(l); err != nil { if err := l.tcp.init(l); err != nil {
c.log.Errorln("Failed to start TCP interface") c.log.Errorln("Failed to start TCP interface")
@ -86,7 +88,7 @@ func (l *link) reconfigure() {
func (l *link) call(uri string, sintf string) error { func (l *link) call(uri string, sintf string) error {
u, err := url.Parse(uri) u, err := url.Parse(uri)
if err != nil { if err != nil {
return fmt.Errorf("peer %s is not correctly formatted", uri) return fmt.Errorf("peer %s is not correctly formatted (%s)", uri, err)
} }
pathtokens := strings.Split(strings.Trim(u.Path, "/"), "/") pathtokens := strings.Split(strings.Trim(u.Path, "/"), "/")
switch u.Scheme { switch u.Scheme {
@ -134,6 +136,14 @@ func (l *link) create(msgIO linkInterfaceMsgIO, name, linkType, local, remote st
return &intf, nil return &intf, nil
} }
func (l *link) stop() error {
close(l.stopped)
if err := l.tcp.stop(); err != nil {
return err
}
return nil
}
func (intf *linkInterface) handler() error { func (intf *linkInterface) handler() error {
// TODO split some of this into shorter functions, so it's easier to read, and for the FIXME duplicate peer issue mentioned later // TODO split some of this into shorter functions, so it's easier to read, and for the FIXME duplicate peer issue mentioned later
myLinkPub, myLinkPriv := crypto.NewBoxKeys() myLinkPub, myLinkPriv := crypto.NewBoxKeys()
@ -224,7 +234,18 @@ func (intf *linkInterface) handler() error {
go intf.peer.start() go intf.peer.start()
intf.reader.Act(nil, intf.reader._read) intf.reader.Act(nil, intf.reader._read)
// Wait for the reader to finish // Wait for the reader to finish
// TODO find a way to do this without keeping live goroutines around
done := make(chan struct{})
defer close(done)
go func() {
select {
case <-intf.link.stopped:
intf.msgIO.close()
case <-done:
}
}()
err = <-intf.reader.err err = <-intf.reader.err
// TODO don't report an error if it's just a 'use of closed network connection'
if err != nil { if err != nil {
intf.link.core.log.Infof("Disconnected %s: %s, source %s; error: %s", intf.link.core.log.Infof("Disconnected %s: %s, source %s; error: %s",
strings.ToUpper(intf.info.linkType), themString, intf.info.local, err) strings.ToUpper(intf.info.linkType), themString, intf.info.local, err)

View File

@ -414,8 +414,10 @@ func (sinfo *sessionInfo) _updateNonce(theirNonce *crypto.BoxNonce) {
// Resets all sessions to an uninitialized state. // Resets all sessions to an uninitialized state.
// Called after coord changes, so attemtps to use a session will trigger a new ping and notify the remote end of the coord change. // Called after coord changes, so attemtps to use a session will trigger a new ping and notify the remote end of the coord change.
// Only call this from the router actor.
func (ss *sessions) reset() { func (ss *sessions) reset() {
for _, sinfo := range ss.sinfos { for _, _sinfo := range ss.sinfos {
sinfo := _sinfo // So we can safely put it in a closure
sinfo.Act(ss.router, func() { sinfo.Act(ss.router, func() {
sinfo.reset = true sinfo.reset = true
}) })
@ -470,7 +472,8 @@ func (sinfo *sessionInfo) _recvPacket(p *wire_trafficPacket) {
callback := func() { callback := func() {
util.PutBytes(p.Payload) util.PutBytes(p.Payload)
if !isOK || k != sinfo.sharedSesKey || !sinfo._nonceIsOK(&p.Nonce) { if !isOK || k != sinfo.sharedSesKey || !sinfo._nonceIsOK(&p.Nonce) {
// Either we failed to decrypt, or the session was updated, or we received this packet in the mean time // Either we failed to decrypt, or the session was updated, or we
// received this packet in the mean time
util.PutBytes(bs) util.PutBytes(bs)
return return
} }

View File

@ -34,6 +34,7 @@ const tcp_ping_interval = (default_timeout * 2 / 3)
// The TCP listener and information about active TCP connections, to avoid duplication. // The TCP listener and information about active TCP connections, to avoid duplication.
type tcp struct { type tcp struct {
link *link link *link
waitgroup sync.WaitGroup
mutex sync.Mutex // Protecting the below mutex sync.Mutex // Protecting the below
listeners map[string]*TcpListener listeners map[string]*TcpListener
calls map[string]struct{} calls map[string]struct{}
@ -46,7 +47,12 @@ type tcp struct {
// multicast interfaces. // multicast interfaces.
type TcpListener struct { type TcpListener struct {
Listener net.Listener Listener net.Listener
Stop chan bool stop chan struct{}
}
func (l *TcpListener) Stop() {
defer func() { recover() }()
close(l.stop)
} }
// Wrapper function to set additional options for specific connection types. // Wrapper function to set additional options for specific connection types.
@ -96,6 +102,16 @@ func (t *tcp) init(l *link) error {
return nil return nil
} }
func (t *tcp) stop() error {
t.mutex.Lock()
for _, listener := range t.listeners {
listener.Stop()
}
t.mutex.Unlock()
t.waitgroup.Wait()
return nil
}
func (t *tcp) reconfigure() { func (t *tcp) reconfigure() {
t.link.core.config.Mutex.RLock() t.link.core.config.Mutex.RLock()
added := util.Difference(t.link.core.config.Current.Listen, t.link.core.config.Previous.Listen) added := util.Difference(t.link.core.config.Current.Listen, t.link.core.config.Previous.Listen)
@ -121,7 +137,7 @@ func (t *tcp) reconfigure() {
t.mutex.Lock() t.mutex.Lock()
if listener, ok := t.listeners[d[6:]]; ok { if listener, ok := t.listeners[d[6:]]; ok {
t.mutex.Unlock() t.mutex.Unlock()
listener.Stop <- true listener.Stop()
t.link.core.log.Infoln("Stopped TCP listener:", d[6:]) t.link.core.log.Infoln("Stopped TCP listener:", d[6:])
} else { } else {
t.mutex.Unlock() t.mutex.Unlock()
@ -141,8 +157,9 @@ func (t *tcp) listen(listenaddr string) (*TcpListener, error) {
if err == nil { if err == nil {
l := TcpListener{ l := TcpListener{
Listener: listener, Listener: listener,
Stop: make(chan bool), stop: make(chan struct{}),
} }
t.waitgroup.Add(1)
go t.listener(&l, listenaddr) go t.listener(&l, listenaddr)
return &l, nil return &l, nil
} }
@ -152,6 +169,7 @@ func (t *tcp) listen(listenaddr string) (*TcpListener, error) {
// Runs the listener, which spawns off goroutines for incoming connections. // Runs the listener, which spawns off goroutines for incoming connections.
func (t *tcp) listener(l *TcpListener, listenaddr string) { func (t *tcp) listener(l *TcpListener, listenaddr string) {
defer t.waitgroup.Done()
if l == nil { if l == nil {
return return
} }
@ -166,7 +184,6 @@ func (t *tcp) listener(l *TcpListener, listenaddr string) {
t.mutex.Unlock() t.mutex.Unlock()
} }
// And here we go! // And here we go!
accepted := make(chan bool)
defer func() { defer func() {
t.link.core.log.Infoln("Stopping TCP listener on:", l.Listener.Addr().String()) t.link.core.log.Infoln("Stopping TCP listener on:", l.Listener.Addr().String())
l.Listener.Close() l.Listener.Close()
@ -175,36 +192,29 @@ func (t *tcp) listener(l *TcpListener, listenaddr string) {
t.mutex.Unlock() t.mutex.Unlock()
}() }()
t.link.core.log.Infoln("Listening for TCP on:", l.Listener.Addr().String()) t.link.core.log.Infoln("Listening for TCP on:", l.Listener.Addr().String())
go func() {
<-l.stop
l.Listener.Close()
}()
defer l.Stop()
for { for {
var sock net.Conn sock, err := l.Listener.Accept()
var err error if err != nil {
// Listen in a separate goroutine, as that way it does not block us from t.link.core.log.Errorln("Failed to accept connection:", err)
// receiving "stop" events
go func() {
sock, err = l.Listener.Accept()
accepted <- true
}()
// Wait for either an accepted connection, or a message telling us to stop
// the TCP listener
select {
case <-accepted:
if err != nil {
t.link.core.log.Errorln("Failed to accept connection:", err)
return
}
go t.handler(sock, true, nil)
case <-l.Stop:
return return
} }
t.waitgroup.Add(1)
go t.handler(sock, true, nil)
} }
} }
// Checks if we already are calling this address // Checks if we already are calling this address
func (t *tcp) isAlreadyCalling(saddr string) bool { func (t *tcp) startCalling(saddr string) bool {
t.mutex.Lock() t.mutex.Lock()
defer t.mutex.Unlock() defer t.mutex.Unlock()
_, isIn := t.calls[saddr] _, isIn := t.calls[saddr]
return isIn t.calls[saddr] = struct{}{}
return !isIn
} }
// Checks if a connection already exists. // Checks if a connection already exists.
@ -218,12 +228,9 @@ func (t *tcp) call(saddr string, options interface{}, sintf string) {
if sintf != "" { if sintf != "" {
callname = fmt.Sprintf("%s/%s", saddr, sintf) callname = fmt.Sprintf("%s/%s", saddr, sintf)
} }
if t.isAlreadyCalling(callname) { if !t.startCalling(callname) {
return return
} }
t.mutex.Lock()
t.calls[callname] = struct{}{}
t.mutex.Unlock()
defer func() { defer func() {
// Block new calls for a little while, to mitigate livelock scenarios // Block new calls for a little while, to mitigate livelock scenarios
time.Sleep(default_timeout) time.Sleep(default_timeout)
@ -252,6 +259,7 @@ func (t *tcp) call(saddr string, options interface{}, sintf string) {
if err != nil { if err != nil {
return return
} }
t.waitgroup.Add(1)
t.handler(conn, false, saddr) t.handler(conn, false, saddr)
} else { } else {
dst, err := net.ResolveTCPAddr("tcp", saddr) dst, err := net.ResolveTCPAddr("tcp", saddr)
@ -316,12 +324,14 @@ func (t *tcp) call(saddr string, options interface{}, sintf string) {
t.link.core.log.Debugln("Failed to dial TCP:", err) t.link.core.log.Debugln("Failed to dial TCP:", err)
return return
} }
t.waitgroup.Add(1)
t.handler(conn, false, nil) t.handler(conn, false, nil)
} }
}() }()
} }
func (t *tcp) handler(sock net.Conn, incoming bool, options interface{}) { func (t *tcp) handler(sock net.Conn, incoming bool, options interface{}) {
defer t.waitgroup.Done() // Happens after sock.close
defer sock.Close() defer sock.Close()
t.setExtraOptions(sock) t.setExtraOptions(sock)
stream := stream{} stream := stream{}