From 8f66d5b8ddc756ce7b888e826fba753b9e24c342 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 3 Mar 2019 14:09:54 +0000 Subject: [PATCH 01/62] Try to clean up UNIX admin socket --- src/yggdrasil/admin.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/yggdrasil/admin.go b/src/yggdrasil/admin.go index f8347f0d..6e941499 100644 --- a/src/yggdrasil/admin.go +++ b/src/yggdrasil/admin.go @@ -402,7 +402,18 @@ func (a *admin) listen() { switch strings.ToLower(u.Scheme) { case "unix": if _, err := os.Stat(a.listenaddr[7:]); err == nil { - a.core.log.Warnln("WARNING:", a.listenaddr[7:], "already exists and may be in use by another process") + a.core.log.Debugln("Admin socket", a.listenaddr[7:], "already exists, trying to clean up") + if _, err := net.Dial("unix", a.listenaddr[7:]); err == nil { + a.core.log.Errorln("Admin socket", a.listenaddr[7:], "is already in use by another process") + os.Exit(1) + } else { + if err := os.Remove(a.listenaddr[7:]); err == nil { + a.core.log.Debugln(a.listenaddr[7:], "was cleaned up") + } else { + a.core.log.Errorln(a.listenaddr[7:], "already exists and was not cleaned up:", err) + os.Exit(1) + } + } } a.listener, err = net.Listen("unix", a.listenaddr[7:]) if err == nil { From c940bae9e3587bea0519b5b10eaecc29006fc07b Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 3 Mar 2019 14:15:01 +0000 Subject: [PATCH 02/62] Update output --- src/yggdrasil/admin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yggdrasil/admin.go b/src/yggdrasil/admin.go index 6e941499..211f3c2d 100644 --- a/src/yggdrasil/admin.go +++ b/src/yggdrasil/admin.go @@ -404,7 +404,7 @@ func (a *admin) listen() { if _, err := os.Stat(a.listenaddr[7:]); err == nil { a.core.log.Debugln("Admin socket", a.listenaddr[7:], "already exists, trying to clean up") if _, err := net.Dial("unix", a.listenaddr[7:]); err == nil { - a.core.log.Errorln("Admin socket", a.listenaddr[7:], "is already in use by another process") + a.core.log.Errorln("Admin socket", a.listenaddr[7:], "already exists and is in use by another process") os.Exit(1) } else { if err := os.Remove(a.listenaddr[7:]); err == nil { From 918ce5a3fcf9f89e63685d1ffa7cf50e1889a41e Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 3 Mar 2019 19:32:36 +0000 Subject: [PATCH 03/62] Add a timeout on the UNIX admin socket check --- src/yggdrasil/admin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yggdrasil/admin.go b/src/yggdrasil/admin.go index 211f3c2d..62803395 100644 --- a/src/yggdrasil/admin.go +++ b/src/yggdrasil/admin.go @@ -403,7 +403,7 @@ func (a *admin) listen() { case "unix": if _, err := os.Stat(a.listenaddr[7:]); err == nil { a.core.log.Debugln("Admin socket", a.listenaddr[7:], "already exists, trying to clean up") - if _, err := net.Dial("unix", a.listenaddr[7:]); err == nil { + if _, err := net.DialTimeout("unix", a.listenaddr[7:], time.Second*2); err == nil || err.(net.Error).Timeout() { a.core.log.Errorln("Admin socket", a.listenaddr[7:], "already exists and is in use by another process") os.Exit(1) } else { From 7c435e6c1ba5d5cb104df4a356256507eb612ebd Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 4 Mar 2019 08:35:45 +0000 Subject: [PATCH 04/62] Fix macOS package --- .circleci/config.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 871d2faf..22557167 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -94,15 +94,13 @@ jobs: - run: name: Build for macOS command: | - rm -f {yggdrasil,yggdrasilctl} GO111MODULE=on GOOS=darwin GOARCH=amd64 ./build - mv yggdrasil /tmp/upload/$CINAME-$CIVERSION-darwin-amd64 - mv yggdrasilctl /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-darwin-amd64; + cp yggdrasil /tmp/upload/$CINAME-$CIVERSION-darwin-amd64 + cp yggdrasilctl /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-darwin-amd64; - run: name: Build for macOS (.pkg format) command: | - rm -rf {yggdrasil,yggdrasilctl} PKGARCH=amd64 sh contrib/macos/create-pkg.sh mv *.pkg /tmp/upload/ From ddd1ac460604162870bc30b2a83a409e2542f8b4 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 4 Mar 2019 08:44:25 +0000 Subject: [PATCH 05/62] Fix launchd file for macOS to use -useconffile --- contrib/macos/yggdrasil.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/macos/yggdrasil.plist b/contrib/macos/yggdrasil.plist index e5d30240..c436d4bd 100644 --- a/contrib/macos/yggdrasil.plist +++ b/contrib/macos/yggdrasil.plist @@ -8,7 +8,7 @@ sh -c - /usr/local/bin/yggdrasil -useconf < /etc/yggdrasil.conf + /usr/local/bin/yggdrasil -useconffile /etc/yggdrasil.conf KeepAlive From ae79246a66dadfdd3a84599731597cf9a1c3ed8e Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 4 Mar 2019 17:09:48 +0000 Subject: [PATCH 06/62] Move TCP under link.go --- src/yggdrasil/admin.go | 4 ++-- src/yggdrasil/core.go | 8 +------- src/yggdrasil/link.go | 9 ++++++++- src/yggdrasil/multicast.go | 6 +++--- src/yggdrasil/tcp.go | 32 ++++++++++++++++---------------- 5 files changed, 30 insertions(+), 29 deletions(-) diff --git a/src/yggdrasil/admin.go b/src/yggdrasil/admin.go index 62803395..d0d4cc90 100644 --- a/src/yggdrasil/admin.go +++ b/src/yggdrasil/admin.go @@ -577,9 +577,9 @@ func (a *admin) addPeer(addr string, sintf string) error { if err == nil { switch strings.ToLower(u.Scheme) { case "tcp": - a.core.tcp.connect(u.Host, sintf) + a.core.link.tcp.connect(u.Host, sintf) case "socks": - a.core.tcp.connectSOCKS(u.Host, u.Path[1:]) + a.core.link.tcp.connectSOCKS(u.Host, u.Path[1:]) default: return errors.New("invalid peer: " + addr) } diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index 04433282..b2a85ec5 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -44,7 +44,6 @@ type Core struct { admin admin searches searches multicast multicast - tcp tcpInterface link link log *log.Logger } @@ -144,7 +143,7 @@ func (c *Core) UpdateConfig(config *config.NodeConfig) { c.router.tun.reconfigure, c.router.cryptokey.reconfigure, c.switchTable.reconfigure, - c.tcp.reconfigure, + // c.link.reconfigure, c.multicast.reconfigure, } @@ -205,11 +204,6 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error { c.init() - if err := c.tcp.init(c); err != nil { - c.log.Errorln("Failed to start TCP interface") - return err - } - if err := c.link.init(c); err != nil { c.log.Errorln("Failed to start link interfaces") return err diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index 6fc7687b..8c03e086 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -8,6 +8,7 @@ import ( "net" "strings" "sync" + //"sync/atomic" "time" @@ -20,7 +21,8 @@ type link struct { core *Core mutex sync.RWMutex // protects interfaces below interfaces map[linkInfo]*linkInterface - awdl awdl // AWDL interface support + awdl awdl // AWDL interface support + tcp tcpInterface // TCP interface support // TODO timeout (to remove from switch), read from config.ReadTimeout } @@ -58,6 +60,11 @@ func (l *link) init(c *Core) error { l.interfaces = make(map[linkInfo]*linkInterface) l.mutex.Unlock() + if err := l.tcp.init(l); err != nil { + c.log.Errorln("Failed to start TCP interface") + return err + } + if err := l.awdl.init(l); err != nil { l.core.log.Errorln("Failed to start AWDL interface") return err diff --git a/src/yggdrasil/multicast.go b/src/yggdrasil/multicast.go index b20ee94d..f416ea26 100644 --- a/src/yggdrasil/multicast.go +++ b/src/yggdrasil/multicast.go @@ -27,7 +27,7 @@ func (m *multicast) init(core *Core) { for { e := <-m.reconfigure m.myAddrMutex.Lock() - m.myAddr = m.core.tcp.getAddr() + m.myAddr = m.core.link.tcp.getAddr() m.myAddrMutex.Unlock() e <- nil } @@ -109,7 +109,7 @@ func (m *multicast) interfaces() []net.Interface { func (m *multicast) announce() { var anAddr net.TCPAddr m.myAddrMutex.Lock() - m.myAddr = m.core.tcp.getAddr() + m.myAddr = m.core.link.tcp.getAddr() m.myAddrMutex.Unlock() groupAddr, err := net.ResolveUDPAddr("udp6", m.groupAddr) if err != nil { @@ -183,6 +183,6 @@ func (m *multicast) listen() { } addr.Zone = from.Zone saddr := addr.String() - m.core.tcp.connect(saddr, addr.Zone) + m.core.link.tcp.connect(saddr, addr.Zone) } } diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index c65e2e66..989480db 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -32,7 +32,7 @@ const tcp_ping_interval = (default_timeout * 2 / 3) // The TCP listener and information about active TCP connections, to avoid duplication. type tcpInterface struct { - core *Core + link *link reconfigure chan chan error serv net.Listener stop chan bool @@ -77,16 +77,16 @@ func (iface *tcpInterface) connectSOCKS(socksaddr, peeraddr string) { } // Initializes the struct. -func (iface *tcpInterface) init(core *Core) (err error) { - iface.core = core +func (iface *tcpInterface) init(l *link) (err error) { + iface.link = l iface.stop = make(chan bool, 1) iface.reconfigure = make(chan chan error, 1) go func() { for { e := <-iface.reconfigure - iface.core.configMutex.RLock() - updated := iface.core.config.Listen != iface.core.configOld.Listen - iface.core.configMutex.RUnlock() + iface.link.core.configMutex.RLock() + updated := iface.link.core.config.Listen != iface.link.core.configOld.Listen + iface.link.core.configMutex.RUnlock() if updated { iface.stop <- true iface.serv.Close() @@ -103,9 +103,9 @@ func (iface *tcpInterface) init(core *Core) (err error) { func (iface *tcpInterface) listen() error { var err error - iface.core.configMutex.RLock() - iface.addr = iface.core.config.Listen - iface.core.configMutex.RUnlock() + iface.link.core.configMutex.RLock() + iface.addr = iface.link.core.config.Listen + iface.link.core.configMutex.RUnlock() ctx := context.Background() lc := net.ListenConfig{ @@ -127,16 +127,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.Infoln("Listening for TCP on:", iface.serv.Addr().String()) + iface.link.core.log.Infoln("Listening for TCP on:", iface.serv.Addr().String()) for { sock, err := iface.serv.Accept() if err != nil { - iface.core.log.Errorln("Failed to accept connection:", err) + iface.link.core.log.Errorln("Failed to accept connection:", err) return } select { case <-iface.stop: - iface.core.log.Errorln("Stopping listener") + iface.link.core.log.Errorln("Stopping listener") return default: if err != nil { @@ -280,12 +280,12 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) { remote, _, _ := net.SplitHostPort(sock.RemoteAddr().String()) remotelinklocal := net.ParseIP(remote).IsLinkLocalUnicast() name := "tcp://" + sock.RemoteAddr().String() - link, err := iface.core.link.create(&stream, name, "tcp", local, remote, incoming, remotelinklocal) + link, err := iface.link.core.link.create(&stream, name, "tcp", local, remote, incoming, remotelinklocal) if err != nil { - iface.core.log.Println(err) + iface.link.core.log.Println(err) panic(err) } - iface.core.log.Debugln("DEBUG: starting handler for", name) + iface.link.core.log.Debugln("DEBUG: starting handler for", name) err = link.handler() - iface.core.log.Debugln("DEBUG: stopped handler for", name, err) + iface.link.core.log.Debugln("DEBUG: stopped handler for", name, err) } From be8db0c120b48c233e29318138cb62cda94a2296 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 4 Mar 2019 17:52:57 +0000 Subject: [PATCH 07/62] Support multiple TCP listeners --- cmd/yggdrasil/main.go | 10 ++- src/config/config.go | 6 +- src/yggdrasil/link.go | 9 ++- src/yggdrasil/tcp.go | 155 +++++++++++++++++++++--------------- src/yggdrasil/tcp_darwin.go | 2 +- src/yggdrasil/tcp_other.go | 2 +- 6 files changed, 110 insertions(+), 74 deletions(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index aa5a7494..e0c764e7 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -134,12 +134,20 @@ func readConfig(useconf *bool, useconffile *string, normaliseconf *bool) *nodeCo } } } + // Do a quick check for old-format Listen statement so that mapstructure + // doesn't fail and crash + if listen, ok := dat["Listen"].(string); ok { + if strings.HasPrefix(listen, "tcp://") { + dat["Listen"] = []string{listen} + } else { + dat["Listen"] = []string{"tcp://" + listen} + } + } // 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 } diff --git a/src/config/config.go b/src/config/config.go index 14b16490..807ce25b 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -12,7 +12,7 @@ import ( // NodeConfig defines all configuration values needed to run a signle yggdrasil node type NodeConfig struct { - Listen string `comment:"Listen address for peer connections. Default is to listen for all\nTCP connections over IPv4 and IPv6 with a random port."` + Listen []string `comment:"Listen address for peer connections. Default is to listen for all\nTCP connections over IPv4 and IPv6 with a random port."` AdminListen string `comment:"Listen address for admin connections. Default is to listen for local\nconnections either on TCP/9001 or a UNIX socket depending on your\nplatform. Use this value for yggdrasilctl -endpoint=X. To disable\nthe admin socket, use the value \"none\" instead."` Peers []string `comment:"List of connection strings for static peers in URI format, e.g.\ntcp://a.b.c.d:e or socks://a.b.c.d:e/f.g.h.i:j."` InterfacePeers map[string][]string `comment:"List of connection strings for static peers in URI format, arranged\nby source interface, e.g. { \"eth0\": [ tcp://a.b.c.d:e ] }. Note that\nSOCKS peerings will NOT be affected by this option and should go in\nthe \"Peers\" section instead."` @@ -79,10 +79,10 @@ func GenerateConfig(isAutoconf bool) *NodeConfig { // Create a node configuration and populate it. cfg := NodeConfig{} if isAutoconf { - cfg.Listen = "[::]:0" + cfg.Listen = []string{"tcp://[::]:0"} } else { r1 := rand.New(rand.NewSource(time.Now().UnixNano())) - cfg.Listen = fmt.Sprintf("[::]:%d", r1.Intn(65534-32768)+32768) + cfg.Listen = []string{fmt.Sprintf("[::]:%d", r1.Intn(65534-32768)+32768)} } cfg.AdminListen = defaults.GetDefaults().DefaultAdminListen cfg.EncryptionPublicKey = hex.EncodeToString(bpub[:]) diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index 8c03e086..277f24c0 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -21,8 +21,9 @@ type link struct { core *Core mutex sync.RWMutex // protects interfaces below interfaces map[linkInfo]*linkInterface - awdl awdl // AWDL interface support - tcp tcpInterface // TCP interface support + handlers map[string]linkListener + awdl awdl // AWDL interface support + tcp tcp // TCP interface support // TODO timeout (to remove from switch), read from config.ReadTimeout } @@ -34,6 +35,10 @@ type linkInfo struct { remote string // Remote name or address } +type linkListener interface { + init(*link) error +} + type linkInterfaceMsgIO interface { readMsg() ([]byte, error) writeMsg([]byte) (int, error) diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index 989480db..45b15f94 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -31,13 +31,12 @@ const default_timeout = 6 * time.Second const tcp_ping_interval = (default_timeout * 2 / 3) // The TCP listener and information about active TCP connections, to avoid duplication. -type tcpInterface struct { +type tcp struct { link *link reconfigure chan chan error - serv net.Listener stop chan bool - addr string mutex sync.Mutex // Protecting the below + listeners map[string]net.Listener calls map[string]struct{} conns map[tcpInfo](chan struct{}) } @@ -52,7 +51,7 @@ type tcpInfo struct { } // Wrapper function to set additional options for specific connection types. -func (iface *tcpInterface) setExtraOptions(c net.Conn) { +func (t *tcp) setExtraOptions(c net.Conn) { switch sock := c.(type) { case *net.TCPConn: sock.SetNoDelay(true) @@ -62,62 +61,81 @@ func (iface *tcpInterface) setExtraOptions(c net.Conn) { } // Returns the address of the listener. -func (iface *tcpInterface) getAddr() *net.TCPAddr { - return iface.serv.Addr().(*net.TCPAddr) +func (t *tcp) getAddr() *net.TCPAddr { + for _, listener := range t.listeners { + return listener.Addr().(*net.TCPAddr) + } + return nil } // Attempts to initiate a connection to the provided address. -func (iface *tcpInterface) connect(addr string, intf string) { - iface.call(addr, nil, intf) +func (t *tcp) connect(addr string, intf string) { + t.call(addr, nil, intf) } // Attempst to initiate a connection to the provided address, viathe provided socks proxy address. -func (iface *tcpInterface) connectSOCKS(socksaddr, peeraddr string) { - iface.call(peeraddr, &socksaddr, "") +func (t *tcp) connectSOCKS(socksaddr, peeraddr string) { + t.call(peeraddr, &socksaddr, "") } // Initializes the struct. -func (iface *tcpInterface) init(l *link) (err error) { - iface.link = l - iface.stop = make(chan bool, 1) - iface.reconfigure = make(chan chan error, 1) +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 := <-iface.reconfigure - iface.link.core.configMutex.RLock() - updated := iface.link.core.config.Listen != iface.link.core.configOld.Listen - iface.link.core.configMutex.RUnlock() + e := <-t.reconfigure + t.link.core.configMutex.RLock() + //updated := t.link.core.config.Listen != t.link.core.configOld.Listen + updated := false + t.link.core.configMutex.RUnlock() if updated { - iface.stop <- true - iface.serv.Close() - e <- iface.listen() + /* t.stop <- true + for _, listener := range t.listeners { + listener.Close() + } + e <- t.listen() */ } else { e <- nil } } }() - return iface.listen() + t.mutex.Lock() + t.calls = make(map[string]struct{}) + t.conns = make(map[tcpInfo](chan struct{})) + t.listeners = make(map[string]net.Listener) + t.mutex.Unlock() + + t.link.core.configMutex.RLock() + defer t.link.core.configMutex.RUnlock() + for _, listenaddr := range t.link.core.config.Listen { + if listenaddr[:6] != "tcp://" { + continue + } + if err := t.listen(listenaddr[6:]); err != nil { + return err + } + } + + return nil } -func (iface *tcpInterface) listen() error { +func (t *tcp) listen(listenaddr string) error { var err error - iface.link.core.configMutex.RLock() - iface.addr = iface.link.core.config.Listen - iface.link.core.configMutex.RUnlock() - ctx := context.Background() lc := net.ListenConfig{ - Control: iface.tcpContext, + Control: t.tcpContext, } - iface.serv, err = lc.Listen(ctx, "tcp", iface.addr) + listener, err := lc.Listen(ctx, "tcp", listenaddr) 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() + t.mutex.Lock() + t.listeners[listenaddr] = listener + t.mutex.Unlock() + go t.listener(listenaddr) return nil } @@ -125,41 +143,46 @@ func (iface *tcpInterface) listen() error { } // Runs the listener, which spawns off goroutines for incoming connections. -func (iface *tcpInterface) listener() { - defer iface.serv.Close() - iface.link.core.log.Infoln("Listening for TCP on:", iface.serv.Addr().String()) +func (t *tcp) listener(listenaddr string) { + listener, ok := t.listeners[listenaddr] + if !ok { + t.link.core.log.Errorln("Tried to start TCP listener for", listenaddr, "which doesn't exist") + return + } + defer listener.Close() + t.link.core.log.Infoln("Listening for TCP on:", listener.Addr().String()) for { - sock, err := iface.serv.Accept() + sock, err := listener.Accept() if err != nil { - iface.link.core.log.Errorln("Failed to accept connection:", err) + t.link.core.log.Errorln("Failed to accept connection:", err) return } select { - case <-iface.stop: - iface.link.core.log.Errorln("Stopping listener") + case <-t.stop: + t.link.core.log.Errorln("Stopping listener") return default: if err != nil { panic(err) } - go iface.handler(sock, true) + go t.handler(sock, true) } } } // 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] +func (t *tcp) isAlreadyConnected(info tcpInfo) bool { + t.mutex.Lock() + defer t.mutex.Unlock() + _, isIn := t.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] +func (t *tcp) isAlreadyCalling(saddr string) bool { + t.mutex.Lock() + defer t.mutex.Unlock() + _, isIn := t.calls[saddr] return isIn } @@ -168,25 +191,25 @@ func (iface *tcpInterface) isAlreadyCalling(saddr string) bool { // If the dial is successful, it launches the handler. // When finished, it removes the outgoing call, so reconnection attempts can be made later. // This all happens in a separate goroutine that it spawns. -func (iface *tcpInterface) call(saddr string, socksaddr *string, sintf string) { +func (t *tcp) call(saddr string, socksaddr *string, sintf string) { go func() { callname := saddr if sintf != "" { callname = fmt.Sprintf("%s/%s", saddr, sintf) } - if iface.isAlreadyCalling(callname) { + if t.isAlreadyCalling(callname) { return } - iface.mutex.Lock() - iface.calls[callname] = struct{}{} - iface.mutex.Unlock() + t.mutex.Lock() + t.calls[callname] = struct{}{} + t.mutex.Unlock() defer func() { // Block new calls for a little while, to mitigate livelock scenarios time.Sleep(default_timeout) time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond) - iface.mutex.Lock() - delete(iface.calls, callname) - iface.mutex.Unlock() + t.mutex.Lock() + delete(t.calls, callname) + t.mutex.Unlock() }() var conn net.Conn var err error @@ -212,7 +235,7 @@ func (iface *tcpInterface) call(saddr string, socksaddr *string, sintf string) { } } else { dialer := net.Dialer{ - Control: iface.tcpContext, + Control: t.tcpContext, } if sintf != "" { ief, err := net.InterfaceByName(sintf) @@ -267,25 +290,25 @@ func (iface *tcpInterface) call(saddr string, socksaddr *string, sintf string) { return } } - iface.handler(conn, false) + t.handler(conn, false) }() } -func (iface *tcpInterface) handler(sock net.Conn, incoming bool) { +func (t *tcp) handler(sock net.Conn, incoming bool) { defer sock.Close() - iface.setExtraOptions(sock) + t.setExtraOptions(sock) stream := stream{} stream.init(sock) local, _, _ := net.SplitHostPort(sock.LocalAddr().String()) remote, _, _ := net.SplitHostPort(sock.RemoteAddr().String()) remotelinklocal := net.ParseIP(remote).IsLinkLocalUnicast() name := "tcp://" + sock.RemoteAddr().String() - link, err := iface.link.core.link.create(&stream, name, "tcp", local, remote, incoming, remotelinklocal) + link, err := t.link.core.link.create(&stream, name, "tcp", local, remote, incoming, remotelinklocal) if err != nil { - iface.link.core.log.Println(err) + t.link.core.log.Println(err) panic(err) } - iface.link.core.log.Debugln("DEBUG: starting handler for", name) + t.link.core.log.Debugln("DEBUG: starting handler for", name) err = link.handler() - iface.link.core.log.Debugln("DEBUG: stopped handler for", name, err) + t.link.core.log.Debugln("DEBUG: stopped handler for", name, err) } diff --git a/src/yggdrasil/tcp_darwin.go b/src/yggdrasil/tcp_darwin.go index 6483ef86..9d55a1db 100644 --- a/src/yggdrasil/tcp_darwin.go +++ b/src/yggdrasil/tcp_darwin.go @@ -10,7 +10,7 @@ import ( // WARNING: This context is used both by net.Dialer and net.Listen in tcp.go -func (iface *tcpInterface) tcpContext(network, address string, c syscall.RawConn) error { +func (t *tcp) tcpContext(network, address string, c syscall.RawConn) error { var control error var recvanyif error diff --git a/src/yggdrasil/tcp_other.go b/src/yggdrasil/tcp_other.go index 5d62b530..47bd772c 100644 --- a/src/yggdrasil/tcp_other.go +++ b/src/yggdrasil/tcp_other.go @@ -8,6 +8,6 @@ import ( // WARNING: This context is used both by net.Dialer and net.Listen in tcp.go -func (iface *tcpInterface) tcpContext(network, address string, c syscall.RawConn) error { +func (t *tcp) tcpContext(network, address string, c syscall.RawConn) error { return nil } From 82bb95b77f2e64264f67c627df56812637705c4f Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 4 Mar 2019 18:41:32 +0000 Subject: [PATCH 08/62] Some more (inelegant) multiple listener code plus some reconfigure support --- src/util/util.go | 17 +++++++++++ src/yggdrasil/awdl.go | 15 ++++++++-- src/yggdrasil/core.go | 2 +- src/yggdrasil/link.go | 31 +++++++++++++++---- src/yggdrasil/tcp.go | 69 ++++++++++++++++++++++++++++++------------- 5 files changed, 103 insertions(+), 31 deletions(-) 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 { From eeede4e6d0c0d221a25f8e5816569f3400d89cfc Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 4 Mar 2019 18:47:40 +0000 Subject: [PATCH 09/62] Fix some obvious concurrency bugs --- src/yggdrasil/tcp.go | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index bacb346f..8dcd4c03 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -63,9 +63,14 @@ func (t *tcp) setExtraOptions(c net.Conn) { // Returns the address of the listener. func (t *tcp) getAddr() *net.TCPAddr { + // TODO: Fix this, because this will currently only give a single address + // to multicast.go, which obviously is not great, but right now multicast.go + // doesn't have the ability to send more than one address in a packet either + t.mutex.Lock() for _, listener := range t.listeners { return listener.Addr().(*net.TCPAddr) } + t.mutex.Unlock() return nil } @@ -83,6 +88,12 @@ func (t *tcp) connectSOCKS(socksaddr, peeraddr string) { func (t *tcp) init(l *link) error { t.link = l t.reconfigure = make(chan chan error, 1) + t.mutex.Lock() + 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() go func() { for { @@ -90,9 +101,8 @@ func (t *tcp) init(l *link) error { t.link.core.configMutex.RLock() 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 { + if len(added) > 0 || len(deleted) > 0 { for _, add := range added { if add[:6] != "tcp://" { continue @@ -120,13 +130,6 @@ func (t *tcp) init(l *link) error { } }() - t.mutex.Lock() - 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() defer t.link.core.configMutex.RUnlock() for _, listenaddr := range t.link.core.config.Listen { @@ -163,8 +166,11 @@ func (t *tcp) listen(listenaddr string) error { // Runs the listener, which spawns off goroutines for incoming connections. func (t *tcp) listener(listenaddr string) { + t.mutex.Lock() listener, ok := t.listeners[listenaddr] - if !ok { + listenerstop, ok2 := t.listenerstops[listenaddr] + t.mutex.Unlock() + if !ok || !ok2 { t.link.core.log.Errorln("Tried to start TCP listener for", listenaddr, "which doesn't exist") return } @@ -185,7 +191,7 @@ func (t *tcp) listener(listenaddr string) { t.link.core.log.Errorln("Failed to accept connection:", err) return } - case <-t.listenerstops[listenaddr]: + case <-listenerstop: t.link.core.log.Errorln("Stopping TCP listener on:", reallistenaddr) return default: From 0be0b078cbe08b0752b5eece2b2aeef5c919a79a Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 4 Mar 2019 19:00:06 +0000 Subject: [PATCH 10/62] Remove unused types in link.go --- src/yggdrasil/link.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index d040aac6..ac869389 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -22,7 +22,6 @@ type link struct { 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 @@ -36,10 +35,6 @@ type linkInfo struct { remote string // Remote name or address } -type linkListener interface { - init(*link) error -} - type linkInterfaceMsgIO interface { readMsg() ([]byte, error) writeMsg([]byte) (int, error) From 2b8648e2b346daef301cd2d968c1315d40617e38 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 4 Mar 2019 19:04:09 +0000 Subject: [PATCH 11/62] Fix debug builds --- src/yggdrasil/debug.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/yggdrasil/debug.go b/src/yggdrasil/debug.go index 94faba40..8405714a 100644 --- a/src/yggdrasil/debug.go +++ b/src/yggdrasil/debug.go @@ -449,19 +449,19 @@ func (c *Core) DEBUG_addSOCKSConn(socksaddr, peeraddr string) { //* func (c *Core) DEBUG_setupAndStartGlobalTCPInterface(addrport string) { - c.config.Listen = addrport - if err := c.tcp.init(c /*, addrport, 0*/); err != nil { - c.log.Println("Failed to start TCP interface:", err) + c.config.Listen = []string{addrport} + if err := c.link.init(c); err != nil { + c.log.Println("Failed to start interfaces:", err) panic(err) } } func (c *Core) DEBUG_getGlobalTCPAddr() *net.TCPAddr { - return c.tcp.serv.Addr().(*net.TCPAddr) + return c.link.tcp.getAddr() } func (c *Core) DEBUG_addTCPConn(saddr string) { - c.tcp.call(saddr, nil, "") + c.link.tcp.call(saddr, nil, "") } //*/ From 61774aed3bfe286422bdb4a92e5bd3e4f84848e0 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 4 Mar 2019 20:33:08 +0000 Subject: [PATCH 12/62] Show proto in admin socket, link linkInfo from peer, other fixes --- src/yggdrasil/admin.go | 6 ++++-- src/yggdrasil/debug.go | 10 +++++++++- src/yggdrasil/link.go | 2 +- src/yggdrasil/peer.go | 8 +++++--- src/yggdrasil/router.go | 10 +++++++++- src/yggdrasil/tcp.go | 34 ++++++---------------------------- 6 files changed, 34 insertions(+), 36 deletions(-) diff --git a/src/yggdrasil/admin.go b/src/yggdrasil/admin.go index d0d4cc90..ae5b02a2 100644 --- a/src/yggdrasil/admin.go +++ b/src/yggdrasil/admin.go @@ -666,7 +666,8 @@ func (a *admin) getData_getPeers() []admin_nodeInfo { {"uptime", int(time.Since(p.firstSeen).Seconds())}, {"bytes_sent", atomic.LoadUint64(&p.bytesSent)}, {"bytes_recvd", atomic.LoadUint64(&p.bytesRecvd)}, - {"endpoint", p.endpoint}, + {"proto", p.intf.info.linkType}, + {"endpoint", p.intf.info.remote}, {"box_pub_key", hex.EncodeToString(p.box[:])}, } peerInfos = append(peerInfos, info) @@ -692,7 +693,8 @@ func (a *admin) getData_getSwitchPeers() []admin_nodeInfo { {"port", elem.port}, {"bytes_sent", atomic.LoadUint64(&peer.bytesSent)}, {"bytes_recvd", atomic.LoadUint64(&peer.bytesRecvd)}, - {"endpoint", peer.endpoint}, + {"proto", peer.intf.info.linkType}, + {"endpoint", peer.intf.info.remote}, {"box_pub_key", hex.EncodeToString(peer.box[:])}, } peerInfos = append(peerInfos, info) diff --git a/src/yggdrasil/debug.go b/src/yggdrasil/debug.go index 8405714a..e575b72d 100644 --- a/src/yggdrasil/debug.go +++ b/src/yggdrasil/debug.go @@ -97,7 +97,15 @@ func (c *Core) DEBUG_getPeers() *peers { } func (ps *peers) DEBUG_newPeer(box crypto.BoxPubKey, sig crypto.SigPubKey, link crypto.BoxSharedKey) *peer { - return ps.newPeer(&box, &sig, &link, "(simulator)", nil) + sim := linkInterface{ + name: "(simulator)", + info: linkInfo{ + local: "(simulator)", + remote: "(simulator)", + linkType: "sim", + }, + } + return ps.newPeer(&box, &sig, &link, &sim, nil) } /* diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index ac869389..4f7c6e1f 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -173,7 +173,7 @@ func (intf *linkInterface) handler() error { intf.link.mutex.Unlock() // Create peer shared := crypto.GetSharedKey(myLinkPriv, &meta.link) - intf.peer = intf.link.core.peers.newPeer(&meta.box, &meta.sig, shared, intf.name, func() { intf.msgIO.close() }) + intf.peer = intf.link.core.peers.newPeer(&meta.box, &meta.sig, shared, intf, func() { intf.msgIO.close() }) if intf.peer == nil { return errors.New("failed to create peer") } diff --git a/src/yggdrasil/peer.go b/src/yggdrasil/peer.go index 237d6f61..ce359368 100644 --- a/src/yggdrasil/peer.go +++ b/src/yggdrasil/peer.go @@ -98,6 +98,7 @@ type peer struct { bytesRecvd uint64 // To track bandwidth usage for getPeers // BUG: sync/atomic, 32 bit platforms need the above to be the first element core *Core + intf *linkInterface port switchPort box crypto.BoxPubKey sig crypto.SigPubKey @@ -113,18 +114,19 @@ type peer struct { } // 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, closer func()) *peer { +func (ps *peers) newPeer(box *crypto.BoxPubKey, sig *crypto.SigPubKey, linkShared *crypto.BoxSharedKey, intf *linkInterface, closer func()) *peer { now := time.Now() p := peer{box: *box, sig: *sig, shared: *crypto.GetSharedKey(&ps.core.boxPriv, box), linkShared: *linkShared, - endpoint: endpoint, firstSeen: now, doSend: make(chan struct{}, 1), dinfo: make(chan *dhtInfo, 1), close: closer, - core: ps.core} + core: ps.core, + intf: intf, + } ps.mutex.Lock() defer ps.mutex.Unlock() oldPorts := ps.getPorts() diff --git a/src/yggdrasil/router.go b/src/yggdrasil/router.go index 6acd4735..1d4c0771 100644 --- a/src/yggdrasil/router.go +++ b/src/yggdrasil/router.go @@ -67,7 +67,15 @@ func (r *router) init(core *Core) { r.addr = *address.AddrForNodeID(&r.core.dht.nodeID) r.subnet = *address.SubnetForNodeID(&r.core.dht.nodeID) in := make(chan []byte, 1) // TODO something better than this... - p := r.core.peers.newPeer(&r.core.boxPub, &r.core.sigPub, &crypto.BoxSharedKey{}, "(self)", nil) + self := linkInterface{ + name: "(self)", + info: linkInfo{ + local: "(self)", + remote: "(self)", + linkType: "self", + }, + } + p := r.core.peers.newPeer(&r.core.boxPub, &r.core.sigPub, &crypto.BoxSharedKey{}, &self, nil) p.out = func(packet []byte) { in <- packet } r.in = in out := make(chan []byte, 32) diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index 8dcd4c03..c0d568a5 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -24,7 +24,6 @@ import ( "golang.org/x/net/proxy" - "github.com/yggdrasil-network/yggdrasil-go/src/crypto" "github.com/yggdrasil-network/yggdrasil-go/src/util" ) @@ -39,16 +38,7 @@ type tcp struct { 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. -// Different address combinations are allowed, so multi-homing is still technically possible (but not necessarily advisable). -type tcpInfo struct { - box crypto.BoxPubKey - sig crypto.SigPubKey - localAddr string - remoteAddr string + conns map[linkInfo](chan struct{}) } // Wrapper function to set additional options for specific connection types. @@ -90,7 +80,7 @@ func (t *tcp) init(l *link) error { t.reconfigure = make(chan chan error, 1) t.mutex.Lock() t.calls = make(map[string]struct{}) - t.conns = make(map[tcpInfo](chan struct{})) + t.conns = make(map[linkInfo](chan struct{})) t.listeners = make(map[string]net.Listener) t.listenerstops = make(map[string]chan bool) t.mutex.Unlock() @@ -167,20 +157,20 @@ func (t *tcp) listen(listenaddr string) error { // Runs the listener, which spawns off goroutines for incoming connections. func (t *tcp) listener(listenaddr string) { t.mutex.Lock() - listener, ok := t.listeners[listenaddr] + listener, ok1 := t.listeners[listenaddr] listenerstop, ok2 := t.listenerstops[listenaddr] t.mutex.Unlock() - if !ok || !ok2 { + if !ok1 || !ok2 { 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:", reallistenaddr) + accepted := make(chan bool) for { var sock net.Conn var err error - accepted := make(chan bool) go func() { sock, err = listener.Accept() accepted <- true @@ -191,26 +181,14 @@ func (t *tcp) listener(listenaddr string) { t.link.core.log.Errorln("Failed to accept connection:", err) return } + go t.handler(sock, true) case <-listenerstop: t.link.core.log.Errorln("Stopping TCP listener on:", reallistenaddr) return - default: - if err != nil { - panic(err) - } - go t.handler(sock, true) } } } -// Checks if we already have a connection to this node -func (t *tcp) isAlreadyConnected(info tcpInfo) bool { - t.mutex.Lock() - defer t.mutex.Unlock() - _, isIn := t.conns[info] - return isIn -} - // Checks if we already are calling this address func (t *tcp) isAlreadyCalling(saddr string) bool { t.mutex.Lock() From 88925d3e06921107a603eb96def354cc938bea52 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 4 Mar 2019 22:45:35 +0000 Subject: [PATCH 13/62] Centralise call/listen functions in link.go --- src/yggdrasil/admin.go | 15 +++---------- src/yggdrasil/link.go | 44 ++++++++++++++++++++++++++++++++------ src/yggdrasil/multicast.go | 2 +- src/yggdrasil/tcp.go | 25 ++++------------------ 4 files changed, 45 insertions(+), 41 deletions(-) diff --git a/src/yggdrasil/admin.go b/src/yggdrasil/admin.go index ae5b02a2..228c43c6 100644 --- a/src/yggdrasil/admin.go +++ b/src/yggdrasil/admin.go @@ -573,18 +573,9 @@ func (a *admin) printInfos(infos []admin_nodeInfo) string { // addPeer triggers a connection attempt to a node. func (a *admin) addPeer(addr string, sintf string) error { - u, err := url.Parse(addr) - if err == nil { - switch strings.ToLower(u.Scheme) { - case "tcp": - a.core.link.tcp.connect(u.Host, sintf) - case "socks": - a.core.link.tcp.connectSOCKS(u.Host, u.Path[1:]) - default: - return errors.New("invalid peer: " + addr) - } - } else { - return errors.New("invalid peer: " + addr) + err := a.core.link.call(addr, sintf) + if err != nil { + return err } return nil } diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index 4f7c6e1f..cbbdb5e9 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "net" + "net/url" "strings" "sync" @@ -68,21 +69,20 @@ func (l *link) init(c *Core) error { } if err := l.awdl.init(l); err != nil { - l.core.log.Errorln("Failed to start AWDL interface") + c.log.Errorln("Failed to start AWDL interface") 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 { + response := make(chan error) + l.tcp.reconfigure <- response + if err := <-response; err != nil { e <- err } - if err := <-awdlresponse; err != nil { + l.awdl.reconfigure <- response + if err := <-response; err != nil { e <- err } e <- nil @@ -92,6 +92,36 @@ func (l *link) init(c *Core) error { return nil } +func (l *link) call(uri string, sintf string) error { + u, err := url.Parse(uri) + if err != nil { + return err + } + pathtokens := strings.Split(strings.Trim(u.Path, "/"), "/") + switch u.Scheme { + case "tcp": + l.tcp.call(u.Host, nil, sintf) + case "socks": + l.tcp.call(pathtokens[0], &u.Host, sintf) + default: + return errors.New("unknown call scheme: " + u.Scheme) + } + return nil +} + +func (l *link) listen(uri string) error { + u, err := url.Parse(uri) + if err != nil { + return err + } + switch u.Scheme { + case "tcp": + return l.tcp.listen(u.Host) + default: + return errors.New("unknown listen scheme: " + u.Scheme) + } +} + func (l *link) create(msgIO linkInterfaceMsgIO, name, linkType, local, remote string, incoming, force bool) (*linkInterface, error) { // Technically anything unique would work for names, but lets pick something human readable, just for debugging intf := linkInterface{ diff --git a/src/yggdrasil/multicast.go b/src/yggdrasil/multicast.go index f416ea26..59f0eea5 100644 --- a/src/yggdrasil/multicast.go +++ b/src/yggdrasil/multicast.go @@ -183,6 +183,6 @@ func (m *multicast) listen() { } addr.Zone = from.Zone saddr := addr.String() - m.core.link.tcp.connect(saddr, addr.Zone) + m.core.link.call("tcp://"+saddr, addr.Zone) } } diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index c0d568a5..74f14d8a 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -64,16 +64,6 @@ func (t *tcp) getAddr() *net.TCPAddr { return nil } -// Attempts to initiate a connection to the provided address. -func (t *tcp) connect(addr string, intf string) { - t.call(addr, nil, intf) -} - -// Attempst to initiate a connection to the provided address, viathe provided socks proxy address. -func (t *tcp) connectSOCKS(socksaddr, peeraddr string) { - t.call(peeraddr, &socksaddr, "") -} - // Initializes the struct. func (t *tcp) init(l *link) error { t.link = l @@ -104,14 +94,6 @@ func (t *tcp) init(l *link) error { } 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() - } - if listener, ok := t.listenerstops[delete]; ok { - listener <- true - } - t.mutex.Unlock()*/ } e <- nil } else { @@ -202,7 +184,7 @@ func (t *tcp) isAlreadyCalling(saddr string) bool { // If the dial is successful, it launches the handler. // When finished, it removes the outgoing call, so reconnection attempts can be made later. // This all happens in a separate goroutine that it spawns. -func (t *tcp) call(saddr string, socksaddr *string, sintf string) { +func (t *tcp) call(saddr string, options interface{}, sintf string) { go func() { callname := saddr if sintf != "" { @@ -224,12 +206,13 @@ func (t *tcp) call(saddr string, socksaddr *string, sintf string) { }() var conn net.Conn var err error - if socksaddr != nil { + socksaddr, issocks := options.(string) + if issocks { if sintf != "" { return } var dialer proxy.Dialer - dialer, err = proxy.SOCKS5("tcp", *socksaddr, nil, proxy.Direct) + dialer, err = proxy.SOCKS5("tcp", socksaddr, nil, proxy.Direct) if err != nil { return } From 2ef823e69c7fd25cb6f1b18e3d9ca7ac965e4a9d Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 4 Mar 2019 23:16:46 +0000 Subject: [PATCH 14/62] Fix deadlock when reconfiguring multicast --- src/yggdrasil/link.go | 13 ++++++++----- src/yggdrasil/tcp.go | 4 +++- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index cbbdb5e9..c4f52a38 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -76,14 +76,17 @@ func (l *link) init(c *Core) error { go func() { for { e := <-l.reconfigure - response := make(chan error) - l.tcp.reconfigure <- response - if err := <-response; err != nil { + tcpresponse := make(chan error) + awdlresponse := make(chan error) + l.tcp.reconfigure <- tcpresponse + if err := <-tcpresponse; err != nil { e <- err + continue } - l.awdl.reconfigure <- response - if err := <-response; err != nil { + l.awdl.reconfigure <- awdlresponse + if err := <-awdlresponse; err != nil { e <- err + continue } e <- nil } diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index 74f14d8a..80d9ccdb 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -16,6 +16,7 @@ package yggdrasil import ( "context" + "errors" "fmt" "math/rand" "net" @@ -57,10 +58,10 @@ func (t *tcp) getAddr() *net.TCPAddr { // to multicast.go, which obviously is not great, but right now multicast.go // doesn't have the ability to send more than one address in a packet either t.mutex.Lock() + defer t.mutex.Unlock() for _, listener := range t.listeners { return listener.Addr().(*net.TCPAddr) } - t.mutex.Unlock() return nil } @@ -85,6 +86,7 @@ func (t *tcp) init(l *link) error { if len(added) > 0 || len(deleted) > 0 { for _, add := range added { if add[:6] != "tcp://" { + e <- errors.New("unknown scheme: " + add) continue } if err := t.listen(add[6:]); err != nil { From e71108dd26537ba3fffe6e228f160808d1c3785d Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 5 Mar 2019 09:16:44 +0000 Subject: [PATCH 15/62] Fix date in changelog.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c290c89..3aa0b818 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,7 +25,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - in case of vulnerabilities. --> -## [0.3.3] - 2018-02-18 +## [0.3.3] - 2019-02-18 ### Added - Dynamic reconfiguration, which allows reloading the configuration file to make changes during runtime by sending a `SIGHUP` signal (note: this only works with `-useconffile` and not `-useconf` and currently reconfiguring TUN/TAP is not supported) - Support for building Yggdrasil as an iOS or Android framework if the appropriate tools (e.g. `gomobile`/`gobind` + SDKs) are available From a17d6d3a688b772cb0b725d7228174b4fba1e672 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 5 Mar 2019 17:37:26 +0000 Subject: [PATCH 16/62] Fix getTunTap (fixes #363) --- src/yggdrasil/admin.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/yggdrasil/admin.go b/src/yggdrasil/admin.go index 62803395..b42b2315 100644 --- a/src/yggdrasil/admin.go +++ b/src/yggdrasil/admin.go @@ -173,9 +173,10 @@ func (a *admin) init(c *Core) { }) a.addHandler("getTunTap", []string{}, func(in admin_info) (r admin_info, e error) { defer func() { - recover() - r = admin_info{"none": admin_info{}} - e = nil + if err := recover(); err != nil { + r = admin_info{"none": admin_info{}} + e = nil + } }() return admin_info{ From 236692bdc42accdfecd43ffd6730df80f20a7a22 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 5 Mar 2019 17:55:46 +0000 Subject: [PATCH 17/62] Add getTunnelRouting and setTunnelRouting (fixes #362) --- src/yggdrasil/admin.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/yggdrasil/admin.go b/src/yggdrasil/admin.go index b42b2315..a7b5bd95 100644 --- a/src/yggdrasil/admin.go +++ b/src/yggdrasil/admin.go @@ -252,6 +252,23 @@ func (a *admin) init(c *Core) { }, errors.New("Failed to remove allowed key") } }) + a.addHandler("getTunnelRouting", []string{}, func(in admin_info) (admin_info, error) { + enabled := false + a.core.router.doAdmin(func() { + enabled = a.core.router.cryptokey.isEnabled() + }) + return admin_info{"enabled": enabled}, nil + }) + a.addHandler("setTunnelRouting", []string{"enabled"}, func(in admin_info) (admin_info, error) { + enabled := false + if e, ok := in["enabled"].(bool); ok { + enabled = e + } + a.core.router.doAdmin(func() { + a.core.router.cryptokey.setEnabled(enabled) + }) + return admin_info{"enabled": enabled}, nil + }) a.addHandler("addSourceSubnet", []string{"subnet"}, func(in admin_info) (admin_info, error) { var err error a.core.router.doAdmin(func() { From 26a952aa6c179831ec3de30fe58c8c3cbf49d9df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Wennerstr=C3=B6m?= Date: Tue, 5 Mar 2019 20:30:06 +0100 Subject: [PATCH 18/62] contrib/openrc: add init file for OpenRC --- contrib/openrc/yggdrasil | 49 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100755 contrib/openrc/yggdrasil diff --git a/contrib/openrc/yggdrasil b/contrib/openrc/yggdrasil new file mode 100755 index 00000000..fa819ed7 --- /dev/null +++ b/contrib/openrc/yggdrasil @@ -0,0 +1,49 @@ +#!/sbin/openrc-run + +description="An experiment in scalable routing as an encrypted IPv6 overlay network." + +CONFFILE="/etc/yggdrasil.conf" +pidfile="/run/${RC_SVCNAME}.pid" + +command="/usr/bin/yggdrasil" + +depend() { + use net dns logger +} + +start_pre() { + if [ ! -f "${CONFFILE}" ]; then + ebegin "Generating new configuration file into ${CONFFILE}" + if ! eval ${command} -genconf > ${CONFFILE}; then + eerror "Failed to generate configuration file" + exit 1 + fi + fi + + if [ ! -e /dev/net/tun ]; then + ebegin "Inserting TUN module" + + if ! modprobe tun; then + eerror "Failed to insert TUN kernel module" + exit 1 + fi + fi +} + +start() { + ebegin "Starting Yggdrasil" + start-stop-daemon --start --quiet \ + --pidfile "${pidfile}" \ + --make-pidfile \ + --background \ + --stdout /var/log/yggdrasil.stdout.log \ + --stderr /var/log/yggdrasil.stderr.log \ + --exec "${command}" -- -useconf < "${CONFFILE}" + eend $? +} + +stop() { + ebegin "Stopping Yggdrasil" + start-stop-daemon --stop --pidfile "${pidfile}" --exec "${command}" + eend $? +} From 1097c1c0c964ef1e9df95fd3a3321ef46be93485 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Wennerstr=C3=B6m?= Date: Tue, 5 Mar 2019 20:50:24 +0100 Subject: [PATCH 19/62] contrib/openrc: add reload command --- contrib/openrc/yggdrasil | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/contrib/openrc/yggdrasil b/contrib/openrc/yggdrasil index fa819ed7..3c8b09b8 100755 --- a/contrib/openrc/yggdrasil +++ b/contrib/openrc/yggdrasil @@ -6,6 +6,7 @@ CONFFILE="/etc/yggdrasil.conf" pidfile="/run/${RC_SVCNAME}.pid" command="/usr/bin/yggdrasil" +extra_started_commands="reload" depend() { use net dns logger @@ -31,19 +32,25 @@ start_pre() { } start() { - ebegin "Starting Yggdrasil" + ebegin "Starting ${RC_SVCNAME}" start-stop-daemon --start --quiet \ --pidfile "${pidfile}" \ --make-pidfile \ --background \ --stdout /var/log/yggdrasil.stdout.log \ --stderr /var/log/yggdrasil.stderr.log \ - --exec "${command}" -- -useconf < "${CONFFILE}" + --exec "${command}" -- -useconffile "${CONFFILE}" + eend $? +} + +reload() { + ebegin "Reloading ${RC_SVCNAME}" + start-stop-daemon --signal HUP --pidfile "${pidfile}" eend $? } stop() { - ebegin "Stopping Yggdrasil" + ebegin "Stopping ${RC_SVCNAME}" start-stop-daemon --stop --pidfile "${pidfile}" --exec "${command}" eend $? } From 76dd1f6345ac2a151d4110d2c35625db5bb1e81c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Wennerstr=C3=B6m?= Date: Tue, 5 Mar 2019 22:27:51 +0100 Subject: [PATCH 20/62] contrib/openrc: I like symmetry --- contrib/openrc/yggdrasil | 1 - 1 file changed, 1 deletion(-) diff --git a/contrib/openrc/yggdrasil b/contrib/openrc/yggdrasil index 3c8b09b8..4a2e0a13 100755 --- a/contrib/openrc/yggdrasil +++ b/contrib/openrc/yggdrasil @@ -23,7 +23,6 @@ start_pre() { if [ ! -e /dev/net/tun ]; then ebegin "Inserting TUN module" - if ! modprobe tun; then eerror "Failed to insert TUN kernel module" exit 1 From f4ccbe6c94fa2a49e8b54392d42f5a1c7879cebf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Wennerstr=C3=B6m?= Date: Wed, 6 Mar 2019 11:30:49 +0100 Subject: [PATCH 21/62] contrib/busybox-init: add reload and use -useconffile + Added reload command. + Use -useconffile instead, as it's required for reloading. --- contrib/busybox-init/S42yggdrasil | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/contrib/busybox-init/S42yggdrasil b/contrib/busybox-init/S42yggdrasil index d09c3a79..862efc25 100755 --- a/contrib/busybox-init/S42yggdrasil +++ b/contrib/busybox-init/S42yggdrasil @@ -34,8 +34,8 @@ start() { fi printf 'Starting yggdrasil: ' - if start-stop-daemon -S -b -x /usr/bin/yggdrasil \ - -- --useconf < "$CONFFILE"; then + if start-stop-daemon -S -q -b -x /usr/bin/yggdrasil \ + -- -useconffile "$CONFFILE"; then echo "OK" else echo "FAIL" @@ -51,20 +51,26 @@ stop() { fi } +reload() { + printf "Reloading yggdrasil: " + if start-stop-daemon -K -q -s HUP -x /usr/bin/yggdrasil; then + echo "OK" + else + echo "FAIL" + start + fi +} + restart() { stop start } -reload() { - restart -} - case "$1" in start|stop|restart|reload) "$1";; *) - echo "Usage: $0 {start|stop|restart}" + echo "Usage: $0 {start|stop|restart|reload}" exit 1 esac From de2aff27583c51cdc1a0bd428235f3c391519ffd Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 6 Mar 2019 11:06:13 +0000 Subject: [PATCH 22/62] Refactor multicast so that it creates a new TCP listener for each interface with LL addresses (so that it will not break if Listen is not set with a wildcard address) --- src/yggdrasil/link.go | 3 +- src/yggdrasil/multicast.go | 54 ++++++++++++++++++++------------- src/yggdrasil/tcp.go | 61 ++++++++++++++++++++------------------ 3 files changed, 68 insertions(+), 50 deletions(-) diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index c4f52a38..07adbe84 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -119,7 +119,8 @@ func (l *link) listen(uri string) error { } switch u.Scheme { case "tcp": - return l.tcp.listen(u.Host) + _, err := l.tcp.listen(u.Host) + return err default: return errors.New("unknown listen scheme: " + u.Scheme) } diff --git a/src/yggdrasil/multicast.go b/src/yggdrasil/multicast.go index 59f0eea5..401f6783 100644 --- a/src/yggdrasil/multicast.go +++ b/src/yggdrasil/multicast.go @@ -5,7 +5,6 @@ import ( "fmt" "net" "regexp" - "sync" "time" "golang.org/x/net/ipv6" @@ -16,19 +15,16 @@ type multicast struct { reconfigure chan chan error sock *ipv6.PacketConn groupAddr string - myAddr *net.TCPAddr - myAddrMutex sync.RWMutex + listeners map[string]*tcpListener } func (m *multicast) init(core *Core) { m.core = core m.reconfigure = make(chan chan error, 1) + m.listeners = make(map[string]*tcpListener) go func() { for { e := <-m.reconfigure - m.myAddrMutex.Lock() - m.myAddr = m.core.link.tcp.getAddr() - m.myAddrMutex.Unlock() e <- nil } }() @@ -94,10 +90,12 @@ func (m *multicast) interfaces() []net.Interface { continue } for _, expr := range exprs { + // Compile each regular expression e, err := regexp.Compile(expr) if err != nil { panic(err) } + // Does the interface match the regular expression? Store it if so if e.MatchString(iface.Name) { interfaces = append(interfaces, iface) } @@ -107,10 +105,6 @@ func (m *multicast) interfaces() []net.Interface { } func (m *multicast) announce() { - var anAddr net.TCPAddr - m.myAddrMutex.Lock() - m.myAddr = m.core.link.tcp.getAddr() - m.myAddrMutex.Unlock() groupAddr, err := net.ResolveUDPAddr("udp6", m.groupAddr) if err != nil { panic(err) @@ -121,27 +115,47 @@ func (m *multicast) announce() { } for { for _, iface := range m.interfaces() { - m.sock.JoinGroup(&iface, groupAddr) + // Find interface addresses addrs, err := iface.Addrs() 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()) + // Ignore IPv4 addresses if addrIP.To4() != nil { continue - } // IPv6 only + } + // Ignore non-link-local addresses if !addrIP.IsLinkLocalUnicast() { continue } - anAddr.IP = addrIP - anAddr.Zone = iface.Name - destAddr.Zone = iface.Name - msg := []byte(anAddr.String()) - m.sock.WriteTo(msg, nil, destAddr) + // Join the multicast group + m.sock.JoinGroup(&iface, groupAddr) + // Try and see if we already have a TCP listener for this interface + var listener *tcpListener + if _, ok := m.listeners[iface.Name]; !ok { + // No listener was found - let's create one + listenaddr := fmt.Sprintf("[%s%%%s]:0", addrIP, iface.Name) + if l, err := m.core.link.tcp.listen(listenaddr); err == nil { + // Store the listener so that we can stop it later if needed + listener = &tcpListener{ + listener: l, + stop: make(chan bool), + } + m.listeners[iface.Name] = listener + } + } else { + // An existing listener was found + listener = m.listeners[iface.Name] + } + // Get the listener details and construct the multicast beacon + lladdr := (*listener.listener).Addr().String() + if a, err := net.ResolveTCPAddr("tcp6", lladdr); err == nil { + destAddr.Zone = iface.Name + msg := []byte(a.String()) + m.sock.WriteTo(msg, nil, destAddr) + } break } time.Sleep(time.Second) diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index 80d9ccdb..652f5abe 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -33,13 +33,17 @@ 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 - mutex sync.Mutex // Protecting the below - listeners map[string]net.Listener - listenerstops map[string]chan bool - calls map[string]struct{} - conns map[linkInfo](chan struct{}) + link *link + reconfigure chan chan error + mutex sync.Mutex // Protecting the below + listeners map[string]*tcpListener + calls map[string]struct{} + conns map[linkInfo](chan struct{}) +} + +type tcpListener struct { + listener *net.Listener + stop chan bool } // Wrapper function to set additional options for specific connection types. @@ -60,7 +64,7 @@ func (t *tcp) getAddr() *net.TCPAddr { t.mutex.Lock() defer t.mutex.Unlock() for _, listener := range t.listeners { - return listener.Addr().(*net.TCPAddr) + return (*listener.listener).Addr().(*net.TCPAddr) } return nil } @@ -72,8 +76,7 @@ func (t *tcp) init(l *link) error { t.mutex.Lock() t.calls = make(map[string]struct{}) t.conns = make(map[linkInfo](chan struct{})) - t.listeners = make(map[string]net.Listener) - t.listenerstops = make(map[string]chan bool) + t.listeners = make(map[string]*tcpListener) t.mutex.Unlock() go func() { @@ -89,7 +92,7 @@ func (t *tcp) init(l *link) error { e <- errors.New("unknown scheme: " + add) continue } - if err := t.listen(add[6:]); err != nil { + if _, err := t.listen(add[6:]); err != nil { e <- err continue } @@ -110,7 +113,7 @@ func (t *tcp) init(l *link) error { if listenaddr[:6] != "tcp://" { continue } - if err := t.listen(listenaddr[6:]); err != nil { + if _, err := t.listen(listenaddr[6:]); err != nil { return err } } @@ -118,7 +121,7 @@ func (t *tcp) init(l *link) error { return nil } -func (t *tcp) listen(listenaddr string) error { +func (t *tcp) listen(listenaddr string) (*net.Listener, error) { var err error ctx := context.Background() @@ -127,36 +130,36 @@ func (t *tcp) listen(listenaddr string) error { } listener, err := lc.Listen(ctx, "tcp", listenaddr) if err == nil { + l := tcpListener{ + listener: &listener, + stop: make(chan bool, 1), + } t.mutex.Lock() - t.listeners[listenaddr] = listener - t.listenerstops[listenaddr] = make(chan bool, 1) + t.listeners[listenaddr[6:]] = &l t.mutex.Unlock() - go t.listener(listenaddr) - return nil + go t.listener(&l) + return &listener, nil } - return err + return nil, err } // Runs the listener, which spawns off goroutines for incoming connections. -func (t *tcp) listener(listenaddr string) { - t.mutex.Lock() - listener, ok1 := t.listeners[listenaddr] - listenerstop, ok2 := t.listenerstops[listenaddr] - t.mutex.Unlock() - if !ok1 || !ok2 { - t.link.core.log.Errorln("Tried to start TCP listener for", listenaddr, "which doesn't exist") +func (t *tcp) listener(listener *tcpListener) { + if listener == nil { return } - reallistenaddr := listener.Addr().String() - defer listener.Close() + reallistener := *listener.listener + reallistenaddr := reallistener.Addr().String() + stop := listener.stop + defer reallistener.Close() t.link.core.log.Infoln("Listening for TCP on:", reallistenaddr) accepted := make(chan bool) for { var sock net.Conn var err error go func() { - sock, err = listener.Accept() + sock, err = reallistener.Accept() accepted <- true }() select { @@ -166,7 +169,7 @@ func (t *tcp) listener(listenaddr string) { return } go t.handler(sock, true) - case <-listenerstop: + case <-stop: t.link.core.log.Errorln("Stopping TCP listener on:", reallistenaddr) return } From f4e17b9a9f10bab9292cddc4bd20f146d2582fa2 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 6 Mar 2019 12:07:33 +0000 Subject: [PATCH 23/62] Properly handle multicast interfaces going up and down --- src/yggdrasil/link.go | 8 +++---- src/yggdrasil/multicast.go | 37 ++++++++++++++++++---------- src/yggdrasil/tcp.go | 49 ++++++++++++++++++++++---------------- 3 files changed, 57 insertions(+), 37 deletions(-) diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index 07adbe84..a81b50d0 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -261,11 +261,11 @@ func (intf *linkInterface) handler() error { // Now block until something is ready or the timer triggers keepalive traffic select { case <-tcpTimer.C: - intf.link.core.log.Debugf("Sending (legacy) keep-alive to %s: %s, source %s", + intf.link.core.log.Tracef("Sending (legacy) keep-alive to %s: %s, source %s", strings.ToUpper(intf.info.linkType), themString, intf.info.local) send(nil) case <-sendAck: - intf.link.core.log.Debugf("Sending ack to %s: %s, source %s", + intf.link.core.log.Tracef("Sending ack to %s: %s, source %s", strings.ToUpper(intf.info.linkType), themString, intf.info.local) send(nil) case msg := <-intf.peer.linkOut: @@ -280,7 +280,7 @@ func (intf *linkInterface) handler() error { case signalReady <- struct{}{}: default: } - //intf.link.core.log.Debugf("Sending packet to %s: %s, source %s", + //intf.link.core.log.Tracef("Sending packet to %s: %s, source %s", // strings.ToUpper(intf.info.linkType), themString, intf.info.local) } } @@ -331,7 +331,7 @@ func (intf *linkInterface) handler() error { sendTimerRunning = true } if !gotMsg { - intf.link.core.log.Debugf("Received ack from %s: %s, source %s", + intf.link.core.log.Tracef("Received ack from %s: %s, source %s", strings.ToUpper(intf.info.linkType), themString, intf.info.local) } case sentMsg, ok := <-signalSent: diff --git a/src/yggdrasil/multicast.go b/src/yggdrasil/multicast.go index 401f6783..d4a03ff3 100644 --- a/src/yggdrasil/multicast.go +++ b/src/yggdrasil/multicast.go @@ -64,13 +64,13 @@ func (m *multicast) start() error { return nil } -func (m *multicast) interfaces() []net.Interface { +func (m *multicast) interfaces() map[string]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 + interfaces := make(map[string]net.Interface) allifaces, err := net.Interfaces() if err != nil { panic(err) @@ -97,7 +97,7 @@ func (m *multicast) interfaces() []net.Interface { } // Does the interface match the regular expression? Store it if so if e.MatchString(iface.Name) { - interfaces = append(interfaces, iface) + interfaces[iface.Name] = iface } } } @@ -114,7 +114,10 @@ func (m *multicast) announce() { panic(err) } for { - for _, iface := range m.interfaces() { + interfaces := m.interfaces() + // 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 { @@ -134,23 +137,24 @@ func (m *multicast) announce() { m.sock.JoinGroup(&iface, groupAddr) // Try and see if we already have a TCP listener for this interface var listener *tcpListener - if _, ok := m.listeners[iface.Name]; !ok { + if l, ok := m.listeners[iface.Name]; !ok || l.listener == nil { // No listener was found - let's create one listenaddr := fmt.Sprintf("[%s%%%s]:0", addrIP, iface.Name) if l, err := m.core.link.tcp.listen(listenaddr); err == nil { + m.core.log.Debugln("Started multicasting on", iface.Name) // Store the listener so that we can stop it later if needed - listener = &tcpListener{ - listener: l, - stop: make(chan bool), - } - m.listeners[iface.Name] = listener + m.listeners[iface.Name] = l } } 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() + lladdr := listener.listener.Addr().String() if a, err := net.ResolveTCPAddr("tcp6", lladdr); err == nil { destAddr.Zone = iface.Name msg := []byte(a.String()) @@ -160,7 +164,16 @@ func (m *multicast) announce() { } time.Sleep(time.Second) } - time.Sleep(time.Second) + // 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 + for name, listener := range m.listeners { + if _, ok := interfaces[name]; !ok { + listener.stop <- true + delete(m.listeners, name) + m.core.log.Debugln("No longer multicasting on", name) + } + } + time.Sleep(time.Second * 5) } } diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index 652f5abe..f46dc56c 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -42,7 +42,7 @@ type tcp struct { } type tcpListener struct { - listener *net.Listener + listener net.Listener stop chan bool } @@ -63,8 +63,8 @@ func (t *tcp) getAddr() *net.TCPAddr { // doesn't have the ability to send more than one address in a packet either t.mutex.Lock() defer t.mutex.Unlock() - for _, listener := range t.listeners { - return (*listener.listener).Addr().(*net.TCPAddr) + for _, l := range t.listeners { + return l.listener.Addr().(*net.TCPAddr) } return nil } @@ -121,7 +121,7 @@ func (t *tcp) init(l *link) error { return nil } -func (t *tcp) listen(listenaddr string) (*net.Listener, error) { +func (t *tcp) listen(listenaddr string) (*tcpListener, error) { var err error ctx := context.Background() @@ -131,37 +131,40 @@ func (t *tcp) listen(listenaddr string) (*net.Listener, error) { listener, err := lc.Listen(ctx, "tcp", listenaddr) if err == nil { l := tcpListener{ - listener: &listener, - stop: make(chan bool, 1), + listener: listener, + stop: make(chan bool), } - t.mutex.Lock() - t.listeners[listenaddr[6:]] = &l - t.mutex.Unlock() - go t.listener(&l) - return &listener, nil + go t.listener(&l, listenaddr[6:]) + return &l, nil } return nil, err } // Runs the listener, which spawns off goroutines for incoming connections. -func (t *tcp) listener(listener *tcpListener) { - if listener == nil { +func (t *tcp) listener(l *tcpListener, listenaddr string) { + if l == nil { return } - reallistener := *listener.listener - reallistenaddr := reallistener.Addr().String() - stop := listener.stop - defer reallistener.Close() - t.link.core.log.Infoln("Listening for TCP on:", reallistenaddr) + // Track the listener so that we can find it again in future + t.mutex.Lock() + t.listeners[listenaddr] = l + t.mutex.Unlock() + // And here we go! accepted := make(chan bool) + defer l.listener.Close() + t.link.core.log.Infoln("Listening for TCP on:", l.listener.Addr().String()) for { var sock net.Conn var err error + // Listen in a separate goroutine, as that way it does not block us from + // receiving "stop" events go func() { - sock, err = reallistener.Accept() + 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 { @@ -169,8 +172,12 @@ func (t *tcp) listener(listener *tcpListener) { return } go t.handler(sock, true) - case <-stop: - t.link.core.log.Errorln("Stopping TCP listener on:", reallistenaddr) + case <-l.stop: + t.link.core.log.Infoln("Stopping TCP listener on:", l.listener.Addr().String()) + l.listener.Close() + t.mutex.Lock() + delete(t.listeners, listenaddr) + t.mutex.Unlock() return } } From c0d5a8c0bd8e9de113edc01063f98b2665ca56a3 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 6 Mar 2019 12:09:57 +0000 Subject: [PATCH 24/62] Clean up old listeners first --- src/yggdrasil/multicast.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/yggdrasil/multicast.go b/src/yggdrasil/multicast.go index d4a03ff3..0b913ed7 100644 --- a/src/yggdrasil/multicast.go +++ b/src/yggdrasil/multicast.go @@ -115,6 +115,15 @@ func (m *multicast) announce() { } for { interfaces := m.interfaces() + // 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 + for name, listener := range m.listeners { + if _, ok := interfaces[name]; !ok { + listener.stop <- true + delete(m.listeners, name) + m.core.log.Debugln("No longer multicasting on", name) + } + } // 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 { @@ -164,15 +173,6 @@ func (m *multicast) announce() { } time.Sleep(time.Second) } - // 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 - for name, listener := range m.listeners { - if _, ok := interfaces[name]; !ok { - listener.stop <- true - delete(m.listeners, name) - m.core.log.Debugln("No longer multicasting on", name) - } - } time.Sleep(time.Second * 5) } } From 531d9f39ca03fae52a397b8e1a87ba436574b132 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 6 Mar 2019 12:15:40 +0000 Subject: [PATCH 25/62] Fix multicast bug, set static multicast interval 15 seconds --- src/yggdrasil/multicast.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/yggdrasil/multicast.go b/src/yggdrasil/multicast.go index 0b913ed7..50891bcb 100644 --- a/src/yggdrasil/multicast.go +++ b/src/yggdrasil/multicast.go @@ -153,6 +153,7 @@ func (m *multicast) announce() { m.core.log.Debugln("Started multicasting on", iface.Name) // Store the listener so that we can stop it later if needed m.listeners[iface.Name] = l + listener = l } } else { // An existing listener was found @@ -171,9 +172,8 @@ func (m *multicast) announce() { } break } - time.Sleep(time.Second) } - time.Sleep(time.Second * 5) + time.Sleep(time.Second * 15) } } From 18ef28a4772e402785c48e5df59c0fb020563da4 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 6 Mar 2019 13:00:45 +0000 Subject: [PATCH 26/62] Fix default Listen config --- src/config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/config.go b/src/config/config.go index 807ce25b..28756f17 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -82,7 +82,7 @@ func GenerateConfig(isAutoconf bool) *NodeConfig { cfg.Listen = []string{"tcp://[::]:0"} } else { r1 := rand.New(rand.NewSource(time.Now().UnixNano())) - cfg.Listen = []string{fmt.Sprintf("[::]:%d", r1.Intn(65534-32768)+32768)} + cfg.Listen = []string{fmt.Sprintf("tcp://[::]:%d", r1.Intn(65534-32768)+32768)} } cfg.AdminListen = defaults.GetDefaults().DefaultAdminListen cfg.EncryptionPublicKey = hex.EncodeToString(bpub[:]) From b8cabf321276911bca9238726c67f7fe30560ee0 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 6 Mar 2019 16:40:48 +0000 Subject: [PATCH 27/62] Support removing Listen interfaces at runtime properly --- src/yggdrasil/multicast.go | 6 +++--- src/yggdrasil/tcp.go | 23 +++++++++++++++-------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/yggdrasil/multicast.go b/src/yggdrasil/multicast.go index 50891bcb..1c67044f 100644 --- a/src/yggdrasil/multicast.go +++ b/src/yggdrasil/multicast.go @@ -149,11 +149,11 @@ func (m *multicast) announce() { if l, ok := m.listeners[iface.Name]; !ok || l.listener == nil { // No listener was found - let's create one listenaddr := fmt.Sprintf("[%s%%%s]:0", addrIP, iface.Name) - if l, err := m.core.link.tcp.listen(listenaddr); err == nil { + if li, err := m.core.link.tcp.listen(listenaddr); err == nil { m.core.log.Debugln("Started multicasting on", iface.Name) // Store the listener so that we can stop it later if needed - m.listeners[iface.Name] = l - listener = l + m.listeners[iface.Name] = li + listener = li } } else { // An existing listener was found diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index f46dc56c..0179c209 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -16,7 +16,6 @@ package yggdrasil import ( "context" - "errors" "fmt" "math/rand" "net" @@ -87,18 +86,26 @@ func (t *tcp) init(l *link) error { deleted := util.Difference(t.link.core.configOld.Listen, t.link.core.config.Listen) t.link.core.configMutex.RUnlock() if len(added) > 0 || len(deleted) > 0 { - for _, add := range added { - if add[:6] != "tcp://" { - e <- errors.New("unknown scheme: " + add) + for _, a := range added { + if a[:6] != "tcp://" { continue } - if _, err := t.listen(add[6:]); err != nil { + if _, err := t.listen(a[6:]); err != nil { e <- err continue } } - for _, delete := range deleted { - t.link.core.log.Warnln("Removing listener", delete, "not currently implemented") + for _, d := range deleted { + if d[:6] != "tcp://" { + continue + } + t.mutex.Lock() + if listener, ok := t.listeners[d[6:]]; ok { + t.mutex.Unlock() + listener.stop <- true + } else { + t.mutex.Unlock() + } } e <- nil } else { @@ -134,7 +141,7 @@ func (t *tcp) listen(listenaddr string) (*tcpListener, error) { listener: listener, stop: make(chan bool), } - go t.listener(&l, listenaddr[6:]) + go t.listener(&l, listenaddr) return &l, nil } From ad7e392afe0236ec3d0c3e4353f615129c7515b0 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 6 Mar 2019 17:32:25 +0000 Subject: [PATCH 28/62] Fix getRoutes (#339) --- cmd/yggdrasilctl/main.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/cmd/yggdrasilctl/main.go b/cmd/yggdrasilctl/main.go index bbe3cd2e..52800620 100644 --- a/cmd/yggdrasilctl/main.go +++ b/cmd/yggdrasilctl/main.go @@ -388,14 +388,18 @@ func main() { } } case "getroutes": - if _, ok := res["routes"]; !ok { - fmt.Println("No routes found") - } else if res["routes"] == nil { + if routes, ok := res["routes"].(map[string]interface{}); !ok { fmt.Println("No routes found") } else { - fmt.Println("Routes:") - for _, v := range res["routes"].([]interface{}) { - fmt.Println("-", v) + if res["routes"] == nil || len(routes) == 0 { + fmt.Println("No routes found") + } else { + fmt.Println("Routes:") + for k, v := range routes { + if pv, ok := v.(string); ok { + fmt.Println("-", k, " via ", pv) + } + } } } default: From 3bc3002fff885b892b5f7cb13ea63fe7740f578b Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 6 Mar 2019 17:37:48 +0000 Subject: [PATCH 29/62] Add handlers for setTunnelRouting/getTunnelRouting --- cmd/yggdrasilctl/main.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cmd/yggdrasilctl/main.go b/cmd/yggdrasilctl/main.go index 52800620..b8864dc3 100644 --- a/cmd/yggdrasilctl/main.go +++ b/cmd/yggdrasilctl/main.go @@ -402,6 +402,16 @@ func main() { } } } + case "settunnelrouting": + fallthrough + case "gettunnelrouting": + if enabled, ok := res["enabled"].(bool); !ok { + fmt.Println("Tunnel routing is disabled") + } else if !enabled { + fmt.Println("Tunnel routing is disabled") + } else { + fmt.Println("Tunnel routing is enabled") + } default: if json, err := json.MarshalIndent(recv["response"], "", " "); err == nil { fmt.Println(string(json)) From 57eb6eaeb0c095d66ff4fa19912c81f820c2303a Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 6 Mar 2019 17:45:47 +0000 Subject: [PATCH 30/62] Clean up config package --- src/config/config.go | 11 +---------- src/config/i2p.go | 8 -------- src/config/tor.go | 8 -------- 3 files changed, 1 insertion(+), 26 deletions(-) delete mode 100644 src/config/i2p.go delete mode 100644 src/config/tor.go diff --git a/src/config/config.go b/src/config/config.go index 28756f17..3c8bbcc6 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -12,7 +12,7 @@ import ( // NodeConfig defines all configuration values needed to run a signle yggdrasil node type NodeConfig struct { - Listen []string `comment:"Listen address for peer connections. Default is to listen for all\nTCP connections over IPv4 and IPv6 with a random port."` + Listen []string `comment:"Listen addresses for peer connections. Default is to listen for all\nTCP connections over IPv4 and IPv6 with a random port."` AdminListen string `comment:"Listen address for admin connections. Default is to listen for local\nconnections either on TCP/9001 or a UNIX socket depending on your\nplatform. Use this value for yggdrasilctl -endpoint=X. To disable\nthe admin socket, use the value \"none\" instead."` Peers []string `comment:"List of connection strings for static peers in URI format, e.g.\ntcp://a.b.c.d:e or socks://a.b.c.d:e/f.g.h.i:j."` InterfacePeers map[string][]string `comment:"List of connection strings for static peers in URI format, arranged\nby source interface, e.g. { \"eth0\": [ tcp://a.b.c.d:e ] }. Note that\nSOCKS peerings will NOT be affected by this option and should go in\nthe \"Peers\" section instead."` @@ -30,13 +30,6 @@ type NodeConfig struct { SwitchOptions SwitchOptions `comment:"Advanced options for tuning the switch. Normally you will not need\nto edit these options."` NodeInfoPrivacy bool `comment:"By default, nodeinfo contains some defaults including the platform,\narchitecture and Yggdrasil version. These can help when surveying\nthe network and diagnosing network routing problems. Enabling\nnodeinfo privacy prevents this, so that only items specified in\n\"NodeInfo\" are sent back if specified."` NodeInfo map[string]interface{} `comment:"Optional node info. This must be a { \"key\": \"value\", ... } map\nor set as null. This is entirely optional but, if set, is visible\nto the whole network on request."` - //Net NetConfig `comment:"Extended options for connecting to peers over other networks."` -} - -// NetConfig defines network/proxy related configuration values -type NetConfig struct { - Tor TorConfig `comment:"Experimental options for configuring peerings over Tor."` - I2P I2PConfig `comment:"Experimental options for configuring peerings over I2P."` } // SessionFirewall controls the session firewall configuration @@ -71,8 +64,6 @@ type SwitchOptions struct { // isAutoconf is that the TCP and UDP ports will likely end up with different // port numbers. func GenerateConfig(isAutoconf bool) *NodeConfig { - // Create a new core. - //core := Core{} // Generate encryption keys. bpub, bpriv := crypto.NewBoxKeys() spub, spriv := crypto.NewSigKeys() diff --git a/src/config/i2p.go b/src/config/i2p.go deleted file mode 100644 index 0ee4a2b7..00000000 --- a/src/config/i2p.go +++ /dev/null @@ -1,8 +0,0 @@ -package config - -// I2PConfig is the configuration structure for i2p related configuration -type I2PConfig struct { - Keyfile string // private key file or empty string for ephemeral keys - Addr string // address of i2p api connector - Enabled bool -} diff --git a/src/config/tor.go b/src/config/tor.go deleted file mode 100644 index c169cbbe..00000000 --- a/src/config/tor.go +++ /dev/null @@ -1,8 +0,0 @@ -package config - -// TorConfig is the configuration structure for Tor Proxy related values -type TorConfig struct { - OnionKeyfile string // hidden service private key for ADD_ONION (currently unimplemented) - ControlAddr string // tor control port address - Enabled bool -} From 02b1892cc583a554cb1a63f9db15790d17b3a092 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Thu, 7 Mar 2019 21:36:12 -0600 Subject: [PATCH 31/62] try to switch parents if a parent link is blocked --- src/yggdrasil/link.go | 1 + src/yggdrasil/switch.go | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index 6fc7687b..253e077f 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -309,6 +309,7 @@ func (intf *linkInterface) handler() error { case <-recvTimer.C: // We haven't received anything, so assume there's a problem and don't return this node to the switch until they start responding isAlive = false + intf.link.core.switchTable.blockPeer(intf.peer.port) case <-closeTimer.C: // We haven't received anything in a really long time, so things have died at the switch level and then some... // Just close the connection at this point... diff --git a/src/yggdrasil/switch.go b/src/yggdrasil/switch.go index bf6b9194..1576ef53 100644 --- a/src/yggdrasil/switch.go +++ b/src/yggdrasil/switch.go @@ -131,6 +131,7 @@ type peerInfo struct { faster map[switchPort]uint64 // Counter of how often a node is faster than the current parent, penalized extra if slower port switchPort // Interface number of this peer msg switchMsg // The wire switchMsg used + blocked bool // True if the link is blocked, used to avoid parenting a blocked link } // This is just a uint64 with a named type for clarity reasons. @@ -256,6 +257,29 @@ func (t *switchTable) cleanRoot() { } } +// Blocks and, if possible, unparents a peer +func (t *switchTable) blockPeer(port switchPort) { + t.mutex.Lock() + defer t.mutex.Unlock() + peer, isIn := t.data.peers[port] + if !isIn { + return + } + peer.blocked = true + t.data.peers[port] = peer + if port != t.parent { + return + } + t.parent = 0 + for _, info := range t.data.peers { + if info.port == port { + continue + } + t.unlockedHandleMsg(&info.msg, info.port, true) + } + t.unlockedHandleMsg(&peer.msg, peer.port, true) +} + // Removes a peer. // Must be called by the router mainLoop goroutine, e.g. call router.doAdmin with a lambda that calls this. // If the removed peer was this node's parent, it immediately tries to find a new parent. @@ -395,6 +419,7 @@ func (t *switchTable) unlockedHandleMsg(msg *switchMsg, fromPort switchPort, rep if reprocessing { sender.faster = oldSender.faster sender.time = oldSender.time + sender.blocked = oldSender.blocked } else { sender.faster = make(map[switchPort]uint64, len(oldSender.faster)) for port, peer := range t.data.peers { @@ -454,6 +479,11 @@ func (t *switchTable) unlockedHandleMsg(msg *switchMsg, fromPort switchPort, rep case sender.faster[t.parent] >= switch_faster_threshold: // The is reliably faster than the current parent. updateRoot = true + case !sender.blocked && oldParent.blocked: + // Replace a blocked parent + updateRoot = true + case reprocessing && sender.blocked && !oldParent.blocked: + // Don't replace an unblocked parent when reprocessing case reprocessing && sender.faster[t.parent] > oldParent.faster[sender.port]: // The sender seems to be reliably faster than the current parent, so switch to them instead. updateRoot = true From 917ca6c1c58696a13c58b4dc44a66644d1165d53 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Fri, 8 Mar 2019 10:26:46 +0000 Subject: [PATCH 32/62] Make changes based on review comments --- src/util/util.go | 2 +- src/yggdrasil/admin.go | 2 +- src/yggdrasil/awdl.go | 3 +-- src/yggdrasil/link.go | 2 +- src/yggdrasil/tcp.go | 38 ++++++++++++++++++++++++++------------ 5 files changed, 30 insertions(+), 17 deletions(-) diff --git a/src/util/util.go b/src/util/util.go index d669fa5e..49e0207a 100644 --- a/src/util/util.go +++ b/src/util/util.go @@ -87,7 +87,7 @@ func Difference(a, b []string) []string { mb[x] = true } for _, x := range a { - if _, ok := mb[x]; !ok { + if !mb[x] { ab = append(ab, x) } } diff --git a/src/yggdrasil/admin.go b/src/yggdrasil/admin.go index 8cb195e1..a0854f22 100644 --- a/src/yggdrasil/admin.go +++ b/src/yggdrasil/admin.go @@ -676,7 +676,7 @@ func (a *admin) getData_getPeers() []admin_nodeInfo { {"bytes_sent", atomic.LoadUint64(&p.bytesSent)}, {"bytes_recvd", atomic.LoadUint64(&p.bytesRecvd)}, {"proto", p.intf.info.linkType}, - {"endpoint", p.intf.info.remote}, + {"endpoint", p.intf.name}, {"box_pub_key", hex.EncodeToString(p.box[:])}, } peerInfos = append(peerInfos, info) diff --git a/src/yggdrasil/awdl.go b/src/yggdrasil/awdl.go index fe64e8bd..5e8cce16 100644 --- a/src/yggdrasil/awdl.go +++ b/src/yggdrasil/awdl.go @@ -54,8 +54,7 @@ func (a *awdl) init(l *link) error { a.mutex.Unlock() go func() { - for { - e := <-a.reconfigure + for e := range a.reconfigure { e <- nil } }() diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index a81b50d0..67ce5c17 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -105,7 +105,7 @@ func (l *link) call(uri string, sintf string) error { case "tcp": l.tcp.call(u.Host, nil, sintf) case "socks": - l.tcp.call(pathtokens[0], &u.Host, sintf) + l.tcp.call(pathtokens[0], u.Host, sintf) default: return errors.New("unknown call scheme: " + u.Scheme) } diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index 0179c209..2a177c03 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -159,7 +159,13 @@ func (t *tcp) listener(l *tcpListener, listenaddr string) { t.mutex.Unlock() // And here we go! accepted := make(chan bool) - defer l.listener.Close() + defer func() { + t.link.core.log.Infoln("Stopping TCP listener on:", l.listener.Addr().String()) + l.listener.Close() + t.mutex.Lock() + delete(t.listeners, listenaddr) + t.mutex.Unlock() + }() t.link.core.log.Infoln("Listening for TCP on:", l.listener.Addr().String()) for { var sock net.Conn @@ -178,13 +184,8 @@ func (t *tcp) listener(l *tcpListener, listenaddr string) { t.link.core.log.Errorln("Failed to accept connection:", err) return } - go t.handler(sock, true) + go t.handler(sock, true, nil) case <-l.stop: - t.link.core.log.Infoln("Stopping TCP listener on:", l.listener.Addr().String()) - l.listener.Close() - t.mutex.Lock() - delete(t.listeners, listenaddr) - t.mutex.Unlock() return } } @@ -230,8 +231,12 @@ func (t *tcp) call(saddr string, options interface{}, sintf string) { if sintf != "" { return } + dialerdst, er := net.ResolveTCPAddr("tcp", socksaddr) + if er != nil { + return + } var dialer proxy.Dialer - dialer, err = proxy.SOCKS5("tcp", socksaddr, nil, proxy.Direct) + dialer, err = proxy.SOCKS5("tcp", dialerdst.String(), nil, proxy.Direct) if err != nil { return } @@ -246,6 +251,7 @@ func (t *tcp) call(saddr string, options interface{}, sintf string) { addr: saddr, }, } + t.handler(conn, false, dialerdst.String()) } else { dialer := net.Dialer{ Control: t.tcpContext, @@ -302,12 +308,12 @@ func (t *tcp) call(saddr string, options interface{}, sintf string) { if err != nil { return } + t.handler(conn, false, nil) } - t.handler(conn, false) }() } -func (t *tcp) handler(sock net.Conn, incoming bool) { +func (t *tcp) handler(sock net.Conn, incoming bool, options interface{}) { defer sock.Close() t.setExtraOptions(sock) stream := stream{} @@ -315,8 +321,16 @@ func (t *tcp) handler(sock net.Conn, incoming bool) { local, _, _ := net.SplitHostPort(sock.LocalAddr().String()) remote, _, _ := net.SplitHostPort(sock.RemoteAddr().String()) remotelinklocal := net.ParseIP(remote).IsLinkLocalUnicast() - name := "tcp://" + sock.RemoteAddr().String() - link, err := t.link.core.link.create(&stream, name, "tcp", local, remote, incoming, remotelinklocal) + var name string + var proto string + if socksaddr, issocks := options.(string); issocks { + name = "socks://" + socksaddr + "/" + sock.RemoteAddr().String() + proto = "socks" + } else { + name = "tcp://" + sock.RemoteAddr().String() + proto = "tcp" + } + link, err := t.link.core.link.create(&stream, name, proto, local, remote, incoming, remotelinklocal) if err != nil { t.link.core.log.Println(err) panic(err) From 426d1570259dedec8d527b8c832b24b4829d4765 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Fri, 8 Mar 2019 18:51:07 -0600 Subject: [PATCH 33/62] make sure we don't replace an existing listener --- src/yggdrasil/tcp.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index 2a177c03..43ea4431 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -155,8 +155,14 @@ func (t *tcp) listener(l *tcpListener, listenaddr string) { } // Track the listener so that we can find it again in future t.mutex.Lock() - t.listeners[listenaddr] = l - t.mutex.Unlock() + if _, isIn := t.listeners[listenaddr]; isIn { + t.mutex.Unlock() + l.listener.Close() + return + } else { + t.listeners[listenaddr] = l + t.mutex.Unlock() + } // And here we go! accepted := make(chan bool) defer func() { From 03eec4b14d6dc42af4177176b2d8a6d3dfc5d160 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 9 Mar 2019 09:24:52 +0000 Subject: [PATCH 34/62] Don't leak interface name via multicast, ensure zone is always correct when dialling link-local --- src/yggdrasil/multicast.go | 8 +++++--- src/yggdrasil/tcp.go | 18 ++++++++++++------ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/yggdrasil/multicast.go b/src/yggdrasil/multicast.go index 1c67044f..bf4b2a74 100644 --- a/src/yggdrasil/multicast.go +++ b/src/yggdrasil/multicast.go @@ -166,6 +166,7 @@ func (m *multicast) announce() { // 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) @@ -208,8 +209,9 @@ func (m *multicast) listen() { if addr.IP.String() != from.IP.String() { continue } - addr.Zone = from.Zone - saddr := addr.String() - m.core.link.call("tcp://"+saddr, addr.Zone) + addr.Zone = "" + if err := m.core.link.call("tcp://"+addr.String(), from.Zone); err != nil { + m.core.log.Debugln("Call from multicast failed:", err) + } } } diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index 43ea4431..8b91457a 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -259,6 +259,16 @@ func (t *tcp) call(saddr string, options interface{}, sintf string) { } t.handler(conn, false, dialerdst.String()) } else { + dst, err := net.ResolveTCPAddr("tcp", saddr) + if err != nil { + return + } + if dst.IP.IsLinkLocalUnicast() { + dst.Zone = sintf + if dst.Zone == "" { + return + } + } dialer := net.Dialer{ Control: t.tcpContext, } @@ -272,10 +282,6 @@ func (t *tcp) call(saddr string, options interface{}, sintf string) { } addrs, err := ief.Addrs() if err == nil { - dst, err := net.ResolveTCPAddr("tcp", saddr) - if err != nil { - return - } for addrindex, addr := range addrs { src, _, err := net.ParseCIDR(addr.String()) if err != nil { @@ -309,9 +315,9 @@ func (t *tcp) call(saddr string, options interface{}, sintf string) { } } } - - conn, err = dialer.Dial("tcp", saddr) + conn, err = dialer.Dial("tcp", dst.String()) if err != nil { + t.link.core.log.Debugln("Failed to dial TCP:", err) return } t.handler(conn, false, nil) From 00ad8e594e6d757a02caccc3638b44741c903cc4 Mon Sep 17 00:00:00 2001 From: Viktor Villainov Date: Sat, 9 Mar 2019 08:15:14 -0500 Subject: [PATCH 35/62] Add AppArmor profile --- contrib/apparmor/usr.bin.yggdrasil | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 contrib/apparmor/usr.bin.yggdrasil diff --git a/contrib/apparmor/usr.bin.yggdrasil b/contrib/apparmor/usr.bin.yggdrasil new file mode 100644 index 00000000..2d178d6c --- /dev/null +++ b/contrib/apparmor/usr.bin.yggdrasil @@ -0,0 +1,22 @@ +# Last Modified: Sat Mar 9 06:08:02 2019 +#include + +/usr/bin/yggdrasil { + #include + + capability net_admin, + + network inet stream, + network inet6 dgram, + network inet6 stream, + network netlink raw, + + /lib/x86_64-linux-gnu/ld-*.so mr, + /proc/sys/net/core/somaxconn r, + /dev/net/tun rw, + + /usr/bin/yggdrasil mr, + /etc/yggdrasil.conf rw, + /run/yggdrasil.sock rw, + +} From 98d66ed0480f47eb110362a3b85f5f261724b975 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 9 Mar 2019 23:13:06 +0000 Subject: [PATCH 36/62] Update CHANGELOG.md --- CHANGELOG.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3aa0b818..c43ca7f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,31 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - in case of vulnerabilities. --> +## [0.3.4] - 2019-03-09 +### Added +- Support for multiple listeners (although currently only TCP listeners are supported) +- New multicast behaviour where each multicast interface is given it's own link-local listener and does not depend on the `Listen` configuration +- Blocking detection in the switch to avoid parenting a blocked peer +- Support for adding and removing listeners and multicast interfaces when reloading configuration during runtime +- Yggdrasil will now attempt to clean up UNIX admin sockets on startup if left behind by a previous crash +- Admin socket `getTunnelRouting` and `setTunnelRouting` calls for enabling and disabling crypto-key routing during runtime +- On macOS, Yggdrasil will now try to wake up AWDL on start-up when `awdl0` is a configured multicast interface + +### Changed +- The `Listen` configuration statement is now an array instead of a string +- The `Listen` configuration statement should now conform to the same formatting as peers with the protocol prefix, e.g. `tcp://[::]:0` +- Session workers are now non-blocking +- Multicast interval is now fixed at every 15 seconds and network interfaces are reevaluated for eligibility on each interval (where before the interval depended upon the number of configured multicast interfaces and evaluation only took place at startup) +- Dead connections are now closed in the link handler as opposed to the switch +- Peer forwarding is now prioritised instead of randomised + +### Fixed +- Admin socket `getTunTap` call now returns properly instead of claiming no interface is enabled in all cases +- Handling of `getRoutes` etc in `yggdrasilctl` is now working +- Local interface names are no longer leaked in multicast packets +- Link-local TCP connections, particularly those initiated because of multicast beacons, are now always correctly scoped for the target interface +- Yggdrasil now correctly responds to multicast interfaces going up and down during runtime + ## [0.3.3] - 2019-02-18 ### Added - Dynamic reconfiguration, which allows reloading the configuration file to make changes during runtime by sending a `SIGHUP` signal (note: this only works with `-useconffile` and not `-useconf` and currently reconfiguring TUN/TAP is not supported) From c7b4bfcef5ddb440ac7ab234609ac0ef89baa9b3 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sat, 9 Mar 2019 18:08:26 -0600 Subject: [PATCH 37/62] misc fixes --- src/yggdrasil/link.go | 11 ++++++++++- src/yggdrasil/switch.go | 12 ++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index b24c4cc6..9c9223b7 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -234,6 +234,9 @@ func (intf *linkInterface) handler() error { signalReady := make(chan struct{}, 1) signalSent := make(chan bool, 1) sendAck := make(chan struct{}, 1) + sendBlocked := time.NewTimer(time.Second) + defer util.TimerStop(sendBlocked) + util.TimerStop(sendBlocked) go func() { defer close(signalReady) defer close(signalSent) @@ -241,7 +244,9 @@ func (intf *linkInterface) handler() error { tcpTimer := time.NewTimer(interval) // used for backwards compat with old tcp defer util.TimerStop(tcpTimer) send := func(bs []byte) { + sendBlocked.Reset(time.Second) intf.msgIO.writeMsg(bs) + util.TimerStop(sendBlocked) select { case signalSent <- len(bs) > 0: default: @@ -269,7 +274,7 @@ func (intf *linkInterface) handler() error { strings.ToUpper(intf.info.linkType), themString, intf.info.local) send(nil) case msg := <-intf.peer.linkOut: - intf.msgIO.writeMsg(msg) + send(msg) case msg, ok := <-out: if !ok { return @@ -360,6 +365,10 @@ func (intf *linkInterface) handler() error { intf.link.core.switchTable.idleIn <- intf.peer.port isReady = true } + case <-sendBlocked.C: + // We blocked while trying to send something + isReady = false + intf.link.core.switchTable.blockPeer(intf.peer.port) case <-sendTimer.C: // We haven't sent anything, so signal a send of a 0 packet to let them know we're alive select { diff --git a/src/yggdrasil/switch.go b/src/yggdrasil/switch.go index 1576ef53..bff2ae39 100644 --- a/src/yggdrasil/switch.go +++ b/src/yggdrasil/switch.go @@ -677,12 +677,12 @@ func (t *switchTable) handleIn(packet []byte, idle map[switchPort]struct{}) bool //nothing case coordLen < bestCoordLen: update = true - /* - case coordLen > bestCoordLen: - //nothing - case port < best.port: - update = true - */ + /* + case coordLen > bestCoordLen: + //nothing + case port < best.port: + update = true + */ default: //nothing } From 3c696c3e55fef4e58f6a60ebfc148ff349aa39b5 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sat, 9 Mar 2019 19:27:52 -0600 Subject: [PATCH 38/62] use idle time in switch decisions to force it to try all links --- src/yggdrasil/switch.go | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/src/yggdrasil/switch.go b/src/yggdrasil/switch.go index bff2ae39..490b15f3 100644 --- a/src/yggdrasil/switch.go +++ b/src/yggdrasil/switch.go @@ -646,7 +646,7 @@ func (t *switchTable) bestPortForCoords(coords []byte) switchPort { // Handle an incoming packet // Either send it to ourself, or to the first idle peer that's free // Returns true if the packet has been handled somehow, false if it should be queued -func (t *switchTable) handleIn(packet []byte, idle map[switchPort]struct{}) bool { +func (t *switchTable) handleIn(packet []byte, idle map[switchPort]time.Time) bool { coords := switch_getPacketCoords(packet) closer := t.getCloser(coords) if len(closer) == 0 { @@ -654,15 +654,13 @@ func (t *switchTable) handleIn(packet []byte, idle map[switchPort]struct{}) bool t.toRouter <- packet return true } - table := t.getTable() var best *peer var bestDist int - var bestCoordLen int + var bestTime time.Time ports := t.core.peers.getPorts() for port, dist := range closer { to := ports[port] - _, isIdle := idle[port] - coordLen := len(table.elems[port].locator.coords) + thisTime, isIdle := idle[port] var update bool switch { case to == nil: @@ -675,21 +673,15 @@ func (t *switchTable) handleIn(packet []byte, idle map[switchPort]struct{}) bool update = true case dist > bestDist: //nothing - case coordLen < bestCoordLen: + case thisTime.Before(bestTime): update = true - /* - case coordLen > bestCoordLen: - //nothing - case port < best.port: - update = true - */ default: //nothing } if update { best = to bestDist = dist - bestCoordLen = coordLen + bestTime = thisTime } } if best != nil { @@ -836,7 +828,7 @@ func (t *switchTable) doWorker() { }() t.queues.switchTable = t t.queues.bufs = make(map[string]switch_buffer) // Packets per PacketStreamID (string) - idle := make(map[switchPort]struct{}) // this is to deduplicate things + idle := make(map[switchPort]time.Time) // this is to deduplicate things for { //t.core.log.Debugf("Switch state: idle = %d, buffers = %d", len(idle), len(t.queues.bufs)) select { @@ -869,7 +861,7 @@ func (t *switchTable) doWorker() { // Try to find something to send to this peer if !t.handleIdle(port) { // Didn't find anything ready to send yet, so stay idle - idle[port] = struct{}{} + idle[port] = time.Now() } case f := <-t.admin: f() From f5c6c191ea6d52173716f77ab4204345d2c4f8ab Mon Sep 17 00:00:00 2001 From: Viktor Villainov Date: Sat, 9 Mar 2019 22:34:26 -0500 Subject: [PATCH 39/62] AppArmor: multiarch support and allow datagram transports --- contrib/apparmor/usr.bin.yggdrasil | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contrib/apparmor/usr.bin.yggdrasil b/contrib/apparmor/usr.bin.yggdrasil index 2d178d6c..e31a27b0 100644 --- a/contrib/apparmor/usr.bin.yggdrasil +++ b/contrib/apparmor/usr.bin.yggdrasil @@ -7,11 +7,12 @@ capability net_admin, network inet stream, + network inet dgram, network inet6 dgram, network inet6 stream, network netlink raw, - /lib/x86_64-linux-gnu/ld-*.so mr, + /lib/@{multiarch}/ld-*.so mr, /proc/sys/net/core/somaxconn r, /dev/net/tun rw, From 3c2cdfea1c14ca1099fe21387f0611a162d01f9c Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 10 Mar 2019 18:05:27 +0000 Subject: [PATCH 40/62] Keep AWDL awake, or wake it up again after a minute if suspended for some reason (e.g. sleep) --- src/yggdrasil/multicast.go | 2 +- src/yggdrasil/multicast_darwin.go | 46 ++++++++++++++++++++++-------- src/yggdrasil/multicast_other.go | 2 +- src/yggdrasil/multicast_unix.go | 2 +- src/yggdrasil/multicast_windows.go | 2 +- 5 files changed, 38 insertions(+), 16 deletions(-) diff --git a/src/yggdrasil/multicast.go b/src/yggdrasil/multicast.go index bf4b2a74..a29bbc7e 100644 --- a/src/yggdrasil/multicast.go +++ b/src/yggdrasil/multicast.go @@ -57,7 +57,7 @@ func (m *multicast) start() error { // Windows can't set this flag, so we need to handle it in other ways } - m.multicastWake() + go m.multicastStarted() go m.listen() go m.announce() } diff --git a/src/yggdrasil/multicast_darwin.go b/src/yggdrasil/multicast_darwin.go index 66c18241..53646113 100644 --- a/src/yggdrasil/multicast_darwin.go +++ b/src/yggdrasil/multicast_darwin.go @@ -6,24 +6,46 @@ package yggdrasil #cgo CFLAGS: -x objective-c #cgo LDFLAGS: -framework Foundation #import -void WakeUpAWDL() { - NSNetServiceBrowser *serviceBrowser; - - serviceBrowser = [[NSNetServiceBrowser alloc] init]; - serviceBrowser.includesPeerToPeer = YES; +NSNetServiceBrowser *serviceBrowser; +void StartAWDLBrowsing() { + if (serviceBrowser == nil) { + serviceBrowser = [[NSNetServiceBrowser alloc] init]; + serviceBrowser.includesPeerToPeer = YES; + } [serviceBrowser searchForServicesOfType:@"_yggdrasil._tcp" inDomain:@""]; } +void StopAWDLBrowsing() { + if (serviceBrowser == nil) { + return; + } + [serviceBrowser stop]; +} */ import "C" -import "syscall" -import "golang.org/x/sys/unix" +import ( + "syscall" + "time" -func (m *multicast) multicastWake() { - for _, intf := range m.interfaces() { - if intf.Name == "awdl0" { - m.core.log.Infoln("Multicast discovery is waking up AWDL") - C.WakeUpAWDL() + "golang.org/x/sys/unix" +) + +var awdlGoroutineStarted bool + +func (m *multicast) multicastStarted() { + if awdlGoroutineStarted { + return + } + m.core.log.Infoln("Multicast discovery will wake up AWDL if required") + awdlGoroutineStarted = true + for { + C.StopAWDLBrowsing() + for _, intf := range m.interfaces() { + if intf.Name == "awdl0" { + C.StartAWDLBrowsing() + break + } } + time.Sleep(time.Minute) } } diff --git a/src/yggdrasil/multicast_other.go b/src/yggdrasil/multicast_other.go index 043dd3ae..e20bbda3 100644 --- a/src/yggdrasil/multicast_other.go +++ b/src/yggdrasil/multicast_other.go @@ -4,7 +4,7 @@ package yggdrasil import "syscall" -func (m *multicast) multicastWake() { +func (m *multicast) multicastStarted() { } diff --git a/src/yggdrasil/multicast_unix.go b/src/yggdrasil/multicast_unix.go index 4237481c..3da1ab4e 100644 --- a/src/yggdrasil/multicast_unix.go +++ b/src/yggdrasil/multicast_unix.go @@ -5,7 +5,7 @@ package yggdrasil import "syscall" import "golang.org/x/sys/unix" -func (m *multicast) multicastWake() { +func (m *multicast) multicastStarted() { } diff --git a/src/yggdrasil/multicast_windows.go b/src/yggdrasil/multicast_windows.go index 14c93129..3e07f6cc 100644 --- a/src/yggdrasil/multicast_windows.go +++ b/src/yggdrasil/multicast_windows.go @@ -5,7 +5,7 @@ package yggdrasil import "syscall" import "golang.org/x/sys/windows" -func (m *multicast) multicastWake() { +func (m *multicast) multicastStarted() { } From 07822a74c7decd0adfbf125722df3972c6d293e1 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 10 Mar 2019 18:32:10 +0000 Subject: [PATCH 41/62] Update CHANGELOG.md in preparation for v0.3.4 --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c43ca7f1..ae3b42cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,7 +25,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - in case of vulnerabilities. --> -## [0.3.4] - 2019-03-09 +## [0.3.4] - 2019-03-12 ### Added - Support for multiple listeners (although currently only TCP listeners are supported) - New multicast behaviour where each multicast interface is given it's own link-local listener and does not depend on the `Listen` configuration @@ -33,7 +33,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Support for adding and removing listeners and multicast interfaces when reloading configuration during runtime - Yggdrasil will now attempt to clean up UNIX admin sockets on startup if left behind by a previous crash - Admin socket `getTunnelRouting` and `setTunnelRouting` calls for enabling and disabling crypto-key routing during runtime -- On macOS, Yggdrasil will now try to wake up AWDL on start-up when `awdl0` is a configured multicast interface +- On macOS, Yggdrasil will now try to wake up AWDL on start-up when `awdl0` is a configured multicast interface, to keep it awake after system sleep, and to stop waking it when no longer needed ### Changed - The `Listen` configuration statement is now an array instead of a string From 9d5ca85424ab25fb8244a6238c780a37e2847e1c Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 10 Mar 2019 19:08:56 +0000 Subject: [PATCH 42/62] Add LinkLocalTCPPort option --- CHANGELOG.md | 1 + src/config/config.go | 1 + src/yggdrasil/multicast.go | 8 +++++++- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae3b42cc..b9ec85a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Yggdrasil will now attempt to clean up UNIX admin sockets on startup if left behind by a previous crash - Admin socket `getTunnelRouting` and `setTunnelRouting` calls for enabling and disabling crypto-key routing during runtime - On macOS, Yggdrasil will now try to wake up AWDL on start-up when `awdl0` is a configured multicast interface, to keep it awake after system sleep, and to stop waking it when no longer needed +- Added `LinkLocalTCPPort` option for controlling the port number that link-local TCP listeners will listen on by default when setting up `MulticastInterfaces` ### Changed - The `Listen` configuration statement is now an array instead of a string diff --git a/src/config/config.go b/src/config/config.go index 3c8bbcc6..dbccc785 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -22,6 +22,7 @@ type NodeConfig struct { SigningPublicKey string `comment:"Your public signing key. You should not ordinarily need to share\nthis with anyone."` SigningPrivateKey string `comment:"Your private signing key. DO NOT share this with anyone!"` MulticastInterfaces []string `comment:"Regular expressions for which interfaces multicast peer discovery\nshould be enabled on. If none specified, multicast peer discovery is\ndisabled. The default value is .* which uses all interfaces."` + LinkLocalTCPPort uint16 `comment:"The port number to be used for the link-local TCP listeners for the\nconfigured MulticastInterfaces. This option does not affect listeners\nspecified in the Listen option. Unless you plan to firewall link-local\ntraffic, it is best to leave this as the default value of 0."` IfName string `comment:"Local network interface name for TUN/TAP adapter, or \"auto\" to select\nan interface automatically, or \"none\" to run without TUN/TAP."` IfTAPMode bool `comment:"Set local network interface to TAP mode rather than TUN mode if\nsupported by your platform - option will be ignored if not."` IfMTU int `comment:"Maximux Transmission Unit (MTU) size for your local TUN/TAP interface.\nDefault is the largest supported size for your platform. The lowest\npossible value is 1280."` diff --git a/src/yggdrasil/multicast.go b/src/yggdrasil/multicast.go index a29bbc7e..dacad27e 100644 --- a/src/yggdrasil/multicast.go +++ b/src/yggdrasil/multicast.go @@ -16,12 +16,16 @@ type multicast struct { sock *ipv6.PacketConn groupAddr string listeners map[string]*tcpListener + listenPort uint16 } func (m *multicast) init(core *Core) { m.core = core m.reconfigure = make(chan chan error, 1) m.listeners = make(map[string]*tcpListener) + m.core.configMutex.RLock() + m.listenPort = m.core.config.LinkLocalTCPPort + m.core.configMutex.RUnlock() go func() { for { e := <-m.reconfigure @@ -148,12 +152,14 @@ func (m *multicast) announce() { var listener *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]:0", addrIP, iface.Name) + listenaddr := fmt.Sprintf("[%s%%%s]:%d", addrIP, iface.Name, m.listenPort) if li, err := m.core.link.tcp.listen(listenaddr); err == nil { m.core.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.core.log.Warnln("Not multicasting on", iface.Name, "due to error:", err) } } else { // An existing listener was found From ec19c479dd31ff342c808df711c50e07808401c9 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 10 Mar 2019 19:17:03 +0000 Subject: [PATCH 43/62] Add comment about no reloading for LinkLocalTCPPort --- CHANGELOG.md | 2 +- src/config/config.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b9ec85a3..76a5d2d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,7 +34,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Yggdrasil will now attempt to clean up UNIX admin sockets on startup if left behind by a previous crash - Admin socket `getTunnelRouting` and `setTunnelRouting` calls for enabling and disabling crypto-key routing during runtime - On macOS, Yggdrasil will now try to wake up AWDL on start-up when `awdl0` is a configured multicast interface, to keep it awake after system sleep, and to stop waking it when no longer needed -- Added `LinkLocalTCPPort` option for controlling the port number that link-local TCP listeners will listen on by default when setting up `MulticastInterfaces` +- Added `LinkLocalTCPPort` option for controlling the port number that link-local TCP listeners will listen on by default when setting up `MulticastInterfaces` (a node restart is currently required for changes to `LinkLocalTCPPort` to take effect - it cannot be updated by reloading config during runtime) ### Changed - The `Listen` configuration statement is now an array instead of a string diff --git a/src/config/config.go b/src/config/config.go index dbccc785..270ce963 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -22,7 +22,7 @@ type NodeConfig struct { SigningPublicKey string `comment:"Your public signing key. You should not ordinarily need to share\nthis with anyone."` SigningPrivateKey string `comment:"Your private signing key. DO NOT share this with anyone!"` MulticastInterfaces []string `comment:"Regular expressions for which interfaces multicast peer discovery\nshould be enabled on. If none specified, multicast peer discovery is\ndisabled. The default value is .* which uses all interfaces."` - LinkLocalTCPPort uint16 `comment:"The port number to be used for the link-local TCP listeners for the\nconfigured MulticastInterfaces. This option does not affect listeners\nspecified in the Listen option. Unless you plan to firewall link-local\ntraffic, it is best to leave this as the default value of 0."` + LinkLocalTCPPort uint16 `comment:"The port number to be used for the link-local TCP listeners for the\nconfigured MulticastInterfaces. This option does not affect listeners\nspecified in the Listen option. Unless you plan to firewall link-local\ntraffic, it is best to leave this as the default value of 0. This\noption cannot currently be changed by reloading config during runtime."` IfName string `comment:"Local network interface name for TUN/TAP adapter, or \"auto\" to select\nan interface automatically, or \"none\" to run without TUN/TAP."` IfTAPMode bool `comment:"Set local network interface to TAP mode rather than TUN mode if\nsupported by your platform - option will be ignored if not."` IfMTU int `comment:"Maximux Transmission Unit (MTU) size for your local TUN/TAP interface.\nDefault is the largest supported size for your platform. The lowest\npossible value is 1280."` From 229de91a3ae35cf0b088fc6064494cb8a9ab3a4b Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 12 Mar 2019 15:01:27 +0000 Subject: [PATCH 44/62] Fix AllowedEncryptionPublicKeys so that it works in incoming connections and not outgoing ones --- src/yggdrasil/link.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index 9c9223b7..10c7e0bb 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -175,7 +175,7 @@ func (intf *linkInterface) handler() error { return errors.New("failed to connect: wrong version") } // Check if we're authorized to connect to this key / IP - if !intf.incoming && !intf.force && !intf.link.core.peers.isAllowedEncryptionPublicKey(&meta.box) { + if intf.incoming && !intf.force && !intf.link.core.peers.isAllowedEncryptionPublicKey(&meta.box) { intf.link.core.log.Warnf("%s connection to %s forbidden: AllowedEncryptionPublicKeys does not contain key %s", strings.ToUpper(intf.info.linkType), intf.info.remote, hex.EncodeToString(meta.box[:])) intf.msgIO.close() From c388885a922cea4f0e5e8a6f46c314c6c0cc0e9e Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 12 Mar 2019 15:29:42 +0000 Subject: [PATCH 45/62] Update config comments for AllowedEncryptionPublicKeys --- src/config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/config.go b/src/config/config.go index 270ce963..eed6bb78 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -16,7 +16,7 @@ type NodeConfig struct { AdminListen string `comment:"Listen address for admin connections. Default is to listen for local\nconnections either on TCP/9001 or a UNIX socket depending on your\nplatform. Use this value for yggdrasilctl -endpoint=X. To disable\nthe admin socket, use the value \"none\" instead."` Peers []string `comment:"List of connection strings for static peers in URI format, e.g.\ntcp://a.b.c.d:e or socks://a.b.c.d:e/f.g.h.i:j."` InterfacePeers map[string][]string `comment:"List of connection strings for static peers in URI format, arranged\nby source interface, e.g. { \"eth0\": [ tcp://a.b.c.d:e ] }. Note that\nSOCKS peerings will NOT be affected by this option and should go in\nthe \"Peers\" section instead."` - AllowedEncryptionPublicKeys []string `comment:"List of peer encryption public keys to allow or incoming TCP\nconnections from. If left empty/undefined then all connections\nwill be allowed by default."` + AllowedEncryptionPublicKeys []string `comment:"List of peer encryption public keys to allow incoming TCP peering\nconnections from. If left empty/undefined then all connections will\nbe allowed by default. This does not affect outgoing peerings."` EncryptionPublicKey string `comment:"Your public encryption key. Your peers may ask you for this to put\ninto their AllowedEncryptionPublicKeys configuration."` EncryptionPrivateKey string `comment:"Your private encryption key. DO NOT share this with anyone!"` SigningPublicKey string `comment:"Your public signing key. You should not ordinarily need to share\nthis with anyone."` From dc3a05f13ab2ea084a6453b0b11915a5458e2ec5 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 12 Mar 2019 16:03:02 +0000 Subject: [PATCH 46/62] Correctly classify link-local addresses in the TCP handler, fix AllowedPublicEncryptionKeys warning --- src/yggdrasil/link.go | 2 +- src/yggdrasil/tcp.go | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index 10c7e0bb..bfec714b 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -176,7 +176,7 @@ func (intf *linkInterface) handler() error { } // Check if we're authorized to connect to this key / IP if intf.incoming && !intf.force && !intf.link.core.peers.isAllowedEncryptionPublicKey(&meta.box) { - intf.link.core.log.Warnf("%s connection to %s forbidden: AllowedEncryptionPublicKeys does not contain key %s", + intf.link.core.log.Warnf("%s connection from %s forbidden: AllowedEncryptionPublicKeys does not contain key %s", strings.ToUpper(intf.info.linkType), intf.info.remote, hex.EncodeToString(meta.box[:])) intf.msgIO.close() return nil diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index 8b91457a..8acf9c17 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -19,6 +19,7 @@ import ( "fmt" "math/rand" "net" + "strings" "sync" "time" @@ -332,7 +333,7 @@ func (t *tcp) handler(sock net.Conn, incoming bool, options interface{}) { stream.init(sock) local, _, _ := net.SplitHostPort(sock.LocalAddr().String()) remote, _, _ := net.SplitHostPort(sock.RemoteAddr().String()) - remotelinklocal := net.ParseIP(remote).IsLinkLocalUnicast() + force := net.ParseIP(strings.Split(remote, "%")[0]).IsLinkLocalUnicast() var name string var proto string if socksaddr, issocks := options.(string); issocks { @@ -342,7 +343,7 @@ func (t *tcp) handler(sock net.Conn, incoming bool, options interface{}) { name = "tcp://" + sock.RemoteAddr().String() proto = "tcp" } - link, err := t.link.core.link.create(&stream, name, proto, local, remote, incoming, remotelinklocal) + link, err := t.link.core.link.create(&stream, name, proto, local, remote, incoming, force) if err != nil { t.link.core.log.Println(err) panic(err) From 830be7f4db5de9836dbd2a49fdefae5aa0cec7ff Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 12 Mar 2019 16:06:12 +0000 Subject: [PATCH 47/62] Update comments again --- src/config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/config.go b/src/config/config.go index eed6bb78..4f97a2bc 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -16,7 +16,7 @@ type NodeConfig struct { AdminListen string `comment:"Listen address for admin connections. Default is to listen for local\nconnections either on TCP/9001 or a UNIX socket depending on your\nplatform. Use this value for yggdrasilctl -endpoint=X. To disable\nthe admin socket, use the value \"none\" instead."` Peers []string `comment:"List of connection strings for static peers in URI format, e.g.\ntcp://a.b.c.d:e or socks://a.b.c.d:e/f.g.h.i:j."` InterfacePeers map[string][]string `comment:"List of connection strings for static peers in URI format, arranged\nby source interface, e.g. { \"eth0\": [ tcp://a.b.c.d:e ] }. Note that\nSOCKS peerings will NOT be affected by this option and should go in\nthe \"Peers\" section instead."` - AllowedEncryptionPublicKeys []string `comment:"List of peer encryption public keys to allow incoming TCP peering\nconnections from. If left empty/undefined then all connections will\nbe allowed by default. This does not affect outgoing peerings."` + AllowedEncryptionPublicKeys []string `comment:"List of peer encryption public keys to allow incoming TCP peering\nconnections from. If left empty/undefined then all connections will\nbe allowed by default. This does not affect outgoing peerings, nor\ndoes it affect link-local peers discovered via multicast."` EncryptionPublicKey string `comment:"Your public encryption key. Your peers may ask you for this to put\ninto their AllowedEncryptionPublicKeys configuration."` EncryptionPrivateKey string `comment:"Your private encryption key. DO NOT share this with anyone!"` SigningPublicKey string `comment:"Your public signing key. You should not ordinarily need to share\nthis with anyone."` From 4062c93e18129b8f51ea86602b7d90c4b86e509c Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 12 Mar 2019 19:04:30 +0000 Subject: [PATCH 48/62] Re-order config, update default Listen --- src/config/config.go | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/src/config/config.go b/src/config/config.go index 4f97a2bc..6ee10130 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -2,9 +2,6 @@ package config import ( "encoding/hex" - "fmt" - "math/rand" - "time" "github.com/yggdrasil-network/yggdrasil-go/src/crypto" "github.com/yggdrasil-network/yggdrasil-go/src/defaults" @@ -12,16 +9,16 @@ import ( // NodeConfig defines all configuration values needed to run a signle yggdrasil node type NodeConfig struct { - Listen []string `comment:"Listen addresses for peer connections. Default is to listen for all\nTCP connections over IPv4 and IPv6 with a random port."` + Peers []string `comment:"List of connection strings for outbound peer connections in URI format,\ne.g. tcp://a.b.c.d:e or socks://a.b.c.d:e/f.g.h.i:j. These connections\nwill obey the operating system routing table, therefore you should\nuse this section when you may connect via different interfaces."` + InterfacePeers map[string][]string `comment:"List of connection strings for outbound peer connections in URI format,\narranged by source interface, e.g. { \"eth0\": [ tcp://a.b.c.d:e ] }.\nNote that SOCKS peerings will NOT be affected by this option and should\ngo in the \"Peers\" section instead."` + Listen []string `comment:"Listen addresses for incoming connections. You will need to add\nlisteners in order to accept incoming peerings from non-local nodes.\nMulticast peer discovery will work regardless of any listeners set\nhere. Each listener should be specified in URI format as above, e.g.\ntcp://0.0.0.0:0 or tcp://[::]:0 to listen on all interfaces."` AdminListen string `comment:"Listen address for admin connections. Default is to listen for local\nconnections either on TCP/9001 or a UNIX socket depending on your\nplatform. Use this value for yggdrasilctl -endpoint=X. To disable\nthe admin socket, use the value \"none\" instead."` - Peers []string `comment:"List of connection strings for static peers in URI format, e.g.\ntcp://a.b.c.d:e or socks://a.b.c.d:e/f.g.h.i:j."` - InterfacePeers map[string][]string `comment:"List of connection strings for static peers in URI format, arranged\nby source interface, e.g. { \"eth0\": [ tcp://a.b.c.d:e ] }. Note that\nSOCKS peerings will NOT be affected by this option and should go in\nthe \"Peers\" section instead."` + MulticastInterfaces []string `comment:"Regular expressions for which interfaces multicast peer discovery\nshould be enabled on. If none specified, multicast peer discovery is\ndisabled. The default value is .* which uses all interfaces."` AllowedEncryptionPublicKeys []string `comment:"List of peer encryption public keys to allow incoming TCP peering\nconnections from. If left empty/undefined then all connections will\nbe allowed by default. This does not affect outgoing peerings, nor\ndoes it affect link-local peers discovered via multicast."` EncryptionPublicKey string `comment:"Your public encryption key. Your peers may ask you for this to put\ninto their AllowedEncryptionPublicKeys configuration."` EncryptionPrivateKey string `comment:"Your private encryption key. DO NOT share this with anyone!"` SigningPublicKey string `comment:"Your public signing key. You should not ordinarily need to share\nthis with anyone."` SigningPrivateKey string `comment:"Your private signing key. DO NOT share this with anyone!"` - MulticastInterfaces []string `comment:"Regular expressions for which interfaces multicast peer discovery\nshould be enabled on. If none specified, multicast peer discovery is\ndisabled. The default value is .* which uses all interfaces."` LinkLocalTCPPort uint16 `comment:"The port number to be used for the link-local TCP listeners for the\nconfigured MulticastInterfaces. This option does not affect listeners\nspecified in the Listen option. Unless you plan to firewall link-local\ntraffic, it is best to leave this as the default value of 0. This\noption cannot currently be changed by reloading config during runtime."` IfName string `comment:"Local network interface name for TUN/TAP adapter, or \"auto\" to select\nan interface automatically, or \"none\" to run without TUN/TAP."` IfTAPMode bool `comment:"Set local network interface to TAP mode rather than TUN mode if\nsupported by your platform - option will be ignored if not."` @@ -70,12 +67,7 @@ func GenerateConfig(isAutoconf bool) *NodeConfig { spub, spriv := crypto.NewSigKeys() // Create a node configuration and populate it. cfg := NodeConfig{} - if isAutoconf { - cfg.Listen = []string{"tcp://[::]:0"} - } else { - r1 := rand.New(rand.NewSource(time.Now().UnixNano())) - cfg.Listen = []string{fmt.Sprintf("tcp://[::]:%d", r1.Intn(65534-32768)+32768)} - } + cfg.Listen = []string{} cfg.AdminListen = defaults.GetDefaults().DefaultAdminListen cfg.EncryptionPublicKey = hex.EncodeToString(bpub[:]) cfg.EncryptionPrivateKey = hex.EncodeToString(bpriv[:]) @@ -91,6 +83,7 @@ func GenerateConfig(isAutoconf bool) *NodeConfig { cfg.SessionFirewall.Enable = false cfg.SessionFirewall.AllowFromDirect = true cfg.SessionFirewall.AllowFromRemote = true + cfg.SessionFirewall.AlwaysAllowOutbound = true cfg.SwitchOptions.MaxTotalQueueSize = 4 * 1024 * 1024 cfg.NodeInfoPrivacy = false From 41872820c38574fbc3fd6be3265e43ce49fb0333 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 12 Mar 2019 19:18:43 +0000 Subject: [PATCH 49/62] Remove isAutoconf option to GenerateConfig --- cmd/yggdrasil/main.go | 6 +++--- src/config/config.go | 2 +- src/yggdrasil/mobile.go | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index e0c764e7..8c8340f0 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -62,7 +62,7 @@ func readConfig(useconf *bool, useconffile *string, normaliseconf *bool) *nodeCo // 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 := config.GenerateConfig(false) + cfg := config.GenerateConfig() var dat map[string]interface{} if err := hjson.Unmarshal(conf, &dat); err != nil { panic(err) @@ -154,7 +154,7 @@ func readConfig(useconf *bool, useconffile *string, normaliseconf *bool) *nodeCo // Generates a new configuration and returns it in HJSON format. This is used // with -genconf. func doGenconf(isjson bool) string { - cfg := config.GenerateConfig(false) + cfg := config.GenerateConfig() var bs []byte var err error if isjson { @@ -191,7 +191,7 @@ func main() { case *autoconf: // Use an autoconf-generated config, this will give us random keys and // port numbers, and will use an automatically selected TUN/TAP interface. - cfg = config.GenerateConfig(true) + cfg = config.GenerateConfig() case *useconffile != "" || *useconf: // Read the configuration from either stdin or from the filesystem cfg = readConfig(useconf, useconffile, normaliseconf) diff --git a/src/config/config.go b/src/config/config.go index 6ee10130..a9007585 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -61,7 +61,7 @@ type SwitchOptions struct { // or whether to generate a random port number. The only side effect of setting // isAutoconf is that the TCP and UDP ports will likely end up with different // port numbers. -func GenerateConfig(isAutoconf bool) *NodeConfig { +func GenerateConfig() *NodeConfig { // Generate encryption keys. bpub, bpriv := crypto.NewBoxKeys() spub, spriv := crypto.NewSigKeys() diff --git a/src/yggdrasil/mobile.go b/src/yggdrasil/mobile.go index 76fbe54d..81aa47f1 100644 --- a/src/yggdrasil/mobile.go +++ b/src/yggdrasil/mobile.go @@ -45,7 +45,7 @@ func (c *Core) addStaticPeers(cfg *config.NodeConfig) { func (c *Core) StartAutoconfigure() error { mobilelog := MobileLogger{} logger := log.New(mobilelog, "", 0) - nc := config.GenerateConfig(true) + nc := config.GenerateConfig() nc.IfName = "dummy" nc.AdminListen = "tcp://localhost:9001" nc.Peers = []string{} @@ -64,7 +64,7 @@ func (c *Core) StartAutoconfigure() error { func (c *Core) StartJSON(configjson []byte) error { mobilelog := MobileLogger{} logger := log.New(mobilelog, "", 0) - nc := config.GenerateConfig(false) + nc := config.GenerateConfig() var dat map[string]interface{} if err := hjson.Unmarshal(configjson, &dat); err != nil { return err @@ -82,7 +82,7 @@ func (c *Core) StartJSON(configjson []byte) error { // Generates mobile-friendly configuration in JSON format. func GenerateConfigJSON() []byte { - nc := config.GenerateConfig(false) + nc := config.GenerateConfig() nc.IfName = "dummy" if json, err := json.Marshal(nc); err == nil { return json From 5bacfabae7061abf3e0a4e7dc6f7628d6c2f378d Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 13 Mar 2019 17:43:33 +0000 Subject: [PATCH 50/62] Handle cases where link-local addresses may disappear or change --- src/yggdrasil/multicast.go | 39 +++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/src/yggdrasil/multicast.go b/src/yggdrasil/multicast.go index dacad27e..ca3a1f75 100644 --- a/src/yggdrasil/multicast.go +++ b/src/yggdrasil/multicast.go @@ -122,11 +122,48 @@ func (m *multicast) announce() { // 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 for name, listener := range m.listeners { - if _, ok := interfaces[name]; !ok { + // Prepare our stop function! + stop := func() { listener.stop <- true delete(m.listeners, name) m.core.log.Debugln("No longer multicasting on", name) } + // If the interface is no longer visible on the system then stop the + // listener, as another one will be started further down + if _, ok := interfaces[name]; !ok { + stop() + continue + } + // 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 + found := false + listenaddr, err := net.ResolveTCPAddr("tcp6", listener.listener.Addr().String()) + if err != nil { + stop() + continue + } + // Find the interface that matches the listener + if intf, err := net.InterfaceByName(name); err == nil { + if addrs, err := intf.Addrs(); err == nil { + // Loop through the addresses attached to that listener and see if any + // of them match the current address of the listener + for _, addr := range addrs { + if ip, _, err := net.ParseCIDR(addr.String()); err == nil { + // Does the interface address match our listener address? + if ip.Equal(listenaddr.IP) { + found = true + 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, // we can start checking if we can send multicasts on them From d0aeffb5f444707c7feb2dfa7684c08e071e707a Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 13 Mar 2019 18:05:59 +0000 Subject: [PATCH 51/62] Update CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 76a5d2d7..5894598c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - in case of vulnerabilities. --> +## [0.3.5] - 2019-03-13 +### Fixed +- The `AllowedEncryptionPublicKeys` option has now been fixed to handle incoming connections properly and no longer blocks outgoing connections (this was broken in v0.3.4) +- Multicast TCP listeners will now be stopped correctly when the link-local address on the interface changes or disappears altogether + ## [0.3.4] - 2019-03-12 ### Added - Support for multiple listeners (although currently only TCP listeners are supported) From d4437afa34cfe61286b82e910966fd8c66ca1bbd Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 13 Mar 2019 18:27:20 +0000 Subject: [PATCH 52/62] Update CircleCI to 2.1 --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 22557167..8c1988c7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,7 +1,7 @@ # Golang CircleCI 2.0 configuration file # # Check https://circleci.com/docs/2.0/language-go/ for more details -version: 2 +version: 2.1 jobs: build-linux: docker: From d6111911d465d8a29f3280a89da767a5443bf2d3 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 13 Mar 2019 18:36:28 +0000 Subject: [PATCH 53/62] Update CircleCI again --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 8c1988c7..9f51d3c8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -178,8 +178,8 @@ jobs: destination: / workflows: - version: 2 - build-all: + version: 2.1 + build: jobs: - build-linux - build-macos From 9f16fc47b3c5493d6d02d45b8b75cbdc59a34ffc Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 13 Mar 2019 18:41:47 +0000 Subject: [PATCH 54/62] Update CircleCI again --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 9f51d3c8..d02e84e5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -5,7 +5,7 @@ version: 2.1 jobs: build-linux: docker: - - image: circleci/golang:1.11 + - image: circleci/golang:1.12 steps: - checkout From 8ddadce699ff61561d3b1de88be4236aa21f6af6 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 13 Mar 2019 18:47:03 +0000 Subject: [PATCH 55/62] Update CircleCI to use Go 1.12 on macOS --- .circleci/config.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d02e84e5..f29bfe0d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -79,11 +79,11 @@ jobs: echo -e "Host *\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config - run: - name: Install Go 1.11 + name: Install Go 1.12 command: | cd /tmp - curl -LO https://dl.google.com/go/go1.11.5.darwin-amd64.pkg - sudo installer -pkg /tmp/go1.11.5.darwin-amd64.pkg -target / + curl -LO https://dl.google.com/go/go1.12.darwin-amd64.pkg + sudo installer -pkg /tmp/go1.12.darwin-amd64.pkg -target / - run: name: Install Gomobile From 14afb8881e744f4cbf65fac01c0ebb1dcbdadfd8 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 13 Mar 2019 18:51:00 +0000 Subject: [PATCH 56/62] Update CircleCI to use Go 1.12 on other --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f29bfe0d..5b32c2f9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -119,7 +119,7 @@ jobs: build-other: docker: - - image: circleci/golang:1.11 + - image: circleci/golang:1.12 steps: - checkout From 9019ccc118a2805762e4d42060750ebc6436d7e7 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 13 Mar 2019 19:09:09 +0000 Subject: [PATCH 57/62] Don't install gomobile for now --- .circleci/config.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5b32c2f9..99088d1c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -85,11 +85,11 @@ jobs: curl -LO https://dl.google.com/go/go1.12.darwin-amd64.pkg sudo installer -pkg /tmp/go1.12.darwin-amd64.pkg -target / - - run: - name: Install Gomobile - command: | - GO111MODULE=off go get golang.org/x/mobile/cmd/gomobile - gomobile init + #- run: + # name: Install Gomobile + # command: | + # GO111MODULE=off go get golang.org/x/mobile/cmd/gomobile + # gomobile init - run: name: Build for macOS From 09c92698dffa5d54e61702acd7191efccafbd517 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 13 Mar 2019 20:06:02 +0000 Subject: [PATCH 58/62] Update README.md --- .gitmodules | 3 + README.md | 215 +++++++++++++++----------------- doc/yggdrasil-network.github.io | 1 + 3 files changed, 103 insertions(+), 116 deletions(-) create mode 100644 .gitmodules create mode 160000 doc/yggdrasil-network.github.io diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..e4e8b525 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "doc/yggdrasil-network.github.io"] + path = doc/yggdrasil-network.github.io + url = https://github.com/yggdrasil-network/yggdrasil-network.github.io/ diff --git a/README.md b/README.md index 11a4cbe5..a9a9d968 100644 --- a/README.md +++ b/README.md @@ -3,149 +3,132 @@ [![CircleCI](https://circleci.com/gh/yggdrasil-network/yggdrasil-go.svg?style=shield&circle-token=:circle-token )](https://circleci.com/gh/yggdrasil-network/yggdrasil-go) -## What is it? +## Introduction -This is a toy implementation of an encrypted IPv6 network, with many good ideas stolen from [cjdns](https://github.com/cjdelisle/cjdns), which was written to test a particular routing scheme that was cobbled together one random afternoon. -It's notably not a shortest path routing scheme, with the goal of scalable name-independent routing on dynamic networks with an internet-like topology. -It's named Yggdrasil after the world tree from Norse mythology, because that seemed like the obvious name given how it works. -More information is available at . +Yggdrasil is an early-stage implementation of a fully end-to-end encrypted IPv6 +network. It is lightweight, self-arranging, supported on multiple platforms and +allows pretty much any IPv6-capable application to communicate securely with +other Yggdrasil nodes. Yggdrasil does not require you to have IPv6 Internet +connectivity - it also works over IPv4. -This is a toy / proof-of-principle, and considered alpha quality by the developers. It's not expected to be feature complete, and future updates may not be backwards compatible, though it should warn you if it sees a connection attempt with a node running a newer version. -You're encouraged to play with it, but it is strongly advised not to use it for anything mission critical. +Although Yggdrasil shares many similarities with +[cjdns](https://github.com/cjdelisle/cjdns), it employs a different routing +algorithm based on a globally-agreed spanning tree and greedy routing in a +metric space, and aims to implement some novel local backpressure routing +techniques. In theory, Yggdrasil should scale well on networks with +internet-like topologies. + +## Supported Platforms + +We actively support the following platforms, and packages are available for +some of the below: + +- Linux + - `.deb` and `.rpm` packages are built by CI for Debian and Red Hat-based + distributions + - Void and Arch packages also available within their respective repositories +- macOS + - `.pkg` packages are built by CI +- Ubiquiti EdgeOS + - `.deb` Vyatta packages are built by CI +- Windows +- FreeBSD +- OpenBSD +- NetBSD + +Please see our [Platforms](https://yggdrasil-network.github.io/) pages for more +specific information about each of our supported platforms, including +installation steps and caveats. ## Building -1. Install Go (requires 1.11 or later, [godeb](https://github.com/niemeyer/godeb) is recommended for Debian-based Linux distributions). -2. Clone this repository. -2. `./build` +If you want to build from source, as opposed to installing one of the pre-built +packages: -Note that you can cross-compile for other platforms and architectures by specifying the `$GOOS` and `$GOARCH` environment variables, for example, `GOOS=windows ./build` or `GOOS=linux GOARCH=mipsle ./build`. +1. Install [Go](https://golang.org) (requires Go 1.11 or later) +2. Clone this repository +2. Run `./build` -The build script sets its own `$GOPATH`, so the build environment is self-contained. +Note that you can cross-compile for other platforms and architectures by +specifying the `GOOS` and `GOARCH` environment variables, e.g. `GOOS=windows +./build` or `GOOS=linux GOARCH=mipsle ./build`. ## Running -To run the program, you'll need permission to create a `tun` device and configure it using `ip`. -If you don't want to mess with capabilities for the `tun` device, then using `sudo` should work, with the usual security caveats about running a program as root. +To generate static configuration, either generate a HJSON file (human-friendly, +complete with comments): -To run with default settings: - -1. `./yggdrasil --autoconf` - -That will generate a new set of keys (and an IP address) each time the program is run. -The program will bind to all addresses on a random port and listen for incoming connections. -It will send announcements over IPv6 link-local multicast, and it will attempt to start a connection if it hears an announcement from another device. - -In practice, you probably want to run this instead: - -1. `./yggdrasil --genconf > conf.json` -2. `./yggdrasil --useconf < conf.json` - -This keeps a persistent set of keys (and by extension, IP address) and gives you the option of editing the configuration file. -If you want to use it as an overlay network on top of e.g. the internet, then you can do so by adding the remote devices domain/address and port (as a string, e.g. `"1.2.3.4:5678"`) to the list of `Peers` in the configuration file. -By default, it peers over TCP (which can be forced with `"tcp://1.2.3.4:5678"` syntax), but it's also possible to connect over a socks proxy (`"socks://socksHost:socksPort/1.2.3.4:5678"`). -The socks proxy approach is useful for e.g. [peering over tor hidden services](https://github.com/yggdrasil-network/public-peers/blob/master/other/tor.md). -UDP support was removed as part of v0.2, and may be replaced by a better implementation at a later date. - -### Platforms - -#### Linux - -- Should work out of the box on most Linux distributions with `iproute2` installed. -- systemd service scripts are included in the `contrib/systemd/` folder so that it runs automatically in the background (using `/etc/yggdrasil.conf` for configuration), copy the service files into `/etc/systemd/system`, copy `yggdrasil` into your `$PATH`, i.e. `/usr/bin`, and then enable the service: ``` -systemctl enable yggdrasil -systemctl start yggdrasil -``` -- Once installed as a systemd service, you can read the `yggdrasil` output: -``` -systemctl status yggdrasil -journalctl -u yggdrasil +./yggdrasil -genconf /path/to/yggdrasil.conf ``` -#### macOS +... or generate a plain JSON file (which is easy to manipulate +programmatically): -- Tested and working out of the box on macOS 10.13 High Sierra. -- May work in theory on any macOS version with `utun` support (which was added in macOS 10.7 Lion), although this is untested at present. -- TAP mode is not supported on macOS. - -#### FreeBSD, NetBSD - -- Works in TAP mode, but currently doesn't work in TUN mode. -- You may need to create the TAP adapter first if it doesn't already exist, i.e. `ifconfig tap0 create`. - -#### OpenBSD - -- Works in TAP mode, but currently doesn't work in TUN mode. -- You may need to create the TAP adapter first if it doesn't already exist, i.e. `ifconfig tap0 create`. -- OpenBSD is not capable of listening on both IPv4 and IPv6 at the same time on the same socket (unlike FreeBSD and NetBSD). This affects the `Listen` and `AdminListen` configuration options. You will need to set `Listen` and `AdminListen` to use either an IPv4 or an IPv6 address. -- You may consider using [relayd](https://man.openbsd.org/relayd.8) to allow incoming Yggdrasil connections on both IPv4 and IPv6 simultaneously. - -#### Windows - -- Tested and working on Windows 7 and Windows 10, and should work on any recent versions of Windows, but it depends on the [OpenVPN TAP driver](https://openvpn.net/index.php/open-source/downloads.html) being installed first. -- Has been proven to work with both the [NDIS 5](https://swupdate.openvpn.org/community/releases/tap-windows-9.9.2_3.exe) (`tap-windows-9.9.2_3`) driver and the [NDIS 6](https://swupdate.openvpn.org/community/releases/tap-windows-9.21.2.exe) (`tap-windows-9.21.2`) driver, however there are substantial performance issues with the NDIS 6 driver therefore it is recommended to use the NDIS 5 driver instead. -- Be aware that connectivity issues can occur on Windows if multiple IPv6 addresses from the `200::/7` prefix are assigned to the TAP interface. If this happens, then you may need to manually remove the old/unused addresses from the interface (though the code has a workaround in place to do this automatically in some cases). -- TUN mode is not supported on Windows. -- Yggdrasil can be installed as a Windows service so that it runs automatically in the background. From an Administrator Command Prompt: ``` -sc create yggdrasil binpath= "\"C:\path\to\yggdrasil.exe\" -useconffile \"C:\path\to\yggdrasil.conf\"" -sc config yggdrasil displayname= "Yggdrasil Service" -sc config yggdrasil start= "auto" -sc start yggdrasil -``` -- Alternatively, if you want the service to autoconfigure instead of using an `yggdrasil.conf`, replace the `sc create` line from above with: -``` -sc create yggdrasil binpath= "\"C:\path\to\yggdrasil.exe\" -autoconf" +./yggdrasil -genconf /path/to/yggdrasil.conf -json ``` -#### EdgeRouter +You will need to edit the `yggdrasil.conf` file to add or remove peers, modify +other configuration such as listen addresses or multicast addresses, etc. -- Tested and working on the EdgeRouter X, using the [vyatta-yggdrasil](https://github.com/neilalexander/vyatta-yggdrasil) wrapper package. - -## Optional: advertise a prefix locally - -Suppose a node has generated the address: `200:1111:2222:3333:4444:5555:6666:7777` - -Then the node may also use addresses from the prefix: `300:1111:2222:3333::/64` (note the `200` changed to `300`, a separate `/8` is used for prefixes, but the rest of the first 64 bits are the same). - -To advertise this prefix and a route to `200::/7`, the following seems to work on the developers' networks: - -1. Enable IPv6 forwarding (e.g. `sysctl -w net.ipv6.conf.all.forwarding=1` or add it to sysctl.conf). - -2. `ip addr add 300:1111:2222:3333::1/64 dev eth0` or similar, to assign an address for the router to use in that prefix, where the LAN is reachable through `eth0`. - -3. Install/run `radvd` with something like the following in `/etc/radvd.conf`: +To run with the generated static configuration: ``` -interface eth0 -{ - AdvSendAdvert on; - prefix 300:1111:2222:3333::/64 { - AdvOnLink on; - AdvAutonomous on; - }; - route 200::/7 {}; -}; +./yggdrasil -useconffile /path/to/yggdrasil.conf ``` -This is enough to give unsupported devices on the LAN access to the yggdrasil network. See the [configuration](https://yggdrasil-network.github.io/configuration.html) page for more info. +To run in auto-configuration mode (which will use sane defaults and random keys +at each startup, instead of using a static configuration file): -## How does it work? +``` +./yggdrasil --autoconf +``` -I'd rather not try to explain in the readme, but it is described further on the [about](https://yggdrasil-network.github.io/about.html) page, so you can check there if you're interested. -Be warned that it's still not a very good explanation, but it at least gives a high-level overview and links to some relevant work by other people. +You will likely need to run Yggdrasil as a privileged user or under `sudo`, +unless you have permission to create TUN/TAP adapters. On Linux this can be done +by giving the Yggdrasil binary the `CAP_NET_ADMIN` capability. -## Obligatory performance propaganda +## Documentation -A [simplified model](misc/sim/treesim-forward.py) of this routing scheme has been tested in simulation on the 9204-node [skitter](https://www.caida.org/tools/measurement/skitter/) network topology dataset from [caida](https://www.caida.org/), and compared with results in [arxiv:0708.2309](https://arxiv.org/abs/0708.2309). -Using the routing scheme as implemented in this code, the average multiplicative stretch is observed to be about 1.08, with an average routing table size of 6 for a name-dependent scheme, and approximately 30 additional (but smaller) entries needed for the name-independent routing table. -The number of name-dependent routing table entries needed is proportional to node degree, so that 6 is the mean of a distribution with a long tail, but this may be an acceptable tradeoff(it's at least worth trying, hence this code). -The size of name-dependent routing table entries is relatively large, due to cryptographic signatures associated with routing table updates, but in the absence of cryptographic overhead, each entry should otherwise be comparable in size to the BC routing scheme described in the above paper. -A modified version of this scheme, with the same resource requirements, achieves a multiplicative stretch of 1.02, which drops to 1.01 if source routing is used. -Both of these optimizations are not present in the current implementation, as the former depends on network state information that appears difficult to cryptographically secure, and the latter optimization is both tedious to implement and would make debugging other aspects of the implementation more difficult. +Documentation is available on our [`GitHub +Pages`](https://yggdrasil-network.github.io) site, or in the base submodule +repository within `doc/yggdrasil-network.github.io`. + +- [Configuration file options](https://yggdrasil-network.github.io/configuration.html) +- [Platform-specific documentation](https://yggdrasil-network.github.io/platforms.html) +- [Frequently asked questions](https://yggdrasil-network.github.io/faq.html) +- [Admin API documentation](https://yggdrasil-network.github.io/admin.html) + +## Performance + +A [simplified model](misc/sim/treesim-forward.py) of this routing scheme has +been tested in simulation on the 9204-node +[skitter](https://www.caida.org/tools/measurement/skitter/) network topology +dataset from [caida](https://www.caida.org/), and compared with results in +[arxiv:0708.2309](https://arxiv.org/abs/0708.2309). Using the routing scheme as +implemented in this code, the average multiplicative stretch is observed to be +about 1.08, with an average routing table size of 6 for a name-dependent scheme, +and approximately 30 additional (but smaller) entries needed for the +name-independent routing table. The number of name-dependent routing table +entries needed is proportional to node degree, so that 6 is the mean of a +distribution with a long tail, but this may be an acceptable tradeoff (it's at +least worth trying, hence this code). The size of name-dependent routing table +entries is relatively large, due to cryptographic signatures associated with +routing table updates, but in the absence of cryptographic overhead, each entry +should otherwise be comparable in size to the BC routing scheme described in the +above paper. A modified version of this scheme, with the same resource +requirements, achieves a multiplicative stretch of 1.02, which drops to 1.01 if +source routing is used. Both of these optimizations are not present in the +current implementation, as the former depends on network state information that +appears difficult to cryptographically secure, and the latter optimization is +both tedious to implement and would make debugging other aspects of the +implementation more difficult. ## License -This code is released under the terms of the LGPLv3, but with an added exception that was shamelessly taken from [godeb](https://github.com/niemeyer/godeb). -Under certain circumstances, this exception permits distribution of binaries that are (statically or dynamically) linked with this code, without requiring the distribution of Minimal Corresponding Source or Minimal Application Code. +This code is released under the terms of the LGPLv3, but with an added exception +that was shamelessly taken from [godeb](https://github.com/niemeyer/godeb). +Under certain circumstances, this exception permits distribution of binaries +that are (statically or dynamically) linked with this code, without requiring +the distribution of Minimal Corresponding Source or Minimal Application Code. For more details, see: [LICENSE](LICENSE). diff --git a/doc/yggdrasil-network.github.io b/doc/yggdrasil-network.github.io new file mode 160000 index 00000000..10672210 --- /dev/null +++ b/doc/yggdrasil-network.github.io @@ -0,0 +1 @@ +Subproject commit 10672210f2fdce97dd5c301dfeed47284d4a28f2 From 7478c8ba2b583a275af49c9b0801702d53d1c3a6 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 13 Mar 2019 20:08:50 +0000 Subject: [PATCH 59/62] Update README.md --- README.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a9a9d968..8c1f1684 100644 --- a/README.md +++ b/README.md @@ -55,23 +55,27 @@ specifying the `GOOS` and `GOARCH` environment variables, e.g. `GOOS=windows ## Running +### Generate configuration + To generate static configuration, either generate a HJSON file (human-friendly, complete with comments): ``` -./yggdrasil -genconf /path/to/yggdrasil.conf +./yggdrasil -genconf > /path/to/yggdrasil.conf ``` ... or generate a plain JSON file (which is easy to manipulate programmatically): ``` -./yggdrasil -genconf /path/to/yggdrasil.conf -json +./yggdrasil -genconf -json > /path/to/yggdrasil.conf ``` You will need to edit the `yggdrasil.conf` file to add or remove peers, modify other configuration such as listen addresses or multicast addresses, etc. +### Run Yggdrasil + To run with the generated static configuration: ``` ./yggdrasil -useconffile /path/to/yggdrasil.conf @@ -81,7 +85,7 @@ To run in auto-configuration mode (which will use sane defaults and random keys at each startup, instead of using a static configuration file): ``` -./yggdrasil --autoconf +./yggdrasil -autoconf ``` You will likely need to run Yggdrasil as a privileged user or under `sudo`, @@ -90,8 +94,8 @@ by giving the Yggdrasil binary the `CAP_NET_ADMIN` capability. ## Documentation -Documentation is available on our [`GitHub -Pages`](https://yggdrasil-network.github.io) site, or in the base submodule +Documentation is available on our [GitHub +Pages](https://yggdrasil-network.github.io) site, or in the base submodule repository within `doc/yggdrasil-network.github.io`. - [Configuration file options](https://yggdrasil-network.github.io/configuration.html) From e582ac102b76e2bd2ce14dd36495b0000dd616f8 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 13 Mar 2019 20:12:08 +0000 Subject: [PATCH 60/62] Update README.md --- README.md | 25 ++----------------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 8c1f1684..7553824f 100644 --- a/README.md +++ b/README.md @@ -103,30 +103,9 @@ repository within `doc/yggdrasil-network.github.io`. - [Frequently asked questions](https://yggdrasil-network.github.io/faq.html) - [Admin API documentation](https://yggdrasil-network.github.io/admin.html) -## Performance +## Community -A [simplified model](misc/sim/treesim-forward.py) of this routing scheme has -been tested in simulation on the 9204-node -[skitter](https://www.caida.org/tools/measurement/skitter/) network topology -dataset from [caida](https://www.caida.org/), and compared with results in -[arxiv:0708.2309](https://arxiv.org/abs/0708.2309). Using the routing scheme as -implemented in this code, the average multiplicative stretch is observed to be -about 1.08, with an average routing table size of 6 for a name-dependent scheme, -and approximately 30 additional (but smaller) entries needed for the -name-independent routing table. The number of name-dependent routing table -entries needed is proportional to node degree, so that 6 is the mean of a -distribution with a long tail, but this may be an acceptable tradeoff (it's at -least worth trying, hence this code). The size of name-dependent routing table -entries is relatively large, due to cryptographic signatures associated with -routing table updates, but in the absence of cryptographic overhead, each entry -should otherwise be comparable in size to the BC routing scheme described in the -above paper. A modified version of this scheme, with the same resource -requirements, achieves a multiplicative stretch of 1.02, which drops to 1.01 if -source routing is used. Both of these optimizations are not present in the -current implementation, as the former depends on network state information that -appears difficult to cryptographically secure, and the latter optimization is -both tedious to implement and would make debugging other aspects of the -implementation more difficult. +Feel free to join us on our [Matrix channel](https://matrix.to/#/#yggdrasil:matrix.org) at `#yggdrasil:matrix.org` or in the `#yggdrasil` IRC channel on Freenode. ## License From b57030430cfaa71b37ea933ce80027ffbb7bb250 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 13 Mar 2019 20:21:01 +0000 Subject: [PATCH 61/62] Update README.md --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7553824f..e9e0c7d9 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,9 @@ Please see our [Platforms](https://yggdrasil-network.github.io/) pages for more specific information about each of our supported platforms, including installation steps and caveats. +You may also find other platform-specific wrappers, scripts or tools in the +`contrib` folder. + ## Building If you want to build from source, as opposed to installing one of the pre-built @@ -102,10 +105,13 @@ repository within `doc/yggdrasil-network.github.io`. - [Platform-specific documentation](https://yggdrasil-network.github.io/platforms.html) - [Frequently asked questions](https://yggdrasil-network.github.io/faq.html) - [Admin API documentation](https://yggdrasil-network.github.io/admin.html) +- [Version changelog](CHANGELOG.md) ## Community -Feel free to join us on our [Matrix channel](https://matrix.to/#/#yggdrasil:matrix.org) at `#yggdrasil:matrix.org` or in the `#yggdrasil` IRC channel on Freenode. +Feel free to join us on our [Matrix +channel](https://matrix.to/#/#yggdrasil:matrix.org) at `#yggdrasil:matrix.org` +or in the `#yggdrasil` IRC channel on Freenode. ## License From 3f824ee99c9381ca139a6725d9fbda3ca03ced58 Mon Sep 17 00:00:00 2001 From: Paul Spooren Date: Tue, 19 Mar 2019 15:54:49 +0100 Subject: [PATCH 62/62] README: add OpenWrt as supported platform --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e9e0c7d9..3f07a2b2 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,7 @@ some of the below: - FreeBSD - OpenBSD - NetBSD +- OpenWrt Please see our [Platforms](https://yggdrasil-network.github.io/) pages for more specific information about each of our supported platforms, including