From d96fb27ab8adef666c5b0a1d2ce94e5f9bc58922 Mon Sep 17 00:00:00 2001 From: Aleksander Mistewicz Date: Wed, 25 Sep 2019 15:58:19 +0200 Subject: [PATCH 01/18] Add simple connection test --- src/yggdrasil/core_test.go | 47 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/yggdrasil/core_test.go diff --git a/src/yggdrasil/core_test.go b/src/yggdrasil/core_test.go new file mode 100644 index 0000000..4b283d4 --- /dev/null +++ b/src/yggdrasil/core_test.go @@ -0,0 +1,47 @@ +package yggdrasil + +import ( + "os" + "testing" + + "github.com/gologme/log" + + "github.com/yggdrasil-network/yggdrasil-go/src/config" +) + +// GenerateConfig is modification +func GenerateConfig() *config.NodeConfig { + cfg := config.GenerateConfig() + cfg.AdminListen = "none" + cfg.Listen = []string{"tcp://127.0.0.1:0"} + cfg.IfName = "none" + + return cfg +} + +func GetLoggerWithPrefix(prefix string) *log.Logger { + l := log.New(os.Stderr, prefix, log.Flags()) + l.EnableLevel("info") + l.EnableLevel("warn") + l.EnableLevel("error") + return l +} + +func TestCore_Start(t *testing.T) { + nodeA := Core{} + _, err := nodeA.Start(GenerateConfig(), GetLoggerWithPrefix("A: ")) + if err != nil { + t.Fatal(err) + } + + nodeB := Core{} + _, err = nodeB.Start(GenerateConfig(), GetLoggerWithPrefix("B: ")) + if err != nil { + t.Fatal(err) + } + + err = nodeB.AddPeer("tcp://"+nodeA.link.tcp.getAddr().String(), "") + if err != nil { + t.Fatal(err) + } +} From fffbbbcbd32923dc1219af4f7dfaeb664ca223de Mon Sep 17 00:00:00 2001 From: Aleksander Mistewicz Date: Wed, 25 Sep 2019 20:07:36 +0200 Subject: [PATCH 02/18] Pass message between nodes --- src/yggdrasil/core_test.go | 85 +++++++++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/src/yggdrasil/core_test.go b/src/yggdrasil/core_test.go index 4b283d4..5bca94f 100644 --- a/src/yggdrasil/core_test.go +++ b/src/yggdrasil/core_test.go @@ -1,8 +1,11 @@ package yggdrasil import ( + "bytes" + "math/rand" "os" "testing" + "time" "github.com/gologme/log" @@ -27,7 +30,7 @@ func GetLoggerWithPrefix(prefix string) *log.Logger { return l } -func TestCore_Start(t *testing.T) { +func CreateAndConnectTwo(t *testing.T) (*Core, *Core) { nodeA := Core{} _, err := nodeA.Start(GenerateConfig(), GetLoggerWithPrefix("A: ")) if err != nil { @@ -44,4 +47,84 @@ func TestCore_Start(t *testing.T) { if err != nil { t.Fatal(err) } + + if l := len(nodeA.GetPeers()); l != 1 { + t.Fatal("unexpected number of peers", l) + } + if l := len(nodeB.GetPeers()); l != 1 { + t.Fatal("unexpected number of peers", l) + } + + return &nodeA, &nodeB +} + +func TestCore_Start_Connect(t *testing.T) { + CreateAndConnectTwo(t) +} + +func TestCore_Start_Transfer(t *testing.T) { + nodeA, nodeB := CreateAndConnectTwo(t) + + // Listen + listener, err := nodeA.ConnListen() + if err != nil { + t.Fatal(err) + } + defer listener.Close() + + done := make(chan struct{}) + go func() { + conn, err := listener.Accept() + if err != nil { + t.Error(err) + return + } + defer conn.Close() + buf := make([]byte, 64) + n, err := conn.Read(buf) + if err != nil { + t.Error(err) + return + } + if n != 64 { + t.Error("missing data") + return + } + _, err = conn.Write(buf) + if err != nil { + t.Error(err) + } + done <- struct{}{} + }() + + time.Sleep(3 * time.Second) // FIXME + // Dial + dialer, err := nodeB.ConnDialer() + if err != nil { + t.Fatal(err) + } + t.Log(nodeA.GetSwitchPeers()) + t.Log(nodeB.GetSwitchPeers()) + t.Log(nodeA.GetSessions()) + t.Log(nodeB.GetSessions()) + conn, err := dialer.Dial("nodeid", nodeA.NodeID().String()) + if err != nil { + t.Fatal(err) + } + defer conn.Close() + msg := make([]byte, 64) + rand.Read(msg) + conn.Write(msg) + if err != nil { + t.Fatal(err) + } + buf := make([]byte, 64) + _, err = conn.Read(buf) + if err != nil { + t.Fatal(err) + } + if bytes.Compare(msg, buf) != 0 { + t.Fatal("expected echo") + } + <-done } From 8677a042cf7d079a41c25f2d85e610c3b76f14cc Mon Sep 17 00:00:00 2001 From: Aleksander Mistewicz Date: Sat, 28 Sep 2019 14:10:17 +0200 Subject: [PATCH 03/18] Wait for nodes to negotiate --- src/yggdrasil/core_test.go | 44 +++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/src/yggdrasil/core_test.go b/src/yggdrasil/core_test.go index 5bca94f..0c70aaf 100644 --- a/src/yggdrasil/core_test.go +++ b/src/yggdrasil/core_test.go @@ -58,35 +58,45 @@ func CreateAndConnectTwo(t *testing.T) (*Core, *Core) { return &nodeA, &nodeB } +// WaitConnected blocks until either nodes negotiated DHT or 5 seconds passed. +func WaitConnected(nodeA, nodeB *Core) bool { + // It may take up to 3 seconds, but let's wait 5. + for i := 0; i < 50; i++ { + time.Sleep(100 * time.Millisecond) + if len(nodeA.GetSwitchPeers()) > 0 && len(nodeB.GetSwitchPeers()) > 0 { + return true + } + } + return false +} + func TestCore_Start_Connect(t *testing.T) { CreateAndConnectTwo(t) } -func TestCore_Start_Transfer(t *testing.T) { - nodeA, nodeB := CreateAndConnectTwo(t) - +func CreateEchoListener(t *testing.T, nodeA *Core, bufLen int) chan struct{} { // Listen listener, err := nodeA.ConnListen() if err != nil { t.Fatal(err) } - defer listener.Close() done := make(chan struct{}) go func() { + defer listener.Close() conn, err := listener.Accept() if err != nil { t.Error(err) return } defer conn.Close() - buf := make([]byte, 64) + buf := make([]byte, bufLen) n, err := conn.Read(buf) if err != nil { t.Error(err) return } - if n != 64 { + if n != bufLen { t.Error("missing data") return } @@ -97,28 +107,36 @@ func TestCore_Start_Transfer(t *testing.T) { done <- struct{}{} }() - time.Sleep(3 * time.Second) // FIXME + return done +} + +func TestCore_Start_Transfer(t *testing.T) { + nodeA, nodeB := CreateAndConnectTwo(t) + + msgLen := 1500 + done := CreateEchoListener(t, nodeA, msgLen) + + if !WaitConnected(nodeA, nodeB) { + t.Fatal("nodes did not connect") + } + // Dial dialer, err := nodeB.ConnDialer() if err != nil { t.Fatal(err) } - t.Log(nodeA.GetSwitchPeers()) - t.Log(nodeB.GetSwitchPeers()) - t.Log(nodeA.GetSessions()) - t.Log(nodeB.GetSessions()) conn, err := dialer.Dial("nodeid", nodeA.NodeID().String()) if err != nil { t.Fatal(err) } defer conn.Close() - msg := make([]byte, 64) + msg := make([]byte, msgLen) rand.Read(msg) conn.Write(msg) if err != nil { t.Fatal(err) } - buf := make([]byte, 64) + buf := make([]byte, msgLen) _, err = conn.Read(buf) if err != nil { t.Fatal(err) From 21b236771b31499590ed704b93902e87b88b33a0 Mon Sep 17 00:00:00 2001 From: Aleksander Mistewicz Date: Sat, 28 Sep 2019 14:20:57 +0200 Subject: [PATCH 04/18] Add a simple transfer benchmark --- src/yggdrasil/core_test.go | 73 ++++++++++++++++++++++++++++++-------- 1 file changed, 58 insertions(+), 15 deletions(-) diff --git a/src/yggdrasil/core_test.go b/src/yggdrasil/core_test.go index 0c70aaf..aaf600a 100644 --- a/src/yggdrasil/core_test.go +++ b/src/yggdrasil/core_test.go @@ -30,7 +30,7 @@ func GetLoggerWithPrefix(prefix string) *log.Logger { return l } -func CreateAndConnectTwo(t *testing.T) (*Core, *Core) { +func CreateAndConnectTwo(t testing.TB) (*Core, *Core) { nodeA := Core{} _, err := nodeA.Start(GenerateConfig(), GetLoggerWithPrefix("A: ")) if err != nil { @@ -74,7 +74,7 @@ func TestCore_Start_Connect(t *testing.T) { CreateAndConnectTwo(t) } -func CreateEchoListener(t *testing.T, nodeA *Core, bufLen int) chan struct{} { +func CreateEchoListener(t testing.TB, nodeA *Core, bufLen int, repeats int) chan struct{} { // Listen listener, err := nodeA.ConnListen() if err != nil { @@ -91,18 +91,21 @@ func CreateEchoListener(t *testing.T, nodeA *Core, bufLen int) chan struct{} { } defer conn.Close() buf := make([]byte, bufLen) - n, err := conn.Read(buf) - if err != nil { - t.Error(err) - return - } - if n != bufLen { - t.Error("missing data") - return - } - _, err = conn.Write(buf) - if err != nil { - t.Error(err) + + for i := 0; i < repeats; i++ { + n, err := conn.Read(buf) + if err != nil { + t.Error(err) + return + } + if n != bufLen { + t.Error("missing data") + return + } + _, err = conn.Write(buf) + if err != nil { + t.Error(err) + } } done <- struct{}{} }() @@ -114,7 +117,7 @@ func TestCore_Start_Transfer(t *testing.T) { nodeA, nodeB := CreateAndConnectTwo(t) msgLen := 1500 - done := CreateEchoListener(t, nodeA, msgLen) + done := CreateEchoListener(t, nodeA, msgLen, 1) if !WaitConnected(nodeA, nodeB) { t.Fatal("nodes did not connect") @@ -146,3 +149,43 @@ func TestCore_Start_Transfer(t *testing.T) { } <-done } + +func BenchmarkCore_Start_Transfer(b *testing.B) { + nodeA, nodeB := CreateAndConnectTwo(b) + + msgLen := 1500 // typical MTU + done := CreateEchoListener(b, nodeA, msgLen, b.N) + + if !WaitConnected(nodeA, nodeB) { + b.Fatal("nodes did not connect") + } + + // Dial + dialer, err := nodeB.ConnDialer() + if err != nil { + b.Fatal(err) + } + conn, err := dialer.Dial("nodeid", nodeA.NodeID().String()) + if err != nil { + b.Fatal(err) + } + defer conn.Close() + msg := make([]byte, msgLen) + rand.Read(msg) + buf := make([]byte, msgLen) + + b.SetBytes(int64(b.N * msgLen)) + b.ResetTimer() + + for i := 0; i < b.N; i++ { + conn.Write(msg) + if err != nil { + b.Fatal(err) + } + _, err = conn.Read(buf) + if err != nil { + b.Fatal(err) + } + } + <-done +} From 805376609209d543f92b2bf165f5f668f230c16e Mon Sep 17 00:00:00 2001 From: Aleksander Mistewicz Date: Sat, 28 Sep 2019 14:24:54 +0200 Subject: [PATCH 05/18] Add verbosity setting --- src/yggdrasil/core_test.go | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/yggdrasil/core_test.go b/src/yggdrasil/core_test.go index aaf600a..2823495 100644 --- a/src/yggdrasil/core_test.go +++ b/src/yggdrasil/core_test.go @@ -22,23 +22,26 @@ func GenerateConfig() *config.NodeConfig { return cfg } -func GetLoggerWithPrefix(prefix string) *log.Logger { +func GetLoggerWithPrefix(prefix string, verbose bool) *log.Logger { l := log.New(os.Stderr, prefix, log.Flags()) + if !verbose { + return l + } l.EnableLevel("info") l.EnableLevel("warn") l.EnableLevel("error") return l } -func CreateAndConnectTwo(t testing.TB) (*Core, *Core) { +func CreateAndConnectTwo(t testing.TB, verbose bool) (*Core, *Core) { nodeA := Core{} - _, err := nodeA.Start(GenerateConfig(), GetLoggerWithPrefix("A: ")) + _, err := nodeA.Start(GenerateConfig(), GetLoggerWithPrefix("A: ", verbose)) if err != nil { t.Fatal(err) } nodeB := Core{} - _, err = nodeB.Start(GenerateConfig(), GetLoggerWithPrefix("B: ")) + _, err = nodeB.Start(GenerateConfig(), GetLoggerWithPrefix("B: ", verbose)) if err != nil { t.Fatal(err) } @@ -71,7 +74,7 @@ func WaitConnected(nodeA, nodeB *Core) bool { } func TestCore_Start_Connect(t *testing.T) { - CreateAndConnectTwo(t) + CreateAndConnectTwo(t, true) } func CreateEchoListener(t testing.TB, nodeA *Core, bufLen int, repeats int) chan struct{} { @@ -114,7 +117,7 @@ func CreateEchoListener(t testing.TB, nodeA *Core, bufLen int, repeats int) chan } func TestCore_Start_Transfer(t *testing.T) { - nodeA, nodeB := CreateAndConnectTwo(t) + nodeA, nodeB := CreateAndConnectTwo(t, true) msgLen := 1500 done := CreateEchoListener(t, nodeA, msgLen, 1) @@ -151,7 +154,7 @@ func TestCore_Start_Transfer(t *testing.T) { } func BenchmarkCore_Start_Transfer(b *testing.B) { - nodeA, nodeB := CreateAndConnectTwo(b) + nodeA, nodeB := CreateAndConnectTwo(b, false) msgLen := 1500 // typical MTU done := CreateEchoListener(b, nodeA, msgLen, b.N) From 783959208cf879a1ab0f5cf8c3566aeecc911954 Mon Sep 17 00:00:00 2001 From: Aleksander Mistewicz Date: Sat, 28 Sep 2019 14:41:53 +0200 Subject: [PATCH 06/18] Add more comments to explain helper functions --- src/yggdrasil/core_test.go | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/yggdrasil/core_test.go b/src/yggdrasil/core_test.go index 2823495..364ed0b 100644 --- a/src/yggdrasil/core_test.go +++ b/src/yggdrasil/core_test.go @@ -12,7 +12,7 @@ import ( "github.com/yggdrasil-network/yggdrasil-go/src/config" ) -// GenerateConfig is modification +// GenerateConfig produces default configuration with suitable modifications for tests. func GenerateConfig() *config.NodeConfig { cfg := config.GenerateConfig() cfg.AdminListen = "none" @@ -22,6 +22,8 @@ func GenerateConfig() *config.NodeConfig { return cfg } +// GetLoggerWithPrefix creates a new logger instance wih prefix. +// If verbose is set to true, three log levels are enabled: "info", "warn", "error". func GetLoggerWithPrefix(prefix string, verbose bool) *log.Logger { l := log.New(os.Stderr, prefix, log.Flags()) if !verbose { @@ -33,14 +35,16 @@ func GetLoggerWithPrefix(prefix string, verbose bool) *log.Logger { return l } -func CreateAndConnectTwo(t testing.TB, verbose bool) (*Core, *Core) { - nodeA := Core{} +// CreateAndConnectTwo creates two nodes. nodeB connects to nodeA. +// Verbosity flag is passed to logger. +func CreateAndConnectTwo(t testing.TB, verbose bool) (nodeA *Core, nodeB *Core) { + nodeA = new(Core) _, err := nodeA.Start(GenerateConfig(), GetLoggerWithPrefix("A: ", verbose)) if err != nil { t.Fatal(err) } - nodeB := Core{} + nodeB = new(Core) _, err = nodeB.Start(GenerateConfig(), GetLoggerWithPrefix("B: ", verbose)) if err != nil { t.Fatal(err) @@ -58,7 +62,7 @@ func CreateAndConnectTwo(t testing.TB, verbose bool) (*Core, *Core) { t.Fatal("unexpected number of peers", l) } - return &nodeA, &nodeB + return nodeA, nodeB } // WaitConnected blocks until either nodes negotiated DHT or 5 seconds passed. @@ -73,17 +77,16 @@ func WaitConnected(nodeA, nodeB *Core) bool { return false } -func TestCore_Start_Connect(t *testing.T) { - CreateAndConnectTwo(t, true) -} - +// CreateEchoListener creates a routine listening on nodeA. It expects repeats messages of length bufLen. +// It returns a channel used to synchronize the routine with caller. func CreateEchoListener(t testing.TB, nodeA *Core, bufLen int, repeats int) chan struct{} { - // Listen + // Listen. Doing it here guarantees that there will be something to try to connect when it returns. listener, err := nodeA.ConnListen() if err != nil { t.Fatal(err) } + // Start routine done := make(chan struct{}) go func() { defer listener.Close() @@ -116,6 +119,12 @@ func CreateEchoListener(t testing.TB, nodeA *Core, bufLen int, repeats int) chan return done } +// TestCore_Start_Connect checks if two nodes can connect together. +func TestCore_Start_Connect(t *testing.T) { + CreateAndConnectTwo(t, true) +} + +// TestCore_Start_Transfer checks that messages can be passed between nodes (in both directions). func TestCore_Start_Transfer(t *testing.T) { nodeA, nodeB := CreateAndConnectTwo(t, true) @@ -153,6 +162,7 @@ func TestCore_Start_Transfer(t *testing.T) { <-done } +// BenchmarkCore_Start_Transfer estimates the possible transfer between nodes (in MB/s). func BenchmarkCore_Start_Transfer(b *testing.B) { nodeA, nodeB := CreateAndConnectTwo(b, false) From 045a24d74ed06f6aa658a131f3980fb8e60938b5 Mon Sep 17 00:00:00 2001 From: Arano-kai Date: Wed, 2 Oct 2019 00:36:33 +0300 Subject: [PATCH 07/18] Systemd: tun module and capabilities - Enable (and limit to) capabilities that require to setup tun/tap interface. - Ensure that tun module is active. --- contrib/systemd/yggdrasil.service | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contrib/systemd/yggdrasil.service b/contrib/systemd/yggdrasil.service index 37859e7..0223dd9 100644 --- a/contrib/systemd/yggdrasil.service +++ b/contrib/systemd/yggdrasil.service @@ -8,6 +8,8 @@ Group=yggdrasil ProtectHome=true ProtectSystem=true SyslogIdentifier=yggdrasil +CapabilityBoundSet=CAP_NET_ADMIN +ExecStartPre=+/sbin/modprobe tun ExecStartPre=/bin/sh -ec "if ! test -s /etc/yggdrasil.conf; \ then umask 077; \ yggdrasil -genconf > /etc/yggdrasil.conf; \ From b2922189b83f2a335aa02b70467f20908da06fd6 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Thu, 3 Oct 2019 18:44:47 -0500 Subject: [PATCH 08/18] fix deadlock from use of phony.Block by actors when ckr is enabled --- src/tuntap/conn.go | 2 +- src/yggdrasil/conn.go | 20 ++++++++------------ 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/tuntap/conn.go b/src/tuntap/conn.go index 31875d9..6db46b2 100644 --- a/src/tuntap/conn.go +++ b/src/tuntap/conn.go @@ -31,7 +31,7 @@ func (s *tunConn) close() { } func (s *tunConn) _close_from_tun() { - s.conn.Close() + go s.conn.Close() // Just in case it blocks on actor operations delete(s.tun.addrToConn, s.addr) delete(s.tun.subnetToConn, s.snet) func() { diff --git a/src/yggdrasil/conn.go b/src/yggdrasil/conn.go index eb2bb74..9da2ad8 100644 --- a/src/yggdrasil/conn.go +++ b/src/yggdrasil/conn.go @@ -96,7 +96,7 @@ func (c *Conn) setMTU(from phony.Actor, mtu uint16) { c.Act(from, func() { c.mtu = mtu }) } -// This should never be called from the router goroutine, used in the dial functions +// This should never be called from an actor, used in the dial functions func (c *Conn) search() error { var err error done := make(chan struct{}) @@ -118,6 +118,10 @@ func (c *Conn) search() error { sinfo.setConn(nil, c) } c.session = sinfo + c.nodeID = crypto.GetNodeID(&c.session.theirPermPub) + for i := range c.nodeMask { + c.nodeMask[i] = 0xFF + } err = e close(done) } @@ -133,12 +137,6 @@ func (c *Conn) search() error { if c.session == nil && err == nil { panic("search failed but returned no error") } - if c.session != nil { - c.nodeID = crypto.GetNodeID(&c.session.theirPermPub) - for i := range c.nodeMask { - c.nodeMask[i] = 0xFF - } - } return err } @@ -262,7 +260,7 @@ func (c *Conn) _write(msg FlowKeyMessage) error { c.session.Act(c, func() { // Send the packet c.session._send(msg) - // Session keep-alive, while we wait for the crypto workers from send + // Session keep-alive, while we wait for the crypto workers from sefnd switch { case time.Since(c.session.time) > 6*time.Second: if c.session.time.Before(c.session.pingTime) && time.Since(c.session.pingTime) > 6*time.Second { @@ -353,10 +351,8 @@ func (c *Conn) LocalAddr() crypto.NodeID { // RemoteAddr returns the complete node ID of the remote side of the connection. func (c *Conn) RemoteAddr() crypto.NodeID { - // TODO warn that this can block while waiting for the Conn actor to run, so don't call it from other actors... - var n crypto.NodeID - phony.Block(c, func() { n = *c.nodeID }) - return n + // RemoteAddr is set during the dial or accept, and isn't changed, so it's safe to access directly + return *c.nodeID } // SetDeadline is equivalent to calling both SetReadDeadline and From f22eac497b9c8673a9a13b6d4480ff5b0fa43303 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Thu, 3 Oct 2019 18:50:33 -0500 Subject: [PATCH 09/18] typo --- src/yggdrasil/conn.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yggdrasil/conn.go b/src/yggdrasil/conn.go index 9da2ad8..0a0fc83 100644 --- a/src/yggdrasil/conn.go +++ b/src/yggdrasil/conn.go @@ -260,7 +260,7 @@ func (c *Conn) _write(msg FlowKeyMessage) error { c.session.Act(c, func() { // Send the packet c.session._send(msg) - // Session keep-alive, while we wait for the crypto workers from sefnd + // Session keep-alive, while we wait for the crypto workers from send switch { case time.Since(c.session.time) > 6*time.Second: if c.session.time.Before(c.session.pingTime) && time.Since(c.session.pingTime) > 6*time.Second { From 8e22d7137a9d46ce48ede098414b425c842e78b3 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sat, 5 Oct 2019 10:47:15 -0500 Subject: [PATCH 10/18] use bbr congestion control on linux, note that we're not doing anything intelligent with the errors right now if setting it fails --- src/yggdrasil/tcp_linux.go | 28 ++++++++++++++++++++++++++++ src/yggdrasil/tcp_other.go | 2 +- 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 src/yggdrasil/tcp_linux.go diff --git a/src/yggdrasil/tcp_linux.go b/src/yggdrasil/tcp_linux.go new file mode 100644 index 0000000..d957ceb --- /dev/null +++ b/src/yggdrasil/tcp_linux.go @@ -0,0 +1,28 @@ +// +build linux + +package yggdrasil + +import ( + "syscall" + + "golang.org/x/sys/unix" +) + +// WARNING: This context is used both by net.Dialer and net.Listen in tcp.go + +func (t *tcp) tcpContext(network, address string, c syscall.RawConn) error { + var control error + var bbr error + + control = c.Control(func(fd uintptr) { + // sys/socket.h: #define SO_RECV_ANYIF 0x1104 + bbr = unix.SetsockoptString(int(fd), unix.IPPROTO_TCP, unix.TCP_CONGESTION, "bbr") + }) + + switch { + case bbr != nil: + return bbr + default: + return control + } +} diff --git a/src/yggdrasil/tcp_other.go b/src/yggdrasil/tcp_other.go index 47bd772..44c3d76 100644 --- a/src/yggdrasil/tcp_other.go +++ b/src/yggdrasil/tcp_other.go @@ -1,4 +1,4 @@ -// +build !darwin +// +build !darwin,!linux package yggdrasil From fb3430207c686fa71e903e70dba36ae278b7331d Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sat, 5 Oct 2019 11:03:38 -0500 Subject: [PATCH 11/18] don't fail if there's an error setting bbr, just log it and continue --- src/yggdrasil/tcp_linux.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/yggdrasil/tcp_linux.go b/src/yggdrasil/tcp_linux.go index d957ceb..90a55d7 100644 --- a/src/yggdrasil/tcp_linux.go +++ b/src/yggdrasil/tcp_linux.go @@ -19,10 +19,14 @@ func (t *tcp) tcpContext(network, address string, c syscall.RawConn) error { bbr = unix.SetsockoptString(int(fd), unix.IPPROTO_TCP, unix.TCP_CONGESTION, "bbr") }) - switch { - case bbr != nil: - return bbr - default: - return control + // Log any errors + if bbr != nil { + t.link.core.log.Debugln("Failed to set tcp_congestion_control to bbr for socket, SetsockoptString error:", bbr) } + if control != nil { + t.link.core.log.Debugln("Failed to set tcp_congestion_control to bbr for socket, Control error:", control) + } + + // Return nil because errors here are not considered fatal for the connection, it just means congestion control is suboptimal + return nil } From b519802fcb5959983932376309f8f2000b84489d Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sat, 5 Oct 2019 12:16:22 -0500 Subject: [PATCH 12/18] update phony dependency --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d86101b..185a9e3 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,7 @@ module github.com/yggdrasil-network/yggdrasil-go require ( - github.com/Arceliar/phony v0.0.0-20190907031509-af5bdbeecab6 + github.com/Arceliar/phony v0.0.0-20191004004458-c7ba8368bafa github.com/gologme/log v0.0.0-20181207131047-4e5d8ccb38e8 github.com/hashicorp/go-syslog v1.0.0 github.com/hjson/hjson-go v3.0.1-0.20190209023717-9147687966d9+incompatible diff --git a/go.sum b/go.sum index cdabc40..8a2b52f 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Arceliar/phony v0.0.0-20190907031509-af5bdbeecab6 h1:zMj5Q1V0yF4WNfV/FpXG6iXfPJ965Xc5asR2vHXanXc= -github.com/Arceliar/phony v0.0.0-20190907031509-af5bdbeecab6/go.mod h1:6Lkn+/zJilRMsKmbmG1RPoamiArC6HS73xbwRyp3UyI= +github.com/Arceliar/phony v0.0.0-20191004004458-c7ba8368bafa h1:bFoWgQ17NKP4FBB2Tnt1QZ8fZqrxLYORIlHUa5ioDjc= +github.com/Arceliar/phony v0.0.0-20191004004458-c7ba8368bafa/go.mod h1:6Lkn+/zJilRMsKmbmG1RPoamiArC6HS73xbwRyp3UyI= github.com/gologme/log v0.0.0-20181207131047-4e5d8ccb38e8 h1:WD8iJ37bRNwvETMfVTusVSAi0WdXTpfNVGY2aHycNKY= github.com/gologme/log v0.0.0-20181207131047-4e5d8ccb38e8/go.mod h1:gq31gQ8wEHkR+WekdWsqDuf8pXTUZA9BnnzTuPz1Y9U= github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= From f474869ad9ab094fcd625d6c6ee3634bbc07bc52 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sat, 5 Oct 2019 12:17:40 -0500 Subject: [PATCH 13/18] cleanup bad comment --- src/yggdrasil/tcp_linux.go | 1 - 1 file changed, 1 deletion(-) diff --git a/src/yggdrasil/tcp_linux.go b/src/yggdrasil/tcp_linux.go index 90a55d7..7eda3b5 100644 --- a/src/yggdrasil/tcp_linux.go +++ b/src/yggdrasil/tcp_linux.go @@ -15,7 +15,6 @@ func (t *tcp) tcpContext(network, address string, c syscall.RawConn) error { var bbr error control = c.Control(func(fd uintptr) { - // sys/socket.h: #define SO_RECV_ANYIF 0x1104 bbr = unix.SetsockoptString(int(fd), unix.IPPROTO_TCP, unix.TCP_CONGESTION, "bbr") }) From c38e40e8e3930dae7bf29fabceaf46fb4aeba5f7 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sat, 5 Oct 2019 12:23:21 -0500 Subject: [PATCH 14/18] actually use doCancel in writeNoCopy --- src/yggdrasil/conn.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/yggdrasil/conn.go b/src/yggdrasil/conn.go index 0a0fc83..4daf6a4 100644 --- a/src/yggdrasil/conn.go +++ b/src/yggdrasil/conn.go @@ -292,6 +292,9 @@ func (c *Conn) writeNoCopy(msg FlowKeyMessage) error { var cancel util.Cancellation var doCancel bool phony.Block(c, func() { cancel, doCancel = c._getDeadlineCancellation(c.writeDeadline) }) + if doCancel { + defer cancel.Cancel(nil) + } var err error select { case <-cancel.Finished(): From 7f8dfe84cf461ca307bf8ccd5aef404003819d03 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sat, 5 Oct 2019 13:19:17 -0500 Subject: [PATCH 15/18] fix race in phony --- go.mod | 2 +- go.sum | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 185a9e3..83c2292 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,7 @@ module github.com/yggdrasil-network/yggdrasil-go require ( - github.com/Arceliar/phony v0.0.0-20191004004458-c7ba8368bafa + github.com/Arceliar/phony v0.0.0-20191005181740-21679e75e3f0 github.com/gologme/log v0.0.0-20181207131047-4e5d8ccb38e8 github.com/hashicorp/go-syslog v1.0.0 github.com/hjson/hjson-go v3.0.1-0.20190209023717-9147687966d9+incompatible diff --git a/go.sum b/go.sum index 8a2b52f..bca7c23 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Arceliar/phony v0.0.0-20191004004458-c7ba8368bafa h1:bFoWgQ17NKP4FBB2Tnt1QZ8fZqrxLYORIlHUa5ioDjc= -github.com/Arceliar/phony v0.0.0-20191004004458-c7ba8368bafa/go.mod h1:6Lkn+/zJilRMsKmbmG1RPoamiArC6HS73xbwRyp3UyI= +github.com/Arceliar/phony v0.0.0-20191005181740-21679e75e3f0 h1:IOFsvAMFkgnKfSQHxXTeqb1+ODFeR5px1HCHU86KF30= +github.com/Arceliar/phony v0.0.0-20191005181740-21679e75e3f0/go.mod h1:6Lkn+/zJilRMsKmbmG1RPoamiArC6HS73xbwRyp3UyI= github.com/gologme/log v0.0.0-20181207131047-4e5d8ccb38e8 h1:WD8iJ37bRNwvETMfVTusVSAi0WdXTpfNVGY2aHycNKY= github.com/gologme/log v0.0.0-20181207131047-4e5d8ccb38e8/go.mod h1:gq31gQ8wEHkR+WekdWsqDuf8pXTUZA9BnnzTuPz1Y9U= github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= @@ -31,4 +31,5 @@ golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e h1:FDhOuMEY4JVRztM/gsbk+IKUQ8kj74bxZrgw87eMMVc= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= From a1c413f76944ab76977bc650a1de0ee254c9174f Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sun, 6 Oct 2019 11:53:14 -0500 Subject: [PATCH 16/18] fix nil pointer dereference in yggdrasil.Conn.search --- src/yggdrasil/conn.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/yggdrasil/conn.go b/src/yggdrasil/conn.go index 4daf6a4..bb5964b 100644 --- a/src/yggdrasil/conn.go +++ b/src/yggdrasil/conn.go @@ -115,12 +115,12 @@ func (c *Conn) search() error { default: if sinfo != nil { // Finish initializing the session - sinfo.setConn(nil, c) - } - c.session = sinfo - c.nodeID = crypto.GetNodeID(&c.session.theirPermPub) - for i := range c.nodeMask { - c.nodeMask[i] = 0xFF + c.session = sinfo + c.session.setConn(nil, c) + c.nodeID = crypto.GetNodeID(&c.session.theirPermPub) + for i := range c.nodeMask { + c.nodeMask[i] = 0xFF + } } err = e close(done) From 92b1bbf08d2ea64959cf24be7aeb40313c4a956c Mon Sep 17 00:00:00 2001 From: Arceliar Date: Tue, 8 Oct 2019 20:32:41 -0500 Subject: [PATCH 17/18] draft of changelog --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fdac254..c926bfa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,17 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - in case of vulnerabilities. --> +## [0.3.10] - 2019-10-10 +### Added +- The core library now includes several unit tests for peering and `yggdrasil.Conn` connections + +### Changed +- On recent linux kernels, Yggdrasil will now set the `tcp_congestion_control` algorithm used for its own TCP sockets to [BBR](https://github.com/google/bbr), which reduces latency under load +- The systemd init configuration now attemps to load the `tun` module, in case tun/tap support is available but not enabled, and it limit Yggdrasil to `CAP_NET_ADMIN` to access the tun/tap, rather than letting it do whatever the (typically `root`) user can do + +### Fixed +- The `yggdrasil.Conn.RemoteAddr()` function no longer blocks, fixing a deadlock when CKR is used while under heavy load + ## [0.3.9] - 2019-09-27 ### Added - Yggdrasil will now complain more verbosely when a peer URI is incorrectly formatted From 14245b88fe46e5d07dc8bdc1fed1ba19c097e4f5 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 9 Oct 2019 21:40:54 +0100 Subject: [PATCH 18/18] Pedantic grammar stuff in changelog --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c926bfa..bbf0bfa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,8 +30,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - The core library now includes several unit tests for peering and `yggdrasil.Conn` connections ### Changed -- On recent linux kernels, Yggdrasil will now set the `tcp_congestion_control` algorithm used for its own TCP sockets to [BBR](https://github.com/google/bbr), which reduces latency under load -- The systemd init configuration now attemps to load the `tun` module, in case tun/tap support is available but not enabled, and it limit Yggdrasil to `CAP_NET_ADMIN` to access the tun/tap, rather than letting it do whatever the (typically `root`) user can do +- On recent Linux kernels, Yggdrasil will now set the `tcp_congestion_control` algorithm used for its own TCP sockets to [BBR](https://github.com/google/bbr), which reduces latency under load +- The systemd service configuration in `contrib` (and, by extension, some of our packages) now attemps to load the `tun` module, in case TUN/TAP support is available but not loaded, and it restricts Yggdrasil to the `CAP_NET_ADMIN` capability for managing the TUN/TAP adapter, rather than letting it do whatever the (typically `root`) user can do ### Fixed - The `yggdrasil.Conn.RemoteAddr()` function no longer blocks, fixing a deadlock when CKR is used while under heavy load