From bcacfb06385ab4177ffabe4e3aa979d3540b6428 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Fri, 25 Oct 2019 18:33:23 -0500 Subject: [PATCH 01/23] test adding BindToDevice to linux. if it works then we'll want to rethink slightly how we get the tcpContext on every platform, to make this compile everywhere and look a little cleaner --- src/yggdrasil/tcp.go | 1 + src/yggdrasil/tcp_linux.go | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index 7d5b80b..0733b58 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -299,6 +299,7 @@ func (t *tcp) call(saddr string, options interface{}, sintf string, upgrade *Tcp Timeout: time.Second * 5, } if sintf != "" { + dialer.Control = t.getContextWithBindToDevice(sintf) ief, err := net.InterfaceByName(sintf) if err != nil { return diff --git a/src/yggdrasil/tcp_linux.go b/src/yggdrasil/tcp_linux.go index 7eda3b5..8d36f04 100644 --- a/src/yggdrasil/tcp_linux.go +++ b/src/yggdrasil/tcp_linux.go @@ -29,3 +29,17 @@ func (t *tcp) tcpContext(network, address string, c syscall.RawConn) error { // Return nil because errors here are not considered fatal for the connection, it just means congestion control is suboptimal return nil } + +func (t *tcp) getContextWithBindToDevice(sintf string) func(string, string, syscall.RawConn) error { + return func(network, address string, c syscall.RawConn) error { + var err error + btd := func(fd uintptr) { + err = unix.BindToDevice(int(fd), sintf) + } + c.Control(btd) + if err != nil { + t.link.core.log.Debugln("Failed to set SO_BINDTODEVICE:", sintf) + } + return t.tcpContext(network, address, c) + } +} From cfc1e6b83d56585bd984fd71ed4f077bdf079d9a Mon Sep 17 00:00:00 2001 From: Arceliar Date: Fri, 25 Oct 2019 18:40:09 -0500 Subject: [PATCH 02/23] fix a crash when shutting down if no multicast interfaces are configured --- src/multicast/multicast.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/multicast/multicast.go b/src/multicast/multicast.go index 206edab..4e0b4f3 100644 --- a/src/multicast/multicast.go +++ b/src/multicast/multicast.go @@ -124,7 +124,9 @@ func (m *Multicast) _stop() error { if m.platformhandler != nil { m.platformhandler.Stop() } - m.sock.Close() + if m.sock != nil { + m.sock.Close() + } return nil } From 710815fed595af7abeac82ca088577e287d231d4 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Fri, 25 Oct 2019 19:32:53 -0500 Subject: [PATCH 03/23] add dummy functions for other platforms --- src/yggdrasil/tcp.go | 2 +- src/yggdrasil/tcp_darwin.go | 4 ++++ src/yggdrasil/tcp_linux.go | 2 +- src/yggdrasil/tcp_other.go | 4 ++++ 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index 0733b58..9cca419 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -299,7 +299,7 @@ func (t *tcp) call(saddr string, options interface{}, sintf string, upgrade *Tcp Timeout: time.Second * 5, } if sintf != "" { - dialer.Control = t.getContextWithBindToDevice(sintf) + dialer.Control = t.getControl(sintf) ief, err := net.InterfaceByName(sintf) if err != nil { return diff --git a/src/yggdrasil/tcp_darwin.go b/src/yggdrasil/tcp_darwin.go index 9d55a1d..3d0626c 100644 --- a/src/yggdrasil/tcp_darwin.go +++ b/src/yggdrasil/tcp_darwin.go @@ -26,3 +26,7 @@ func (t *tcp) tcpContext(network, address string, c syscall.RawConn) error { return control } } + +func (t *tcp) getControl(sintf string) func(string, string, syscall.RawConn) error { + return t.tcpContext +} diff --git a/src/yggdrasil/tcp_linux.go b/src/yggdrasil/tcp_linux.go index 8d36f04..9ec3c10 100644 --- a/src/yggdrasil/tcp_linux.go +++ b/src/yggdrasil/tcp_linux.go @@ -30,7 +30,7 @@ func (t *tcp) tcpContext(network, address string, c syscall.RawConn) error { return nil } -func (t *tcp) getContextWithBindToDevice(sintf string) func(string, string, syscall.RawConn) error { +func (t *tcp) getControl(sintf string) func(string, string, syscall.RawConn) error { return func(network, address string, c syscall.RawConn) error { var err error btd := func(fd uintptr) { diff --git a/src/yggdrasil/tcp_other.go b/src/yggdrasil/tcp_other.go index 44c3d76..7ee4197 100644 --- a/src/yggdrasil/tcp_other.go +++ b/src/yggdrasil/tcp_other.go @@ -11,3 +11,7 @@ import ( func (t *tcp) tcpContext(network, address string, c syscall.RawConn) error { return nil } + +func (t *tcp) getControl(sintf string) func(string, string, syscall.RawConn) error { + return t.tcpContext +} From 6d3aefb8257f52f5e2d075464efcc5f39ec75244 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sun, 27 Oct 2019 19:55:35 -0500 Subject: [PATCH 04/23] fix a data race when an existing session's coords are updated in response to a successful search --- src/yggdrasil/search.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yggdrasil/search.go b/src/yggdrasil/search.go index caa8df7..f52dcbe 100644 --- a/src/yggdrasil/search.go +++ b/src/yggdrasil/search.go @@ -192,7 +192,7 @@ func (sinfo *searchInfo) checkDHTRes(res *dhtRes) bool { finishSearch := func(sess *sessionInfo, err error) { if sess != nil { // FIXME (!) replay attacks could mess with coords? Give it a handle (tstamp)? - sess.coords = res.Coords + sess.Act(sinfo.searches.router, func() { sess.coords = res.Coords }) sess.ping(sinfo.searches.router) } if err != nil { From 74d824302b2dacd04b3d41ef41e0ab0902b504ae Mon Sep 17 00:00:00 2001 From: Arano-kai Date: Tue, 29 Oct 2019 16:36:03 +0200 Subject: [PATCH 05/23] FIX: Systemd: typo in directive --- contrib/systemd/yggdrasil.service | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/systemd/yggdrasil.service b/contrib/systemd/yggdrasil.service index b48ff78..bdf365c 100644 --- a/contrib/systemd/yggdrasil.service +++ b/contrib/systemd/yggdrasil.service @@ -8,7 +8,7 @@ Group=yggdrasil ProtectHome=true ProtectSystem=true SyslogIdentifier=yggdrasil -CapabilityBoundSet=CAP_NET_ADMIN +CapabilityBoundingSet=CAP_NET_ADMIN ExecStartPre=+-/sbin/modprobe tun ExecStartPre=/bin/sh -ec "if ! test -s /etc/yggdrasil.conf; \ then umask 077; \ From e3a5e4f3b79c934ab20b2aebe40e839a172c60af Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 10 Nov 2019 19:38:35 +0000 Subject: [PATCH 06/23] Add -address and -subnet flag for getting address/subnet out of config --- cmd/yggdrasil/main.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 91cea9a..6158fd9 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -2,11 +2,13 @@ package main import ( "bytes" + "crypto/sha512" "encoding/hex" "encoding/json" "flag" "fmt" "io/ioutil" + "net" "os" "os/signal" "strings" @@ -20,6 +22,7 @@ import ( "github.com/kardianos/minwinsvc" "github.com/mitchellh/mapstructure" + "github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/admin" "github.com/yggdrasil-network/yggdrasil-go/src/config" "github.com/yggdrasil-network/yggdrasil-go/src/crypto" @@ -142,6 +145,8 @@ func main() { ver := flag.Bool("version", false, "prints the version of this build") logging := flag.String("logging", "info,warn,error", "comma-separated list of logging levels to enable") logto := flag.String("logto", "stdout", "file path to log to, \"syslog\" or \"stdout\"") + getaddr := flag.Bool("address", false, "returns the IPv6 address as derived from the supplied configuration") + getsnet := flag.Bool("subnet", false, "returns the IPv6 subnet as derived from the supplied configuration") flag.Parse() var cfg *config.NodeConfig @@ -188,6 +193,26 @@ func main() { if cfg == nil { return } + // Have we been asked for the node address yet? If so, print it and then stop. + switch { + case *getaddr: + if pubkey, err := hex.DecodeString(cfg.EncryptionPublicKey); err == nil { + nodeid := sha512.Sum512(pubkey) + fromnodeid := address.AddrForNodeID((*crypto.NodeID)(&nodeid)) + fmt.Println(net.IP(fromnodeid[:]).String()) + } + return + case *getsnet: + if pubkey, err := hex.DecodeString(cfg.EncryptionPublicKey); err == nil { + nodeid := sha512.Sum512(pubkey) + fromnodeid := address.SubnetForNodeID((*crypto.NodeID)(&nodeid)) + subnet := append(fromnodeid[:], 0, 0, 0, 0, 0, 0, 0, 0) + ipnet := net.IPNet{IP: subnet, Mask: net.CIDRMask(64, 128)} + fmt.Println(ipnet.String()) + } + return + default: + } // Create a new logger that logs output to stdout. var logger *log.Logger switch *logto { From 49ba5bae171d8d7078979e261832fccea669d784 Mon Sep 17 00:00:00 2001 From: William Fleurant Date: Mon, 11 Nov 2019 00:24:50 -0500 Subject: [PATCH 07/23] yggdrasil: buildName should report unknown --- src/version/version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version/version.go b/src/version/version.go index 4cc7a8f..09fc04e 100644 --- a/src/version/version.go +++ b/src/version/version.go @@ -7,7 +7,7 @@ var buildVersion string // from git, or returns "unknown" otherwise. func BuildName() string { if buildName == "" { - return "yggdrasilctl" + return "unknown" } return buildName } From e310a25e5987a2400bbdbb765b14bb1967ca02e2 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 11 Nov 2019 09:40:25 +0000 Subject: [PATCH 08/23] Use crypto.GetNodeID instead of sha512 directly --- cmd/yggdrasil/main.go | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 6158fd9..981c6ef 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -2,7 +2,6 @@ package main import ( "bytes" - "crypto/sha512" "encoding/hex" "encoding/json" "flag" @@ -194,20 +193,29 @@ func main() { return } // Have we been asked for the node address yet? If so, print it and then stop. + getNodeID := func() *crypto.NodeID { + if pubkey, err := hex.DecodeString(cfg.EncryptionPublicKey); err == nil { + var box crypto.BoxPubKey + copy(box[:], pubkey[:]) + return crypto.GetNodeID(&box) + } + return nil + } switch { case *getaddr: - if pubkey, err := hex.DecodeString(cfg.EncryptionPublicKey); err == nil { - nodeid := sha512.Sum512(pubkey) - fromnodeid := address.AddrForNodeID((*crypto.NodeID)(&nodeid)) - fmt.Println(net.IP(fromnodeid[:]).String()) + if nodeid := getNodeID(); nodeid != nil { + addr := *address.AddrForNodeID(nodeid) + ip := net.IP(addr[:]) + fmt.Println(ip.String()) } return case *getsnet: - if pubkey, err := hex.DecodeString(cfg.EncryptionPublicKey); err == nil { - nodeid := sha512.Sum512(pubkey) - fromnodeid := address.SubnetForNodeID((*crypto.NodeID)(&nodeid)) - subnet := append(fromnodeid[:], 0, 0, 0, 0, 0, 0, 0, 0) - ipnet := net.IPNet{IP: subnet, Mask: net.CIDRMask(64, 128)} + if nodeid := getNodeID(); nodeid != nil { + snet := *address.SubnetForNodeID(nodeid) + ipnet := net.IPNet{ + IP: append(snet[:], 0, 0, 0, 0, 0, 0, 0, 0), + Mask: net.CIDRMask(len(snet)*8, 128), + } fmt.Println(ipnet.String()) } return From 5f1aea3636003a282467d7fc01afe39dd9f018eb Mon Sep 17 00:00:00 2001 From: Arceliar Date: Tue, 12 Nov 2019 21:01:32 -0600 Subject: [PATCH 09/23] fix deadlock when AddPeer fails --- src/yggdrasil/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yggdrasil/api.go b/src/yggdrasil/api.go index 80f669b..6dd70b8 100644 --- a/src/yggdrasil/api.go +++ b/src/yggdrasil/api.go @@ -424,6 +424,7 @@ func (c *Core) AddPeer(addr string, sintf string) error { return err } c.config.Mutex.Lock() + defer c.config.Mutex.Unlock() if sintf == "" { for _, peer := range c.config.Current.Peers { if peer == addr { @@ -445,7 +446,6 @@ func (c *Core) AddPeer(addr string, sintf string) error { c.config.Current.InterfacePeers[sintf] = append(c.config.Current.InterfacePeers[sintf], addr) } } - c.config.Mutex.Unlock() return nil } From f49d9de421e98bed3eedeabacf2055ba65ed8ae9 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 19 Nov 2019 14:20:11 +0000 Subject: [PATCH 10/23] Fix setting up of MTU when value is outside of acceptable bounds, also account for ethernet headers in calculations, notify about clipping to stdout --- src/tuntap/tun.go | 30 ++++++++++++++++++++++-------- src/tuntap/tun_bsd.go | 2 +- src/tuntap/tun_darwin.go | 5 +++-- src/tuntap/tun_linux.go | 2 +- src/tuntap/tun_other.go | 4 ++-- src/tuntap/tun_windows.go | 5 +++-- 6 files changed, 32 insertions(+), 16 deletions(-) diff --git a/src/tuntap/tun.go b/src/tuntap/tun.go index f0250c9..ffee63b 100644 --- a/src/tuntap/tun.go +++ b/src/tuntap/tun.go @@ -63,9 +63,12 @@ type TunOptions struct { // Gets the maximum supported MTU for the platform based on the defaults in // defaults.GetDefaults(). -func getSupportedMTU(mtu int) int { - if mtu > defaults.GetDefaults().MaximumIfMTU { - return defaults.GetDefaults().MaximumIfMTU +func getSupportedMTU(mtu int, istapmode bool) int { + if mtu < 1280 { + return 1280 + } + if mtu > MaximumMTU(istapmode) { + return MaximumMTU(istapmode) } return mtu } @@ -80,7 +83,7 @@ func (tun *TunAdapter) Name() string { // the maximum value is determined by your platform. The returned value will // never exceed that of MaximumMTU(). func (tun *TunAdapter) MTU() int { - return getSupportedMTU(tun.mtu) + return getSupportedMTU(tun.mtu, tun.IsTAP()) } // IsTAP returns true if the adapter is a TAP adapter (Layer 2) or false if it @@ -97,7 +100,11 @@ func DefaultName() string { // DefaultMTU gets the default TUN/TAP interface MTU for your platform. This can // be as high as MaximumMTU(), depending on platform, but is never lower than 1280. func DefaultMTU() int { - return defaults.GetDefaults().DefaultIfMTU + ehbytes := 0 + if DefaultIsTAP() { + ehbytes = 14 + } + return defaults.GetDefaults().DefaultIfMTU - ehbytes } // DefaultIsTAP returns true if the default adapter mode for the current @@ -109,8 +116,12 @@ func DefaultIsTAP() bool { // MaximumMTU returns the maximum supported TUN/TAP interface MTU for your // platform. This can be as high as 65535, depending on platform, but is never // lower than 1280. -func MaximumMTU() int { - return defaults.GetDefaults().MaximumIfMTU +func MaximumMTU(iftapmode bool) int { + ehbytes := 0 + if iftapmode { + ehbytes = 14 + } + return defaults.GetDefaults().MaximumIfMTU - ehbytes } // Init initialises the TUN/TAP module. You must have acquired a Listener from @@ -167,6 +178,9 @@ func (tun *TunAdapter) _start() error { if err := tun.setup(ifname, iftapmode, addr, tun.mtu); err != nil { return err } + if tun.MTU() != current.IfMTU { + tun.log.Warnf("Warning: Interface MTU %d automatically adjusted to %d (supported range is 1280-%d)", current.IfMTU, tun.MTU(), MaximumMTU(tun.IsTAP())) + } } if ifname == "none" || ifname == "dummy" { tun.log.Debugln("Not starting TUN/TAP as ifname is none or dummy") @@ -176,7 +190,7 @@ func (tun *TunAdapter) _start() error { go tun.handler() tun.reader.Act(nil, tun.reader._read) // Start the reader tun.icmpv6.Init(tun) - if iftapmode { + if tun.IsTAP() { go tun.icmpv6.Solicit(tun.addr) } tun.ckr.init(tun) diff --git a/src/tuntap/tun_bsd.go b/src/tuntap/tun_bsd.go index 996f314..78a4ada 100644 --- a/src/tuntap/tun_bsd.go +++ b/src/tuntap/tun_bsd.go @@ -99,7 +99,7 @@ func (tun *TunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int panic(err) } tun.iface = iface - tun.mtu = getSupportedMTU(mtu) + tun.mtu = getSupportedMTU(mtu, iftapmode) return tun.setupAddress(addr) } diff --git a/src/tuntap/tun_darwin.go b/src/tuntap/tun_darwin.go index d7b4653..ab6f34e 100644 --- a/src/tuntap/tun_darwin.go +++ b/src/tuntap/tun_darwin.go @@ -18,7 +18,8 @@ import ( // Configures the "utun" adapter with the correct IPv6 address and MTU. func (tun *TunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error { if iftapmode { - tun.log.Warnln("TAP mode is not supported on this platform, defaulting to TUN") + tun.log.Warnln("Warning: TAP mode is not supported on this platform, defaulting to TUN") + iftapmode = false } config := water.Config{DeviceType: water.TUN} iface, err := water.New(config) @@ -26,7 +27,7 @@ func (tun *TunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int panic(err) } tun.iface = iface - tun.mtu = getSupportedMTU(mtu) + tun.mtu = getSupportedMTU(mtu, iftapmode) return tun.setupAddress(addr) } diff --git a/src/tuntap/tun_linux.go b/src/tuntap/tun_linux.go index 764e56b..d3e9c85 100644 --- a/src/tuntap/tun_linux.go +++ b/src/tuntap/tun_linux.go @@ -26,7 +26,7 @@ func (tun *TunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int panic(err) } tun.iface = iface - tun.mtu = getSupportedMTU(mtu) + tun.mtu = getSupportedMTU(mtu, iftapmode) // The following check is specific to Linux, as the TAP driver only supports // an MTU of 65535-14 to make room for the ethernet headers. This makes sure // that the MTU gets rounded down to 65521 instead of causing a panic. diff --git a/src/tuntap/tun_other.go b/src/tuntap/tun_other.go index 48276b4..7d4f064 100644 --- a/src/tuntap/tun_other.go +++ b/src/tuntap/tun_other.go @@ -21,13 +21,13 @@ func (tun *TunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int panic(err) } tun.iface = iface - tun.mtu = getSupportedMTU(mtu) + tun.mtu = getSupportedMTU(mtu, iftapmode) return tun.setupAddress(addr) } // We don't know how to set the IPv6 address on an unknown platform, therefore // write about it to stdout and don't try to do anything further. func (tun *TunAdapter) setupAddress(addr string) error { - tun.log.Warnln("Platform not supported, you must set the address of", tun.iface.Name(), "to", addr) + tun.log.Warnln("Warning: Platform not supported, you must set the address of", tun.iface.Name(), "to", addr) return nil } diff --git a/src/tuntap/tun_windows.go b/src/tuntap/tun_windows.go index ea1515f..d4fd1c3 100644 --- a/src/tuntap/tun_windows.go +++ b/src/tuntap/tun_windows.go @@ -17,7 +17,8 @@ import ( // delegate the hard work to "netsh". func (tun *TunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error { if !iftapmode { - tun.log.Warnln("TUN mode is not supported on this platform, defaulting to TAP") + tun.log.Warnln("Warning: TUN mode is not supported on this platform, defaulting to TAP") + iftapmode = true } config := water.Config{DeviceType: water.TAP} config.PlatformSpecificParams.ComponentID = "tap0901" @@ -60,7 +61,7 @@ func (tun *TunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int panic(err) } tun.iface = iface - tun.mtu = getSupportedMTU(mtu) + tun.mtu = getSupportedMTU(mtu, iftapmode) err = tun.setupMTU(tun.mtu) if err != nil { panic(err) From 16a487cb1dbb327b8477fdb422a095a667e25d68 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 19 Nov 2019 14:34:10 +0000 Subject: [PATCH 11/23] Move genkeys into cmd/ as this allows 'go run github.com/yggdrasil-network/yggdrasil-go/cmd/genkeys' --- misc/genkeys.go => cmd/genkeys/main.go | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename misc/genkeys.go => cmd/genkeys/main.go (100%) diff --git a/misc/genkeys.go b/cmd/genkeys/main.go similarity index 100% rename from misc/genkeys.go rename to cmd/genkeys/main.go From 4b9bce855eeb6af6b53e6927dc394c65af8bd157 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 19 Nov 2019 14:37:16 +0000 Subject: [PATCH 12/23] Only build yggdrasil/yggdrasilctl when running ./build --- build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build b/build index 7ca5f4f..68dac03 100755 --- a/build +++ b/build @@ -44,7 +44,7 @@ elif [ $ANDROID ]; then github.com/yggdrasil-network/yggdrasil-extras/src/mobile \ github.com/yggdrasil-network/yggdrasil-extras/src/dummy else - for CMD in `ls cmd/` ; do + for CMD in yggdrasil yggdrasilctl ; do echo "Building: $CMD" go build $ARGS -ldflags="$LDFLAGS" -gcflags="$GCFLAGS" ./cmd/$CMD From b70fbfa0f1e38a85d9805558fc6f735eb1938a93 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 20 Nov 2019 11:54:09 +0000 Subject: [PATCH 13/23] Update changelog for v0.3.12 --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 903ffc7..32490d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,22 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - in case of vulnerabilities. --> +## [0.3.12] - 2019-11-22 +### Added +- New command line parameters `-address` and `-subnet` for getting the address/subnet from the config file +- A warning is now produced in the Yggdrasil output when the MTU has been adjusted for some reason + +### Changed +- Outgoing `InterfacePeers` connections now use `SO_BINDTODEVICE` to prefer an outgoing interface +- The `genkeys` utility is now in `cmd` rather than `contrib` + +### Fixed +- A data race has been fixed when updating session coordinates +- A crash when shutting down when no multicast interfaces are configured has been fixed +- A deadlock when calling `AddPeer` multiple times has been fixed +- A typo in the systemd unit file (for some Linux packages) has been fixed +- The nodeinfo now reports `unknown` correctly when no build name/version is available in the environment +- The MTU calculation now correctly accounts for ethernet headers when running in TAP mode ## [0.3.11] - 2019-10-25 ### Added From ec46b217da1434f59c7e992e0428f6316f1e9f29 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 20 Nov 2019 13:41:18 +0000 Subject: [PATCH 14/23] Update CHANGELOG.md --- CHANGELOG.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32490d8..3babf8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,19 +27,19 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [0.3.12] - 2019-11-22 ### Added -- New command line parameters `-address` and `-subnet` for getting the address/subnet from the config file -- A warning is now produced in the Yggdrasil output when the MTU has been adjusted for some reason +- New command line parameters `-address` and `-subnet` for getting the address/subnet from the config file, for use with `-useconffile` or `-useconf` +- A warning is now produced in the Yggdrasil output at startup when the MTU in the config is invalid or has been adjusted for some reason ### Changed -- Outgoing `InterfacePeers` connections now use `SO_BINDTODEVICE` to prefer an outgoing interface +- On Linux, outgoing `InterfacePeers` connections now use `SO_BINDTODEVICE` to prefer an outgoing interface - The `genkeys` utility is now in `cmd` rather than `contrib` ### Fixed -- A data race has been fixed when updating session coordinates +- A data race condition has been fixed when updating session coordinates - A crash when shutting down when no multicast interfaces are configured has been fixed - A deadlock when calling `AddPeer` multiple times has been fixed - A typo in the systemd unit file (for some Linux packages) has been fixed -- The nodeinfo now reports `unknown` correctly when no build name/version is available in the environment +- The NodeInfo and admin socket now report `unknown` correctly when no build name/version is available in the environment at build time - The MTU calculation now correctly accounts for ethernet headers when running in TAP mode ## [0.3.11] - 2019-10-25 From 9fca3640f9743862c4fa1f25e3cce5b026e7c2a5 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 20 Nov 2019 22:11:52 +0000 Subject: [PATCH 15/23] Fix couple of issues with MTU calculations --- src/tuntap/tun.go | 19 +++++++------------ src/tuntap/tun_linux.go | 8 -------- 2 files changed, 7 insertions(+), 20 deletions(-) diff --git a/src/tuntap/tun.go b/src/tuntap/tun.go index ffee63b..c9a627a 100644 --- a/src/tuntap/tun.go +++ b/src/tuntap/tun.go @@ -170,22 +170,17 @@ func (tun *TunAdapter) _start() error { nodeID := crypto.GetNodeID(&boxPub) tun.addr = *address.AddrForNodeID(nodeID) tun.subnet = *address.SubnetForNodeID(nodeID) - tun.mtu = current.IfMTU - ifname := current.IfName - iftapmode := current.IfTAPMode addr := fmt.Sprintf("%s/%d", net.IP(tun.addr[:]).String(), 8*len(address.GetPrefix())-1) - if ifname != "none" { - if err := tun.setup(ifname, iftapmode, addr, tun.mtu); err != nil { - return err - } - if tun.MTU() != current.IfMTU { - tun.log.Warnf("Warning: Interface MTU %d automatically adjusted to %d (supported range is 1280-%d)", current.IfMTU, tun.MTU(), MaximumMTU(tun.IsTAP())) - } - } - if ifname == "none" || ifname == "dummy" { + if current.IfName == "none" || current.IfName == "dummy" { tun.log.Debugln("Not starting TUN/TAP as ifname is none or dummy") return nil } + if err := tun.setup(current.IfName, current.IfTAPMode, addr, current.IfMTU); err != nil { + return err + } + if tun.MTU() != current.IfMTU { + tun.log.Warnf("Warning: Interface MTU %d automatically adjusted to %d (supported range is 1280-%d)", current.IfMTU, tun.MTU(), MaximumMTU(tun.IsTAP())) + } tun.isOpen = true go tun.handler() tun.reader.Act(nil, tun.reader._read) // Start the reader diff --git a/src/tuntap/tun_linux.go b/src/tuntap/tun_linux.go index d3e9c85..b591832 100644 --- a/src/tuntap/tun_linux.go +++ b/src/tuntap/tun_linux.go @@ -27,14 +27,6 @@ func (tun *TunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int } tun.iface = iface tun.mtu = getSupportedMTU(mtu, iftapmode) - // The following check is specific to Linux, as the TAP driver only supports - // an MTU of 65535-14 to make room for the ethernet headers. This makes sure - // that the MTU gets rounded down to 65521 instead of causing a panic. - if iftapmode { - if tun.mtu > 65535-tun_ETHER_HEADER_LENGTH { - tun.mtu = 65535 - tun_ETHER_HEADER_LENGTH - } - } // Friendly output tun.log.Infof("Interface name: %s", tun.iface.Name()) tun.log.Infof("Interface IPv6: %s", addr) From d06c40ad19dca10fa0c106be3cffe88ec1f0ea41 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 20 Nov 2019 22:40:48 +0000 Subject: [PATCH 16/23] Use existing constant --- src/tuntap/tun.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tuntap/tun.go b/src/tuntap/tun.go index c9a627a..d5bed52 100644 --- a/src/tuntap/tun.go +++ b/src/tuntap/tun.go @@ -102,7 +102,7 @@ func DefaultName() string { func DefaultMTU() int { ehbytes := 0 if DefaultIsTAP() { - ehbytes = 14 + ehbytes = tun_ETHER_HEADER_LENGTH } return defaults.GetDefaults().DefaultIfMTU - ehbytes } @@ -119,7 +119,7 @@ func DefaultIsTAP() bool { func MaximumMTU(iftapmode bool) int { ehbytes := 0 if iftapmode { - ehbytes = 14 + ehbytes = tun_ETHER_HEADER_LENGTH } return defaults.GetDefaults().MaximumIfMTU - ehbytes } From e90be6f5695ebc38a096796e049e774ec475d48e Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 21 Nov 2019 00:02:39 +0000 Subject: [PATCH 17/23] Add API functions for manipulating maximum session MTU, fix TUN/TAP to use that --- src/tuntap/tun.go | 3 +++ src/yggdrasil/api.go | 16 ++++++++++++++++ src/yggdrasil/session.go | 8 +++++--- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/tuntap/tun.go b/src/tuntap/tun.go index d5bed52..6feb533 100644 --- a/src/tuntap/tun.go +++ b/src/tuntap/tun.go @@ -35,6 +35,7 @@ const tun_ETHER_HEADER_LENGTH = 14 // you should pass this object to the yggdrasil.SetRouterAdapter() function // before calling yggdrasil.Start(). type TunAdapter struct { + core *yggdrasil.Core writer tunWriter reader tunReader config *config.NodeState @@ -131,6 +132,7 @@ func (tun *TunAdapter) Init(core *yggdrasil.Core, config *config.NodeState, log if !ok { return fmt.Errorf("invalid options supplied to TunAdapter module") } + tun.core = core tun.config = config tun.log = log tun.listener = tunoptions.Listener @@ -181,6 +183,7 @@ func (tun *TunAdapter) _start() error { if tun.MTU() != current.IfMTU { tun.log.Warnf("Warning: Interface MTU %d automatically adjusted to %d (supported range is 1280-%d)", current.IfMTU, tun.MTU(), MaximumMTU(tun.IsTAP())) } + tun.core.SetMaximumSessionMTU(uint16(tun.MTU())) tun.isOpen = true go tun.handler() tun.reader.Act(nil, tun.reader._read) // Start the reader diff --git a/src/yggdrasil/api.go b/src/yggdrasil/api.go index 6dd70b8..59cc831 100644 --- a/src/yggdrasil/api.go +++ b/src/yggdrasil/api.go @@ -363,6 +363,22 @@ func (c *Core) SetNodeInfo(nodeinfo interface{}, nodeinfoprivacy bool) { c.router.nodeinfo.setNodeInfo(nodeinfo, nodeinfoprivacy) } +// GetMaximumSessionMTU returns the maximum allowed session MTU size. +func (c *Core) GetMaximumSessionMTU(mtu uint16) uint16 { + return c.router.sessions.myMaximumMTU +} + +// SetMaximumSessionMTU sets the maximum allowed session MTU size. The return +// value contains the actual set value, since Yggdrasil will not accept MTUs +// below 1280 bytes. The default value is 65535 bytes. +func (c *Core) SetMaximumSessionMTU(mtu uint16) uint16 { + if mtu < 1280 { + mtu = 1280 + } + c.router.sessions.myMaximumMTU = mtu + return mtu +} + // GetNodeInfo requests nodeinfo from a remote node, as specified by the public // key and coordinates specified. The third parameter specifies whether a cached // result is acceptable - this results in less traffic being generated than is diff --git a/src/yggdrasil/session.go b/src/yggdrasil/session.go index 2f5e0af..b287377 100644 --- a/src/yggdrasil/session.go +++ b/src/yggdrasil/session.go @@ -121,6 +121,7 @@ type sessions struct { lastCleanup time.Time isAllowedHandler func(pubkey *crypto.BoxPubKey, initiator bool) bool // Returns true or false if session setup is allowed isAllowedMutex sync.RWMutex // Protects the above + myMaximumMTU uint16 // Maximum allowed session MTU permShared map[crypto.BoxPubKey]*crypto.BoxSharedKey // Maps known permanent keys to their shared key, used by DHT a lot sinfos map[crypto.Handle]*sessionInfo // Maps handle onto session info byTheirPerm map[crypto.BoxPubKey]*crypto.Handle // Maps theirPermPub onto handle @@ -133,6 +134,7 @@ func (ss *sessions) init(r *router) { ss.sinfos = make(map[crypto.Handle]*sessionInfo) ss.byTheirPerm = make(map[crypto.BoxPubKey]*crypto.Handle) ss.lastCleanup = time.Now() + ss.myMaximumMTU = 65535 } func (ss *sessions) reconfigure() { @@ -187,9 +189,9 @@ func (ss *sessions) createSession(theirPermKey *crypto.BoxPubKey) *sessionInfo { sinfo.mySesPriv = *priv sinfo.myNonce = *crypto.NewBoxNonce() sinfo.theirMTU = 1280 - ss.router.core.config.Mutex.RLock() - sinfo.myMTU = uint16(ss.router.core.config.Current.IfMTU) - ss.router.core.config.Mutex.RUnlock() + // TODO: sinfo.myMTU becomes unnecessary if we always have a reference to the + // sessions struct so let's check if that is the case + sinfo.myMTU = ss.myMaximumMTU now := time.Now() sinfo.timeOpened = now sinfo.time = now From d1c445dc41837d5f3c85d37acc8d693a9f4df279 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 21 Nov 2019 09:28:36 +0000 Subject: [PATCH 18/23] Thread safety for MTU API functions --- src/yggdrasil/api.go | 21 +++++++++++---------- src/yggdrasil/session.go | 2 -- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/yggdrasil/api.go b/src/yggdrasil/api.go index 59cc831..e6b32e6 100644 --- a/src/yggdrasil/api.go +++ b/src/yggdrasil/api.go @@ -365,18 +365,19 @@ func (c *Core) SetNodeInfo(nodeinfo interface{}, nodeinfoprivacy bool) { // GetMaximumSessionMTU returns the maximum allowed session MTU size. func (c *Core) GetMaximumSessionMTU(mtu uint16) uint16 { - return c.router.sessions.myMaximumMTU + mtu := 0 + phony.Block(c.router, func() { + mtu = c.router.sessions.myMaximumMTU + }) + return mtu } -// SetMaximumSessionMTU sets the maximum allowed session MTU size. The return -// value contains the actual set value, since Yggdrasil will not accept MTUs -// below 1280 bytes. The default value is 65535 bytes. -func (c *Core) SetMaximumSessionMTU(mtu uint16) uint16 { - if mtu < 1280 { - mtu = 1280 - } - c.router.sessions.myMaximumMTU = mtu - return mtu +// SetMaximumSessionMTU sets the maximum allowed session MTU size. The default +// value is 65535 bytes. +func (c *Core) SetMaximumSessionMTU(mtu uint16) { + phony.Block(c.router, func() { + c.router.sessions.myMaximumMTU = mtu + }) } // GetNodeInfo requests nodeinfo from a remote node, as specified by the public diff --git a/src/yggdrasil/session.go b/src/yggdrasil/session.go index b287377..78b0a3b 100644 --- a/src/yggdrasil/session.go +++ b/src/yggdrasil/session.go @@ -189,8 +189,6 @@ func (ss *sessions) createSession(theirPermKey *crypto.BoxPubKey) *sessionInfo { sinfo.mySesPriv = *priv sinfo.myNonce = *crypto.NewBoxNonce() sinfo.theirMTU = 1280 - // TODO: sinfo.myMTU becomes unnecessary if we always have a reference to the - // sessions struct so let's check if that is the case sinfo.myMTU = ss.myMaximumMTU now := time.Now() sinfo.timeOpened = now From 7c18c6806d0d0a61b4eb7895471339c1e65e5f99 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 21 Nov 2019 09:54:36 +0000 Subject: [PATCH 19/23] Further updates, notify sessions about updated MTU from API call --- src/tuntap/tun.go | 6 ++++++ src/yggdrasil/api.go | 16 ++++++++++------ src/yggdrasil/session.go | 13 ++++++------- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/tuntap/tun.go b/src/tuntap/tun.go index 6feb533..1e994ea 100644 --- a/src/tuntap/tun.go +++ b/src/tuntap/tun.go @@ -233,6 +233,12 @@ func (tun *TunAdapter) UpdateConfig(config *config.NodeConfig) { // Replace the active configuration with the supplied one tun.config.Replace(*config) + // If the MTU has changed in the TUN/TAP module then this is where we would + // tell the router so that updated session pings can be sent. However, we + // don't currently update the MTU of the adapter once it has been created so + // this doesn't actually happen in the real world yet. + // tun.core.SetMaximumSessionMTU(...) + // Notify children about the configuration change tun.Act(nil, tun.ckr.configure) } diff --git a/src/yggdrasil/api.go b/src/yggdrasil/api.go index e6b32e6..7f82c26 100644 --- a/src/yggdrasil/api.go +++ b/src/yggdrasil/api.go @@ -364,19 +364,23 @@ func (c *Core) SetNodeInfo(nodeinfo interface{}, nodeinfoprivacy bool) { } // GetMaximumSessionMTU returns the maximum allowed session MTU size. -func (c *Core) GetMaximumSessionMTU(mtu uint16) uint16 { - mtu := 0 - phony.Block(c.router, func() { +func (c *Core) GetMaximumSessionMTU() uint16 { + var mtu uint16 + phony.Block(&c.router, func() { mtu = c.router.sessions.myMaximumMTU }) return mtu } // SetMaximumSessionMTU sets the maximum allowed session MTU size. The default -// value is 65535 bytes. +// value is 65535 bytes. Session pings will be sent to update all open sessions +// if the MTU has changed. func (c *Core) SetMaximumSessionMTU(mtu uint16) { - phony.Block(c.router, func() { - c.router.sessions.myMaximumMTU = mtu + phony.Block(&c.router, func() { + if c.router.sessions.myMaximumMTU != mtu { + c.router.sessions.myMaximumMTU = mtu + c.router.sessions.reconfigure() + } }) } diff --git a/src/yggdrasil/session.go b/src/yggdrasil/session.go index 78b0a3b..c203fe2 100644 --- a/src/yggdrasil/session.go +++ b/src/yggdrasil/session.go @@ -55,10 +55,6 @@ type sessionInfo struct { callbacks []chan func() // Finished work from crypto workers } -func (sinfo *sessionInfo) reconfigure() { - // This is where reconfiguration would go, if we had anything to do -} - // Represents a session ping/pong packet, andincludes information like public keys, a session handle, coords, a timestamp to prevent replays, and the tun/tap MTU. type sessionPing struct { SendPermPub crypto.BoxPubKey // Sender's permanent key @@ -138,9 +134,12 @@ func (ss *sessions) init(r *router) { } func (ss *sessions) reconfigure() { - for _, session := range ss.sinfos { - session.reconfigure() - } + ss.router.Act(nil, func() { + for _, session := range ss.sinfos { + session.myMTU = ss.myMaximumMTU + session.ping(ss.router) + } + }) } // Determines whether the session with a given publickey is allowed based on From d3a2087e0fc0c449b8f5b37fca537c5b67ab83cd Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 21 Nov 2019 10:02:18 +0000 Subject: [PATCH 20/23] Update changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3babf8f..eb5c439 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,7 +27,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [0.3.12] - 2019-11-22 ### Added -- New command line parameters `-address` and `-subnet` for getting the address/subnet from the config file, for use with `-useconffile` or `-useconf` +- New API functions `SetMaximumSessionMTU` and `GetMaximumSessionMTU` +- New command line parameters `-address` and `-subnet` for getting the address/subnet from the config file, for use with `-useconffile` or `-useconf` - A warning is now produced in the Yggdrasil output at startup when the MTU in the config is invalid or has been adjusted for some reason ### Changed From 248a08b2f1362389e48901c6725796c4eabd5986 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Thu, 21 Nov 2019 19:23:44 -0600 Subject: [PATCH 21/23] send a message to the sessions to update mtu instead of trying to update it directly --- src/yggdrasil/session.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/yggdrasil/session.go b/src/yggdrasil/session.go index c203fe2..743dd7a 100644 --- a/src/yggdrasil/session.go +++ b/src/yggdrasil/session.go @@ -136,7 +136,10 @@ func (ss *sessions) init(r *router) { func (ss *sessions) reconfigure() { ss.router.Act(nil, func() { for _, session := range ss.sinfos { - session.myMTU = ss.myMaximumMTU + sinfo, mtu := session, ss.myMaximumMTU + sinfo.Act(ss.router, func() { + sinfo.myMTU = mtu + }) session.ping(ss.router) } }) From 117d44d008a7390939e66f40c86ad34fb6fef10f Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sat, 23 Nov 2019 15:47:08 -0600 Subject: [PATCH 22/23] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eb5c439..51d67dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,7 +33,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Changed - On Linux, outgoing `InterfacePeers` connections now use `SO_BINDTODEVICE` to prefer an outgoing interface -- The `genkeys` utility is now in `cmd` rather than `contrib` +- The `genkeys` utility is now in `cmd` rather than `misc` ### Fixed - A data race condition has been fixed when updating session coordinates From ebef3045e2395a56c8d9615fe338744e8233e2b5 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 24 Nov 2019 09:44:52 +0000 Subject: [PATCH 23/23] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 51d67dc..a511c52 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.12] - 2019-11-22 +## [0.3.12] - 2019-11-24 ### Added - New API functions `SetMaximumSessionMTU` and `GetMaximumSessionMTU` - New command line parameters `-address` and `-subnet` for getting the address/subnet from the config file, for use with `-useconffile` or `-useconf`