From 9e5f90d0e4e08eec4c32dc17fc261db93cbf452d Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 4 Nov 2018 19:15:53 +0000 Subject: [PATCH 001/109] Add neilalexander's logo proposal --- contrib/logo/ygg-neilalexander.svg | 62 ++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 contrib/logo/ygg-neilalexander.svg diff --git a/contrib/logo/ygg-neilalexander.svg b/contrib/logo/ygg-neilalexander.svg new file mode 100644 index 0000000..7f28138 --- /dev/null +++ b/contrib/logo/ygg-neilalexander.svg @@ -0,0 +1,62 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + From 953ad0ef5979ca862b0eb57dbc375b6bc7bdaf5d Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 14 Nov 2018 19:25:35 +0000 Subject: [PATCH 002/109] Update neilalexander's logo proposal --- contrib/logo/ygg-neilalexander.svg | 151 +++++++++++++++++++++++------ 1 file changed, 123 insertions(+), 28 deletions(-) diff --git a/contrib/logo/ygg-neilalexander.svg b/contrib/logo/ygg-neilalexander.svg index 7f28138..d222200 100644 --- a/contrib/logo/ygg-neilalexander.svg +++ b/contrib/logo/ygg-neilalexander.svg @@ -9,35 +9,39 @@ xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="297mm" - height="210mm" - viewBox="0 0 1052.3622 744.09448" - id="svg4211" - version="1.1" + sodipodi:docname="drawing.svg" inkscape:version="0.91 r13725" - sodipodi:docname="ygg.svg"> + version="1.1" + id="svg4240" + viewBox="0 0 981.96461 321.60015" + height="90.762711mm" + width="277.13223mm"> + id="defs4242" /> + inkscape:window-x="0" + inkscape:window-height="1021" + inkscape:window-width="2048" + showgrid="false" + inkscape:current-layer="layer2" + inkscape:document-units="px" + inkscape:cy="13.914395" + inkscape:cx="751.6295" + inkscape:zoom="0.66468037" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + borderopacity="1.0" + bordercolor="#666666" + pagecolor="#ffffff" + id="base" /> + id="metadata4245"> @@ -52,11 +56,102 @@ inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" - transform="translate(0,-308.26772)"> + transform="translate(383.92494,-160.49328)" /> + + d="m 352.74397,478.24119 c 0.92103,-3.76903 11.87131,-30.48993 21.5083,-52.48465 9.86344,-22.51152 9.67726,-21.6278 6.92943,-32.89221 -3.42997,-14.06075 -3.22164,-36.95243 0.44688,-49.10642 13.24423,-43.87864 47.63362,-73.61698 122.30718,-105.76556 24.32504,-10.47245 37.67777,-17.18807 47.80968,-24.04538 17.86083,-12.08828 36.4402,-33.06424 42.38057,-47.84736 1.25285,-3.11781 2.66096,-5.64051 3.12912,-5.60598 1.46014,0.10767 0.73701,44.30167 -0.9768,59.69719 -10.61597,95.36545 -42.95689,157.39345 -96.20598,184.51751 -30.73114,15.65385 -79.17559,21.45357 -101.74118,12.18037 -3.19081,-1.31125 -6.5492,-2.38408 -7.46311,-2.38408 -3.43636,0 -15.75824,32.89925 -19.29523,51.51802 -1.09802,5.78003 -2.76237,13.70787 -3.00898,14.91667 -5.50064,-0.0422 -0.35371,-0.0119 -8.18026,-0.0119 l -8.29605,0 z" + id="path4918" + inkscape:connector-curvature="0" + sodipodi:nodetypes="ssssssscssssscscs" /> + + + + + + + + + + + + + + + + + + From e6a246f0408eaa688ac3d81bac6dcdac8f9a12b8 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 27 Dec 2018 20:03:46 +0000 Subject: [PATCH 003/109] Chop up contrib/semver/version.sh --- contrib/semver/version.sh | 61 +++++++++------------------------------ 1 file changed, 14 insertions(+), 47 deletions(-) diff --git a/contrib/semver/version.sh b/contrib/semver/version.sh index 964a320..37fc524 100644 --- a/contrib/semver/version.sh +++ b/contrib/semver/version.sh @@ -1,63 +1,30 @@ #!/bin/sh -# Merge commits from this branch are counted -DEVELOPBRANCH="yggdrasil-network/develop" - # Get the last tag -TAG=$(git describe --abbrev=0 --tags --match="v[0-9]*\.[0-9]*\.0" 2>/dev/null) +TAG=$(git describe --abbrev=0 --tags --match="v[0-9]*\.[0-9]*\.[0-9]*" 2>/dev/null) +test $? != 0 && (echo "unknown"; exit 1) -# Get last merge to master -MERGE=$(git rev-list $TAG..master --grep "from $DEVELOPBRANCH" 2>/dev/null | head -n 1) - -# Get the number of merges since the last merge to master -PATCH=$(git rev-list $TAG..master --count --merges --grep="from $DEVELOPBRANCH" --first-parent master 2>/dev/null) - -# Decide whether we should prepend the version with "v" - the default is that -# we do because we use it in git tags, but we might not always need it -PREPEND="v" -if [ "$1" = "--bare" ]; then - PREPEND="" -fi - -# If it fails then there's no last tag - go from the first commit -if [ $? != 0 ]; then - PATCH=$(git rev-list HEAD --count 2>/dev/null) - - # Complain if the git history is not available - if [ $? != 0 ]; then - printf 'unknown' - exit 1 - fi - - printf '%s0.0.%d' "$PREPEND" "$PATCH" - exit 1 -fi +# Get the current branch +BRANCH=$(git symbolic-ref -q HEAD --short 2>/dev/null) +test $? != 0 && BRANCH="master" # Split out into major, minor and patch numbers MAJOR=$(echo $TAG | cut -c 2- | cut -d "." -f 1) MINOR=$(echo $TAG | cut -c 2- | cut -d "." -f 2) - -# Get the current checked out branch -BRANCH=$(git rev-parse --abbrev-ref HEAD) +PATCH=$(echo $TAG | cut -c 2- | cut -d "." -f 3) # Output in the desired format -if [ $PATCH = 0 ]; then - if [ ! -z $FULL ]; then - printf '%s%d.%d.0' "$PREPEND" "$MAJOR" "$MINOR" - else - printf '%s%d.%d' "$PREPEND" "$MAJOR" "$MINOR" - fi +if [ $((PATCH)) -eq 0 ]; then + printf '%s%d.%d' "$PREPEND" "$((MAJOR))" "$((MINOR))" else - printf '%s%d.%d.%d' "$PREPEND" "$MAJOR" "$MINOR" "$PATCH" + printf '%s%d.%d.%d' "$PREPEND" "$((MAJOR))" "$((MINOR))" "$((PATCH))" fi -# Get the number of merges on the current branch since the last tag -TAG=$(git describe --abbrev=0 --tags --match="v[0-9]*\.[0-9]*\.[0-9]*" --first-parent master 2>/dev/null) -BUILD=$(git rev-list $TAG.. --count) - # Add the build tag on non-master branches -if [ $BRANCH != "master" ]; then - if [ $BUILD != 0 ]; then - printf -- "-%04d" "$BUILD" +if [ "$BRANCH" != "master" ]; then + BUILD=$(git rev-list $TAG..HEAD --count) + + if [ $? == 0 ] && [ $((BUILD)) -gt 0 ]; then + printf -- "-%04d" "$((BUILD))" fi fi From 57894541b78dc1b597103a0fd15b82d073780935 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 27 Dec 2018 21:14:23 +0000 Subject: [PATCH 004/109] Check string emptiness --- contrib/semver/version.sh | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/contrib/semver/version.sh b/contrib/semver/version.sh index 37fc524..14dafa1 100644 --- a/contrib/semver/version.sh +++ b/contrib/semver/version.sh @@ -2,11 +2,11 @@ # Get the last tag TAG=$(git describe --abbrev=0 --tags --match="v[0-9]*\.[0-9]*\.[0-9]*" 2>/dev/null) -test $? != 0 && (echo "unknown"; exit 1) +(test $? != 0 || test -z "$TAG") && (echo "unknown"; exit 1) # Get the current branch BRANCH=$(git symbolic-ref -q HEAD --short 2>/dev/null) -test $? != 0 && BRANCH="master" +(test $? != 0 || test -z "$BRANCH") && BRANCH="master" # Split out into major, minor and patch numbers MAJOR=$(echo $TAG | cut -c 2- | cut -d "." -f 1) @@ -23,8 +23,9 @@ fi # Add the build tag on non-master branches if [ "$BRANCH" != "master" ]; then BUILD=$(git rev-list $TAG..HEAD --count) - - if [ $? == 0 ] && [ $((BUILD)) -gt 0 ]; then - printf -- "-%04d" "$((BUILD))" + if [ $? = 0 ] && [ -n "$BUILD" ]; then + if [ $((BUILD)) -gt 0 ]; then + printf -- "-%04d" "$((BUILD))" + fi fi fi From 7eaee172cf22e5b54a619a2a08c233f83511224c Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 27 Dec 2018 21:22:46 +0000 Subject: [PATCH 005/109] Replace tests with ifs --- contrib/semver/version.sh | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/contrib/semver/version.sh b/contrib/semver/version.sh index 14dafa1..ca035ff 100644 --- a/contrib/semver/version.sh +++ b/contrib/semver/version.sh @@ -2,11 +2,20 @@ # Get the last tag TAG=$(git describe --abbrev=0 --tags --match="v[0-9]*\.[0-9]*\.[0-9]*" 2>/dev/null) -(test $? != 0 || test -z "$TAG") && (echo "unknown"; exit 1) + +# Did getting the tag succeed? +if [ $? != 0 ] || [ -z "$TAG" ]; then + printf "unknown" + exit 1 +fi # Get the current branch BRANCH=$(git symbolic-ref -q HEAD --short 2>/dev/null) -(test $? != 0 || test -z "$BRANCH") && BRANCH="master" + +# Did getting the branch succeed? +if [ $? != 0 ] || [ -z "$BRANCH" ]; then + BRANCH="master" +fi # Split out into major, minor and patch numbers MAJOR=$(echo $TAG | cut -c 2- | cut -d "." -f 1) @@ -22,10 +31,15 @@ fi # Add the build tag on non-master branches if [ "$BRANCH" != "master" ]; then - BUILD=$(git rev-list $TAG..HEAD --count) - if [ $? = 0 ] && [ -n "$BUILD" ]; then - if [ $((BUILD)) -gt 0 ]; then - printf -- "-%04d" "$((BUILD))" - fi + BUILD=$(git rev-list $TAG..HEAD --count 2>/dev/null) + + # Did getting the count of commits since the tag succeed? + if [ $? != 0 ] && [ -z "$BUILD" ]; then + exit 1 + fi + + # Is the build greater than zero? + if [ $((BUILD)) -gt 0 ]; then + printf -- "-%04d" "$((BUILD))" fi fi From 6fcd8a8dbdb290a045e7d54b84e59df425859837 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 27 Dec 2018 21:36:50 +0000 Subject: [PATCH 006/109] Fix incorrect check --- contrib/semver/version.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/contrib/semver/version.sh b/contrib/semver/version.sh index ca035ff..7d4f373 100644 --- a/contrib/semver/version.sh +++ b/contrib/semver/version.sh @@ -5,7 +5,7 @@ TAG=$(git describe --abbrev=0 --tags --match="v[0-9]*\.[0-9]*\.[0-9]*" 2>/dev/nu # Did getting the tag succeed? if [ $? != 0 ] || [ -z "$TAG" ]; then - printf "unknown" + printf -- "unknown" exit 1 fi @@ -34,7 +34,8 @@ if [ "$BRANCH" != "master" ]; then BUILD=$(git rev-list $TAG..HEAD --count 2>/dev/null) # Did getting the count of commits since the tag succeed? - if [ $? != 0 ] && [ -z "$BUILD" ]; then + if [ $? != 0 ] || [ -z "$BUILD" ]; then + printf -- "-unknown" exit 1 fi From 8c7b9e2f90f2b1ce8f432295599acdcb1b963e0c Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 27 Dec 2018 21:44:29 +0000 Subject: [PATCH 007/109] Add a null check to name.sh --- contrib/semver/name.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/semver/name.sh b/contrib/semver/name.sh index 935cc75..1fa2ce0 100644 --- a/contrib/semver/name.sh +++ b/contrib/semver/name.sh @@ -4,8 +4,8 @@ BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null) # Complain if the git history is not available -if [ $? != 0 ]; then - printf "unknown" +if [ $? != 0 ] || [ -z "$BRANCH" ]; then + printf "yggdrasil" exit 1 fi From e6e7f9377f47c5101ac20bccf9782361eeafb6e6 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 27 Dec 2018 21:45:30 +0000 Subject: [PATCH 008/109] Move --count parameter --- contrib/semver/version.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/semver/version.sh b/contrib/semver/version.sh index 7d4f373..3052094 100644 --- a/contrib/semver/version.sh +++ b/contrib/semver/version.sh @@ -31,7 +31,7 @@ fi # Add the build tag on non-master branches if [ "$BRANCH" != "master" ]; then - BUILD=$(git rev-list $TAG..HEAD --count 2>/dev/null) + BUILD=$(git rev-list --count $TAG..HEAD 2>/dev/null) # Did getting the count of commits since the tag succeed? if [ $? != 0 ] || [ -z "$BUILD" ]; then From 219fb96553a0f647036657f99fc7ed6cfbe00d18 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 29 Dec 2018 18:51:51 +0000 Subject: [PATCH 009/109] Support notifying components for config reload, listen for SIGHUP --- cmd/yggdrasil/main.go | 12 ++++++++- src/yggdrasil/admin.go | 24 ++++++++++++++--- src/yggdrasil/core.go | 53 ++++++++++++++++++++++++++++++++------ src/yggdrasil/dht.go | 23 +++++++++++++---- src/yggdrasil/multicast.go | 19 +++++++++++--- src/yggdrasil/peer.go | 13 ++++++++++ src/yggdrasil/router.go | 33 ++++++++++++++---------- src/yggdrasil/search.go | 17 ++++++++++-- src/yggdrasil/session.go | 24 +++++++++++++++++ src/yggdrasil/switch.go | 7 +++++ 10 files changed, 189 insertions(+), 36 deletions(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 2b6d2f0..e98e623 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -314,7 +314,9 @@ func main() { logger.Printf("Your IPv6 subnet is %s", subnet.String()) // Catch interrupts from the operating system to exit gracefully. c := make(chan os.Signal, 1) + r := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) + signal.Notify(r, os.Interrupt, syscall.SIGHUP) // Create a function to capture the service being stopped on Windows. winTerminate := func() { c <- os.Interrupt @@ -322,5 +324,13 @@ func main() { minwinsvc.SetOnExit(winTerminate) // Wait for the terminate/interrupt signal. Once a signal is received, the // deferred Stop function above will run which will shut down TUN/TAP. - <-c + for { + select { + case _ = <-r: + n.core.UpdateConfig(cfg) + case _ = <-c: + goto exit + } + } +exit: } diff --git a/src/yggdrasil/admin.go b/src/yggdrasil/admin.go index bd3c905..1c8c80e 100644 --- a/src/yggdrasil/admin.go +++ b/src/yggdrasil/admin.go @@ -22,10 +22,11 @@ import ( // TODO: Add authentication type admin struct { - core *Core - listenaddr string - listener net.Listener - handlers []admin_handlerInfo + core *Core + reconfigure chan bool + listenaddr string + listener net.Listener + handlers []admin_handlerInfo } type admin_info map[string]interface{} @@ -53,6 +54,21 @@ func (a *admin) addHandler(name string, args []string, handler func(admin_info) // init runs the initial admin setup. func (a *admin) init(c *Core, listenaddr string) { a.core = c + a.reconfigure = make(chan bool, 1) + go func() { + for { + select { + case _ = <-a.reconfigure: + a.core.configMutex.RLock() + a.core.log.Println("Notified: admin") + if a.core.config.AdminListen != a.core.configOld.AdminListen { + a.core.log.Println("AdminListen has changed!") + } + a.core.configMutex.RUnlock() + continue + } + } + }() a.listenaddr = listenaddr a.addHandler("list", []string{}, func(in admin_info) (admin_info, error) { handlers := make(map[string]interface{}) diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index e38274f..9e4bb62 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -7,6 +7,7 @@ import ( "log" "net" "regexp" + "sync" "github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/config" @@ -17,14 +18,26 @@ import ( var buildName string var buildVersion string +type module interface { + init(*config.NodeConfig) error + start() error +} + // The Core object represents the Yggdrasil node. You should create a Core // object for each Yggdrasil node you plan to run. type Core struct { // This is the main data structure that holds everything else for a node - boxPub crypto.BoxPubKey - boxPriv crypto.BoxPrivKey - sigPub crypto.SigPubKey - sigPriv crypto.SigPrivKey + // We're going to keep our own copy of the provided config - that way we can + // guarantee that it will be covered by the mutex + config config.NodeConfig // Active config + configOld config.NodeConfig // Previous config + configMutex sync.RWMutex // Protects both config and configOld + // Core-specific config + boxPub crypto.BoxPubKey + boxPriv crypto.BoxPrivKey + sigPub crypto.SigPubKey + sigPriv crypto.SigPrivKey + // Modules switchTable switchTable peers peers sessions sessions @@ -35,8 +48,9 @@ type Core struct { multicast multicast nodeinfo nodeinfo tcp tcpInterface - log *log.Logger - ifceExpr []*regexp.Regexp // the zone of link-local IPv6 peers must match this + // Other bits + log *log.Logger + ifceExpr []*regexp.Regexp // the zone of link-local IPv6 peers must match this } func (c *Core) init(bpub *crypto.BoxPubKey, @@ -62,8 +76,26 @@ func (c *Core) init(bpub *crypto.BoxPubKey, c.switchTable.init(c, c.sigPub) // TODO move before peers? before router? } -// Get the current build name. This is usually injected if built from git, -// or returns "unknown" otherwise. +// UpdateConfig updates the configuration in Core and then signals the +// various module goroutines to reconfigure themselves if needed +func (c *Core) UpdateConfig(config *config.NodeConfig) { + c.configMutex.Lock() + c.configOld = c.config + c.config = *config + c.configMutex.Unlock() + + c.admin.reconfigure <- true + c.searches.reconfigure <- true + c.dht.reconfigure <- true + c.sessions.reconfigure <- true + c.multicast.reconfigure <- true + c.peers.reconfigure <- true + c.router.reconfigure <- true + c.switchTable.reconfigure <- true +} + +// GetBuildName gets the current build name. This is usually injected if built +// from git, or returns "unknown" otherwise. func GetBuildName() string { if buildName == "" { return "unknown" @@ -96,6 +128,11 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error { c.log.Println("Starting up...") + c.configMutex.Lock() + c.config = *nc + c.configOld = c.config + c.configMutex.Unlock() + var boxPub crypto.BoxPubKey var boxPriv crypto.BoxPrivKey var sigPub crypto.SigPubKey diff --git a/src/yggdrasil/dht.go b/src/yggdrasil/dht.go index b52a820..3f2debd 100644 --- a/src/yggdrasil/dht.go +++ b/src/yggdrasil/dht.go @@ -65,11 +65,12 @@ type dhtReqKey struct { // The main DHT struct. type dht struct { - core *Core - nodeID crypto.NodeID - peers chan *dhtInfo // other goroutines put incoming dht updates here - reqs map[dhtReqKey]time.Time // Keeps track of recent outstanding requests - callbacks map[dhtReqKey]dht_callbackInfo // Search and admin lookup callbacks + core *Core + reconfigure chan bool + nodeID crypto.NodeID + peers chan *dhtInfo // other goroutines put incoming dht updates here + reqs map[dhtReqKey]time.Time // Keeps track of recent outstanding requests + callbacks map[dhtReqKey]dht_callbackInfo // Search and admin lookup callbacks // These next two could be replaced by a single linked list or similar... table map[crypto.NodeID]*dhtInfo imp []*dhtInfo @@ -78,6 +79,18 @@ type dht struct { // Initializes the DHT. func (t *dht) init(c *Core) { t.core = c + t.reconfigure = make(chan bool, 1) + go func() { + for { + select { + case _ = <-t.reconfigure: + t.core.configMutex.RLock() + t.core.log.Println("Notified: dht") + t.core.configMutex.RUnlock() + continue + } + } + }() t.nodeID = *t.core.GetNodeID() t.peers = make(chan *dhtInfo, 1024) t.callbacks = make(map[dhtReqKey]dht_callbackInfo) diff --git a/src/yggdrasil/multicast.go b/src/yggdrasil/multicast.go index 749dfcd..3d73237 100644 --- a/src/yggdrasil/multicast.go +++ b/src/yggdrasil/multicast.go @@ -10,13 +10,26 @@ import ( ) type multicast struct { - core *Core - sock *ipv6.PacketConn - groupAddr string + core *Core + reconfigure chan bool + sock *ipv6.PacketConn + groupAddr string } func (m *multicast) init(core *Core) { m.core = core + m.reconfigure = make(chan bool, 1) + go func() { + for { + select { + case _ = <-m.reconfigure: + m.core.configMutex.RLock() + m.core.log.Println("Notified: multicast") + m.core.configMutex.RUnlock() + continue + } + } + }() m.groupAddr = "[ff02::114]:9001" // Check if we've been given any expressions if len(m.core.ifceExpr) == 0 { diff --git a/src/yggdrasil/peer.go b/src/yggdrasil/peer.go index a2b94b6..502ea67 100644 --- a/src/yggdrasil/peer.go +++ b/src/yggdrasil/peer.go @@ -19,6 +19,7 @@ import ( // In other cases, it's link protocol traffic used to build the spanning tree, in which case this checks signatures and passes the message along to the switch. type peers struct { core *Core + reconfigure chan bool mutex sync.Mutex // Synchronize writes to atomic ports atomic.Value //map[switchPort]*peer, use CoW semantics authMutex sync.RWMutex @@ -31,6 +32,18 @@ func (ps *peers) init(c *Core) { defer ps.mutex.Unlock() ps.putPorts(make(map[switchPort]*peer)) ps.core = c + ps.reconfigure = make(chan bool, 1) + go func() { + for { + select { + case _ = <-ps.reconfigure: + ps.core.configMutex.RLock() + ps.core.log.Println("Notified: peers") + ps.core.configMutex.RUnlock() + continue + } + } + }() ps.allowedEncryptionPublicKeys = make(map[crypto.BoxPubKey]struct{}) } diff --git a/src/yggdrasil/router.go b/src/yggdrasil/router.go index 87da882..096a978 100644 --- a/src/yggdrasil/router.go +++ b/src/yggdrasil/router.go @@ -37,19 +37,20 @@ import ( // The router struct has channels to/from the tun/tap device and a self peer (0), which is how messages are passed between this node and the peers/switch layer. // The router's mainLoop goroutine is responsible for managing all information related to the dht, searches, and crypto sessions. type router struct { - core *Core - addr address.Address - subnet address.Subnet - in <-chan []byte // packets we received from the network, link to peer's "out" - out func([]byte) // packets we're sending to the network, link to peer's "in" - toRecv chan router_recvPacket // packets to handle via recvPacket() - tun tunAdapter // TUN/TAP adapter - adapters []Adapter // Other adapters - recv chan<- []byte // place where the tun pulls received packets from - send <-chan []byte // place where the tun puts outgoing packets - reset chan struct{} // signal that coords changed (re-init sessions/dht) - admin chan func() // pass a lambda for the admin socket to query stuff - cryptokey cryptokey + core *Core + reconfigure chan bool + addr address.Address + subnet address.Subnet + in <-chan []byte // packets we received from the network, link to peer's "out" + out func([]byte) // packets we're sending to the network, link to peer's "in" + toRecv chan router_recvPacket // packets to handle via recvPacket() + tun tunAdapter // TUN/TAP adapter + adapters []Adapter // Other adapters + recv chan<- []byte // place where the tun pulls received packets from + send <-chan []byte // place where the tun puts outgoing packets + reset chan struct{} // signal that coords changed (re-init sessions/dht) + admin chan func() // pass a lambda for the admin socket to query stuff + cryptokey cryptokey } // Packet and session info, used to check that the packet matches a valid IP range or CKR prefix before sending to the tun. @@ -61,6 +62,7 @@ type router_recvPacket struct { // Initializes the router struct, which includes setting up channels to/from the tun/tap. func (r *router) init(core *Core) { r.core = core + r.reconfigure = make(chan bool, 1) r.addr = *address.AddrForNodeID(&r.core.dht.nodeID) r.subnet = *address.SubnetForNodeID(&r.core.dht.nodeID) in := make(chan []byte, 32) // TODO something better than this... @@ -124,6 +126,11 @@ func (r *router) mainLoop() { } case f := <-r.admin: f() + case _ = <-r.reconfigure: + r.core.configMutex.RLock() + r.core.log.Println("Notified: router") + r.core.configMutex.RUnlock() + continue } } } diff --git a/src/yggdrasil/search.go b/src/yggdrasil/search.go index c85b719..f522d7b 100644 --- a/src/yggdrasil/search.go +++ b/src/yggdrasil/search.go @@ -42,13 +42,26 @@ type searchInfo struct { // This stores a map of active searches. type searches struct { - core *Core - searches map[crypto.NodeID]*searchInfo + core *Core + reconfigure chan bool + searches map[crypto.NodeID]*searchInfo } // Intializes the searches struct. func (s *searches) init(core *Core) { s.core = core + s.reconfigure = make(chan bool, 1) + go func() { + for { + select { + case _ = <-s.reconfigure: + s.core.configMutex.RLock() + s.core.log.Println("Notified: searches") + s.core.configMutex.RUnlock() + continue + } + } + }() s.searches = make(map[crypto.NodeID]*searchInfo) } diff --git a/src/yggdrasil/session.go b/src/yggdrasil/session.go index 4f395b0..78b36ec 100644 --- a/src/yggdrasil/session.go +++ b/src/yggdrasil/session.go @@ -18,6 +18,7 @@ import ( // This includes coords, permanent and ephemeral keys, handles and nonces, various sorts of timing information for timeout and maintenance, and some metadata for the admin API. type sessionInfo struct { core *Core + reconfigure chan bool theirAddr address.Address theirSubnet address.Subnet theirPermPub crypto.BoxPubKey @@ -101,6 +102,7 @@ func (s *sessionInfo) timedout() bool { // Additionally, stores maps of address/subnet onto keys, and keys onto handles. type sessions struct { core *Core + reconfigure chan bool lastCleanup time.Time // Maps known permanent keys to their shared key, used by DHT a lot permShared map[crypto.BoxPubKey]*crypto.BoxSharedKey @@ -124,6 +126,22 @@ type sessions struct { // Initializes the session struct. func (ss *sessions) init(core *Core) { ss.core = core + ss.reconfigure = make(chan bool, 1) + go func() { + for { + select { + case newConfig := <-ss.reconfigure: + ss.core.configMutex.RLock() + ss.core.log.Println("Notified: sessions") + ss.core.configMutex.RUnlock() + + for _, sinfo := range ss.sinfos { + sinfo.reconfigure <- newConfig + } + continue + } + } + }() ss.permShared = make(map[crypto.BoxPubKey]*crypto.BoxSharedKey) ss.sinfos = make(map[crypto.Handle]*sessionInfo) ss.byMySes = make(map[crypto.BoxPubKey]*crypto.Handle) @@ -271,6 +289,7 @@ func (ss *sessions) createSession(theirPermKey *crypto.BoxPubKey) *sessionInfo { } sinfo := sessionInfo{} sinfo.core = ss.core + sinfo.reconfigure = make(chan bool, 1) sinfo.theirPermPub = *theirPermKey pub, priv := crypto.NewBoxKeys() sinfo.mySesPub = *pub @@ -539,6 +558,11 @@ func (sinfo *sessionInfo) doWorker() { } else { return } + case _ = <-sinfo.reconfigure: + sinfo.core.configMutex.RLock() + sinfo.core.log.Println("Notified: sessionInfo") + sinfo.core.configMutex.RUnlock() + continue } } } diff --git a/src/yggdrasil/switch.go b/src/yggdrasil/switch.go index 3c1dae6..420392b 100644 --- a/src/yggdrasil/switch.go +++ b/src/yggdrasil/switch.go @@ -162,6 +162,7 @@ type switchData struct { // All the information stored by the switch. type switchTable struct { core *Core + reconfigure chan bool key crypto.SigPubKey // Our own key time time.Time // Time when locator.tstamp was last updated drop map[crypto.SigPubKey]int64 // Tstamp associated with a dropped root @@ -184,6 +185,7 @@ const SwitchQueueTotalMinSize = 4 * 1024 * 1024 func (t *switchTable) init(core *Core, key crypto.SigPubKey) { now := time.Now() t.core = core + t.reconfigure = make(chan bool, 1) t.key = key locator := switchLocator{root: key, tstamp: now.Unix()} peers := make(map[switchPort]peerInfo) @@ -808,6 +810,11 @@ func (t *switchTable) doWorker() { } case f := <-t.admin: f() + case _ = <-t.reconfigure: + t.core.configMutex.RLock() + t.core.log.Println("Notified: switchTable") + t.core.configMutex.RUnlock() + continue } } } From fa7c4117b4cce9932f20b7d28399315854296dbc Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 29 Dec 2018 19:14:26 +0000 Subject: [PATCH 010/109] Use Core.config in init functions --- src/yggdrasil/admin.go | 6 ++- src/yggdrasil/core.go | 97 +++++++++++++++++++---------------------- src/yggdrasil/switch.go | 6 +-- src/yggdrasil/tcp.go | 9 ++-- 4 files changed, 56 insertions(+), 62 deletions(-) diff --git a/src/yggdrasil/admin.go b/src/yggdrasil/admin.go index 1c8c80e..723bf8f 100644 --- a/src/yggdrasil/admin.go +++ b/src/yggdrasil/admin.go @@ -52,7 +52,7 @@ func (a *admin) addHandler(name string, args []string, handler func(admin_info) } // init runs the initial admin setup. -func (a *admin) init(c *Core, listenaddr string) { +func (a *admin) init(c *Core) { a.core = c a.reconfigure = make(chan bool, 1) go func() { @@ -69,7 +69,9 @@ func (a *admin) init(c *Core, listenaddr string) { } } }() - a.listenaddr = listenaddr + a.core.configMutex.RLock() + a.listenaddr = a.core.config.AdminListen + a.core.configMutex.RUnlock() a.addHandler("list", []string{}, func(in admin_info) (admin_info, error) { handlers := make(map[string]interface{}) for _, handler := range a.handlers { diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index 9e4bb62..58d92b0 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -19,7 +19,7 @@ var buildName string var buildVersion string type module interface { - init(*config.NodeConfig) error + init(*Core, *config.NodeConfig) error start() error } @@ -32,12 +32,10 @@ type Core struct { config config.NodeConfig // Active config configOld config.NodeConfig // Previous config configMutex sync.RWMutex // Protects both config and configOld - // Core-specific config - boxPub crypto.BoxPubKey - boxPriv crypto.BoxPrivKey - sigPub crypto.SigPubKey - sigPriv crypto.SigPrivKey - // Modules + boxPub crypto.BoxPubKey + boxPriv crypto.BoxPrivKey + sigPub crypto.SigPubKey + sigPriv crypto.SigPrivKey switchTable switchTable peers peers sessions sessions @@ -48,15 +46,11 @@ type Core struct { multicast multicast nodeinfo nodeinfo tcp tcpInterface - // Other bits - log *log.Logger - ifceExpr []*regexp.Regexp // the zone of link-local IPv6 peers must match this + log *log.Logger + ifceExpr []*regexp.Regexp // the zone of link-local IPv6 peers must match this } -func (c *Core) init(bpub *crypto.BoxPubKey, - bpriv *crypto.BoxPrivKey, - spub *crypto.SigPubKey, - spriv *crypto.SigPrivKey) { +func (c *Core) init() error { // TODO separate init and start functions // Init sets up structs // Start launches goroutines that depend on structs being set up @@ -64,16 +58,45 @@ func (c *Core) init(bpub *crypto.BoxPubKey, if c.log == nil { c.log = log.New(ioutil.Discard, "", 0) } - c.boxPub, c.boxPriv = *bpub, *bpriv - c.sigPub, c.sigPriv = *spub, *spriv - c.admin.core = c + + boxPubHex, err := hex.DecodeString(c.config.EncryptionPublicKey) + if err != nil { + return err + } + boxPrivHex, err := hex.DecodeString(c.config.EncryptionPrivateKey) + if err != nil { + return err + } + sigPubHex, err := hex.DecodeString(c.config.SigningPublicKey) + if err != nil { + return err + } + sigPrivHex, err := hex.DecodeString(c.config.SigningPrivateKey) + if err != nil { + return err + } + + copy(c.boxPub[:], boxPubHex) + copy(c.boxPriv[:], boxPrivHex) + copy(c.sigPub[:], sigPubHex) + copy(c.sigPriv[:], sigPrivHex) + + c.admin.init(c) + c.nodeinfo.init(c) c.searches.init(c) c.dht.init(c) c.sessions.init(c) c.multicast.init(c) c.peers.init(c) c.router.init(c) - c.switchTable.init(c, c.sigPub) // TODO move before peers? before router? + c.switchTable.init(c) // TODO move before peers? before router? + + if err := c.tcp.init(c); err != nil { + c.log.Println("Failed to start TCP interface") + return err + } + + return nil } // UpdateConfig updates the configuration in Core and then signals the @@ -133,42 +156,10 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error { c.configOld = c.config c.configMutex.Unlock() - var boxPub crypto.BoxPubKey - var boxPriv crypto.BoxPrivKey - var sigPub crypto.SigPubKey - var sigPriv crypto.SigPrivKey - boxPubHex, err := hex.DecodeString(nc.EncryptionPublicKey) - if err != nil { - return err - } - boxPrivHex, err := hex.DecodeString(nc.EncryptionPrivateKey) - if err != nil { - return err - } - sigPubHex, err := hex.DecodeString(nc.SigningPublicKey) - if err != nil { - return err - } - sigPrivHex, err := hex.DecodeString(nc.SigningPrivateKey) - if err != nil { - return err - } - copy(boxPub[:], boxPubHex) - copy(boxPriv[:], boxPrivHex) - copy(sigPub[:], sigPubHex) - copy(sigPriv[:], sigPrivHex) + c.init() - c.init(&boxPub, &boxPriv, &sigPub, &sigPriv) - c.admin.init(c, nc.AdminListen) - - c.nodeinfo.init(c) c.nodeinfo.setNodeInfo(nc.NodeInfo, nc.NodeInfoPrivacy) - if err := c.tcp.init(c, nc.Listen, nc.ReadTimeout); err != nil { - c.log.Println("Failed to start TCP interface") - return err - } - if nc.SwitchOptions.MaxTotalQueueSize >= SwitchQueueTotalMinSize { c.switchTable.queueTotalMaxSize = nc.SwitchOptions.MaxTotalQueueSize } @@ -201,7 +192,7 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error { } } for _, source := range nc.TunnelRouting.IPv6Sources { - if c.router.cryptokey.addSourceSubnet(source); err != nil { + if err := c.router.cryptokey.addSourceSubnet(source); err != nil { panic(err) } } @@ -211,7 +202,7 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error { } } for _, source := range nc.TunnelRouting.IPv4Sources { - if c.router.cryptokey.addSourceSubnet(source); err != nil { + if err := c.router.cryptokey.addSourceSubnet(source); err != nil { panic(err) } } diff --git a/src/yggdrasil/switch.go b/src/yggdrasil/switch.go index 420392b..f3c9512 100644 --- a/src/yggdrasil/switch.go +++ b/src/yggdrasil/switch.go @@ -182,12 +182,12 @@ type switchTable struct { const SwitchQueueTotalMinSize = 4 * 1024 * 1024 // Initializes the switchTable struct. -func (t *switchTable) init(core *Core, key crypto.SigPubKey) { +func (t *switchTable) init(core *Core) { now := time.Now() t.core = core t.reconfigure = make(chan bool, 1) - t.key = key - locator := switchLocator{root: key, tstamp: now.Unix()} + t.key = t.core.sigPub + locator := switchLocator{root: t.key, tstamp: now.Unix()} peers := make(map[switchPort]peerInfo) t.data = switchData{locator: locator, peers: peers} t.updater.Store(&sync.Once{}) diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index 6d92344..c986dc6 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -40,6 +40,7 @@ type tcpInterface struct { core *Core serv net.Listener tcp_timeout time.Duration + tcp_addr string mutex sync.Mutex // Protecting the below calls map[string]struct{} conns map[tcpInfo](chan struct{}) @@ -80,15 +81,15 @@ func (iface *tcpInterface) connectSOCKS(socksaddr, peeraddr string) { } // Initializes the struct. -func (iface *tcpInterface) init(core *Core, addr string, readTimeout int32) (err error) { +func (iface *tcpInterface) init(core *Core) (err error) { iface.core = core - - iface.tcp_timeout = time.Duration(readTimeout) * time.Millisecond + iface.tcp_addr = iface.core.config.Listen + iface.tcp_timeout = time.Duration(iface.core.config.ReadTimeout) * time.Millisecond if iface.tcp_timeout >= 0 && iface.tcp_timeout < default_tcp_timeout { iface.tcp_timeout = default_tcp_timeout } - iface.serv, err = net.Listen("tcp", addr) + iface.serv, err = net.Listen("tcp", iface.tcp_addr) if err == nil { iface.calls = make(map[string]struct{}) iface.conns = make(map[tcpInfo](chan struct{})) From 2925920c703a3a743346d241097513952c80d979 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 29 Dec 2018 19:53:31 +0000 Subject: [PATCH 011/109] Use mutex in switch/tcp init --- src/yggdrasil/switch.go | 2 ++ src/yggdrasil/tcp.go | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/yggdrasil/switch.go b/src/yggdrasil/switch.go index f3c9512..10c9563 100644 --- a/src/yggdrasil/switch.go +++ b/src/yggdrasil/switch.go @@ -186,7 +186,9 @@ func (t *switchTable) init(core *Core) { now := time.Now() t.core = core t.reconfigure = make(chan bool, 1) + t.core.configMutex.RLock() t.key = t.core.sigPub + t.core.configMutex.RUnlock() locator := switchLocator{root: t.key, tstamp: now.Unix()} peers := make(map[switchPort]peerInfo) t.data = switchData{locator: locator, peers: peers} diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index c986dc6..ad50d78 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -83,8 +83,10 @@ func (iface *tcpInterface) connectSOCKS(socksaddr, peeraddr string) { // Initializes the struct. func (iface *tcpInterface) init(core *Core) (err error) { iface.core = core + iface.core.configMutex.RLock() iface.tcp_addr = iface.core.config.Listen iface.tcp_timeout = time.Duration(iface.core.config.ReadTimeout) * time.Millisecond + iface.core.configMutex.RUnlock() if iface.tcp_timeout >= 0 && iface.tcp_timeout < default_tcp_timeout { iface.tcp_timeout = default_tcp_timeout } From 7fae1c993ab5d754625336b16e13e47a3d71a807 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 30 Dec 2018 12:04:42 +0000 Subject: [PATCH 012/109] Handle errors from reconfigure tasks --- src/yggdrasil/admin.go | 9 ++++----- src/yggdrasil/core.go | 26 ++++++++++++++++++-------- src/yggdrasil/dht.go | 11 ++++------- src/yggdrasil/multicast.go | 11 ++++------- src/yggdrasil/peer.go | 11 ++++------- src/yggdrasil/router.go | 11 ++++------- src/yggdrasil/search.go | 11 ++++------- src/yggdrasil/session.go | 35 ++++++++++++++++++----------------- src/yggdrasil/switch.go | 11 ++++------- 9 files changed, 64 insertions(+), 72 deletions(-) diff --git a/src/yggdrasil/admin.go b/src/yggdrasil/admin.go index 723bf8f..90fb112 100644 --- a/src/yggdrasil/admin.go +++ b/src/yggdrasil/admin.go @@ -23,7 +23,7 @@ import ( type admin struct { core *Core - reconfigure chan bool + reconfigure chan chan error listenaddr string listener net.Listener handlers []admin_handlerInfo @@ -54,18 +54,17 @@ func (a *admin) addHandler(name string, args []string, handler func(admin_info) // init runs the initial admin setup. func (a *admin) init(c *Core) { a.core = c - a.reconfigure = make(chan bool, 1) + a.reconfigure = make(chan chan error, 1) go func() { for { select { - case _ = <-a.reconfigure: + case e := <-a.reconfigure: a.core.configMutex.RLock() - a.core.log.Println("Notified: admin") if a.core.config.AdminListen != a.core.configOld.AdminListen { a.core.log.Println("AdminListen has changed!") } a.core.configMutex.RUnlock() - continue + e <- nil } } }() diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index 58d92b0..435cd67 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -107,14 +107,24 @@ func (c *Core) UpdateConfig(config *config.NodeConfig) { c.config = *config c.configMutex.Unlock() - c.admin.reconfigure <- true - c.searches.reconfigure <- true - c.dht.reconfigure <- true - c.sessions.reconfigure <- true - c.multicast.reconfigure <- true - c.peers.reconfigure <- true - c.router.reconfigure <- true - c.switchTable.reconfigure <- true + components := []chan chan error{ + c.admin.reconfigure, + c.searches.reconfigure, + c.dht.reconfigure, + c.sessions.reconfigure, + c.multicast.reconfigure, + c.peers.reconfigure, + c.router.reconfigure, + c.switchTable.reconfigure, + } + + for _, component := range components { + response := make(chan error) + component <- response + if err := <-response; err != nil { + c.log.Println(err) + } + } } // GetBuildName gets the current build name. This is usually injected if built diff --git a/src/yggdrasil/dht.go b/src/yggdrasil/dht.go index 3f2debd..bba6dfc 100644 --- a/src/yggdrasil/dht.go +++ b/src/yggdrasil/dht.go @@ -66,7 +66,7 @@ type dhtReqKey struct { // The main DHT struct. type dht struct { core *Core - reconfigure chan bool + reconfigure chan chan error nodeID crypto.NodeID peers chan *dhtInfo // other goroutines put incoming dht updates here reqs map[dhtReqKey]time.Time // Keeps track of recent outstanding requests @@ -79,15 +79,12 @@ type dht struct { // Initializes the DHT. func (t *dht) init(c *Core) { t.core = c - t.reconfigure = make(chan bool, 1) + t.reconfigure = make(chan chan error, 1) go func() { for { select { - case _ = <-t.reconfigure: - t.core.configMutex.RLock() - t.core.log.Println("Notified: dht") - t.core.configMutex.RUnlock() - continue + case e := <-t.reconfigure: + e <- nil } } }() diff --git a/src/yggdrasil/multicast.go b/src/yggdrasil/multicast.go index 3d73237..25c979c 100644 --- a/src/yggdrasil/multicast.go +++ b/src/yggdrasil/multicast.go @@ -11,22 +11,19 @@ import ( type multicast struct { core *Core - reconfigure chan bool + reconfigure chan chan error sock *ipv6.PacketConn groupAddr string } func (m *multicast) init(core *Core) { m.core = core - m.reconfigure = make(chan bool, 1) + m.reconfigure = make(chan chan error, 1) go func() { for { select { - case _ = <-m.reconfigure: - m.core.configMutex.RLock() - m.core.log.Println("Notified: multicast") - m.core.configMutex.RUnlock() - continue + case e := <-m.reconfigure: + e <- nil } } }() diff --git a/src/yggdrasil/peer.go b/src/yggdrasil/peer.go index 502ea67..15174b7 100644 --- a/src/yggdrasil/peer.go +++ b/src/yggdrasil/peer.go @@ -19,7 +19,7 @@ import ( // In other cases, it's link protocol traffic used to build the spanning tree, in which case this checks signatures and passes the message along to the switch. type peers struct { core *Core - reconfigure chan bool + reconfigure chan chan error mutex sync.Mutex // Synchronize writes to atomic ports atomic.Value //map[switchPort]*peer, use CoW semantics authMutex sync.RWMutex @@ -32,15 +32,12 @@ func (ps *peers) init(c *Core) { defer ps.mutex.Unlock() ps.putPorts(make(map[switchPort]*peer)) ps.core = c - ps.reconfigure = make(chan bool, 1) + ps.reconfigure = make(chan chan error, 1) go func() { for { select { - case _ = <-ps.reconfigure: - ps.core.configMutex.RLock() - ps.core.log.Println("Notified: peers") - ps.core.configMutex.RUnlock() - continue + case e := <-ps.reconfigure: + e <- nil } } }() diff --git a/src/yggdrasil/router.go b/src/yggdrasil/router.go index 096a978..68fb025 100644 --- a/src/yggdrasil/router.go +++ b/src/yggdrasil/router.go @@ -38,7 +38,7 @@ import ( // The router's mainLoop goroutine is responsible for managing all information related to the dht, searches, and crypto sessions. type router struct { core *Core - reconfigure chan bool + reconfigure chan chan error addr address.Address subnet address.Subnet in <-chan []byte // packets we received from the network, link to peer's "out" @@ -62,7 +62,7 @@ type router_recvPacket struct { // Initializes the router struct, which includes setting up channels to/from the tun/tap. func (r *router) init(core *Core) { r.core = core - r.reconfigure = make(chan bool, 1) + r.reconfigure = make(chan chan error, 1) r.addr = *address.AddrForNodeID(&r.core.dht.nodeID) r.subnet = *address.SubnetForNodeID(&r.core.dht.nodeID) in := make(chan []byte, 32) // TODO something better than this... @@ -126,11 +126,8 @@ func (r *router) mainLoop() { } case f := <-r.admin: f() - case _ = <-r.reconfigure: - r.core.configMutex.RLock() - r.core.log.Println("Notified: router") - r.core.configMutex.RUnlock() - continue + case e := <-r.reconfigure: + e <- nil } } } diff --git a/src/yggdrasil/search.go b/src/yggdrasil/search.go index f522d7b..f0af61f 100644 --- a/src/yggdrasil/search.go +++ b/src/yggdrasil/search.go @@ -43,22 +43,19 @@ type searchInfo struct { // This stores a map of active searches. type searches struct { core *Core - reconfigure chan bool + reconfigure chan chan error searches map[crypto.NodeID]*searchInfo } // Intializes the searches struct. func (s *searches) init(core *Core) { s.core = core - s.reconfigure = make(chan bool, 1) + s.reconfigure = make(chan chan error, 1) go func() { for { select { - case _ = <-s.reconfigure: - s.core.configMutex.RLock() - s.core.log.Println("Notified: searches") - s.core.configMutex.RUnlock() - continue + case e := <-s.reconfigure: + e <- nil } } }() diff --git a/src/yggdrasil/session.go b/src/yggdrasil/session.go index 78b36ec..3c8e013 100644 --- a/src/yggdrasil/session.go +++ b/src/yggdrasil/session.go @@ -18,7 +18,7 @@ import ( // This includes coords, permanent and ephemeral keys, handles and nonces, various sorts of timing information for timeout and maintenance, and some metadata for the admin API. type sessionInfo struct { core *Core - reconfigure chan bool + reconfigure chan chan error theirAddr address.Address theirSubnet address.Subnet theirPermPub crypto.BoxPubKey @@ -102,7 +102,7 @@ func (s *sessionInfo) timedout() bool { // Additionally, stores maps of address/subnet onto keys, and keys onto handles. type sessions struct { core *Core - reconfigure chan bool + reconfigure chan chan error lastCleanup time.Time // Maps known permanent keys to their shared key, used by DHT a lot permShared map[crypto.BoxPubKey]*crypto.BoxSharedKey @@ -126,19 +126,23 @@ type sessions struct { // Initializes the session struct. func (ss *sessions) init(core *Core) { ss.core = core - ss.reconfigure = make(chan bool, 1) + ss.reconfigure = make(chan chan error, 1) go func() { for { select { - case newConfig := <-ss.reconfigure: - ss.core.configMutex.RLock() - ss.core.log.Println("Notified: sessions") - ss.core.configMutex.RUnlock() - - for _, sinfo := range ss.sinfos { - sinfo.reconfigure <- newConfig + case e := <-ss.reconfigure: + responses := make(map[crypto.Handle]chan error) + for index, session := range ss.sinfos { + responses[index] = make(chan error) + session.reconfigure <- responses[index] } - continue + for _, response := range responses { + if err := <-response; err != nil { + e <- err + continue + } + } + e <- nil } } }() @@ -289,7 +293,7 @@ func (ss *sessions) createSession(theirPermKey *crypto.BoxPubKey) *sessionInfo { } sinfo := sessionInfo{} sinfo.core = ss.core - sinfo.reconfigure = make(chan bool, 1) + sinfo.reconfigure = make(chan chan error, 1) sinfo.theirPermPub = *theirPermKey pub, priv := crypto.NewBoxKeys() sinfo.mySesPub = *pub @@ -558,11 +562,8 @@ func (sinfo *sessionInfo) doWorker() { } else { return } - case _ = <-sinfo.reconfigure: - sinfo.core.configMutex.RLock() - sinfo.core.log.Println("Notified: sessionInfo") - sinfo.core.configMutex.RUnlock() - continue + case e := <-sinfo.reconfigure: + e <- nil } } } diff --git a/src/yggdrasil/switch.go b/src/yggdrasil/switch.go index 10c9563..741de98 100644 --- a/src/yggdrasil/switch.go +++ b/src/yggdrasil/switch.go @@ -162,7 +162,7 @@ type switchData struct { // All the information stored by the switch. type switchTable struct { core *Core - reconfigure chan bool + reconfigure chan chan error key crypto.SigPubKey // Our own key time time.Time // Time when locator.tstamp was last updated drop map[crypto.SigPubKey]int64 // Tstamp associated with a dropped root @@ -185,7 +185,7 @@ const SwitchQueueTotalMinSize = 4 * 1024 * 1024 func (t *switchTable) init(core *Core) { now := time.Now() t.core = core - t.reconfigure = make(chan bool, 1) + t.reconfigure = make(chan chan error, 1) t.core.configMutex.RLock() t.key = t.core.sigPub t.core.configMutex.RUnlock() @@ -812,11 +812,8 @@ func (t *switchTable) doWorker() { } case f := <-t.admin: f() - case _ = <-t.reconfigure: - t.core.configMutex.RLock() - t.core.log.Println("Notified: switchTable") - t.core.configMutex.RUnlock() - continue + case e := <-t.reconfigure: + e <- nil } } } From f96747181d13480046874900898b7ede6e6c8069 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 30 Dec 2018 12:26:55 +0000 Subject: [PATCH 013/109] Allow updating AdminListen during runtime --- cmd/yggdrasil/main.go | 231 ++++++++++++++++++++++------------------- src/yggdrasil/admin.go | 4 +- 2 files changed, 125 insertions(+), 110 deletions(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index e98e623..5ee7add 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -76,6 +76,119 @@ func generateConfig(isAutoconf bool) *nodeConfig { return &cfg } +func readConfig(useconf *bool, useconffile *string, normaliseconf *bool) *nodeConfig { + // Use a configuration file. If -useconf, the configuration will be read + // from stdin. If -useconffile, the configuration will be read from the + // filesystem. + var config []byte + var err error + if *useconffile != "" { + // Read the file from the filesystem + config, err = ioutil.ReadFile(*useconffile) + } else { + // Read the file from stdin. + config, err = ioutil.ReadAll(os.Stdin) + } + if err != nil { + panic(err) + } + // If there's a byte order mark - which Windows 10 is now incredibly fond of + // throwing everywhere when it's converting things into UTF-16 for the hell + // of it - remove it and decode back down into UTF-8. This is necessary + // because hjson doesn't know what to do with UTF-16 and will panic + if bytes.Compare(config[0:2], []byte{0xFF, 0xFE}) == 0 || + bytes.Compare(config[0:2], []byte{0xFE, 0xFF}) == 0 { + utf := unicode.UTF16(unicode.BigEndian, unicode.UseBOM) + decoder := utf.NewDecoder() + config, err = decoder.Bytes(config) + if err != nil { + panic(err) + } + } + // Generate a new configuration - this gives us a set of sane defaults - + // 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 := generateConfig(false) + var dat map[string]interface{} + if err := hjson.Unmarshal(config, &dat); err != nil { + panic(err) + } + confJson, err := json.Marshal(dat) + if err != nil { + panic(err) + } + json.Unmarshal(confJson, &cfg) + // For now we will do a little bit to help the user adjust their + // configuration to match the new configuration format, as some of the key + // names have changed recently. + changes := map[string]string{ + "Multicast": "", + "LinkLocal": "MulticastInterfaces", + "BoxPub": "EncryptionPublicKey", + "BoxPriv": "EncryptionPrivateKey", + "SigPub": "SigningPublicKey", + "SigPriv": "SigningPrivateKey", + "AllowedBoxPubs": "AllowedEncryptionPublicKeys", + } + // Loop over the mappings aove and see if we have anything to fix. + for from, to := range changes { + if _, ok := dat[from]; ok { + if to == "" { + if !*normaliseconf { + log.Println("Warning: Deprecated config option", from, "- please remove") + } + } else { + if !*normaliseconf { + log.Println("Warning: Deprecated config option", from, "- please rename to", to) + } + // If the configuration file doesn't already contain a line with the + // new name then set it to the old value. This makes sure that we + // don't overwrite something that was put there intentionally. + if _, ok := dat[to]; !ok { + dat[to] = dat[from] + } + } + } + } + // Check to see if the peers are in a parsable format, if not then default + // them to the TCP scheme + if peers, ok := dat["Peers"].([]interface{}); ok { + for index, peer := range peers { + uri := peer.(string) + if strings.HasPrefix(uri, "tcp://") || strings.HasPrefix(uri, "socks://") { + continue + } + if strings.HasPrefix(uri, "tcp:") { + uri = uri[4:] + } + (dat["Peers"].([]interface{}))[index] = "tcp://" + uri + } + } + // Now do the same with the interface peers + if interfacepeers, ok := dat["InterfacePeers"].(map[string]interface{}); ok { + for intf, peers := range interfacepeers { + for index, peer := range peers.([]interface{}) { + uri := peer.(string) + if strings.HasPrefix(uri, "tcp://") || strings.HasPrefix(uri, "socks://") { + continue + } + if strings.HasPrefix(uri, "tcp:") { + uri = uri[4:] + } + ((dat["InterfacePeers"].(map[string]interface{}))[intf]).([]interface{})[index] = "tcp://" + uri + } + } + } + // 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 +} + // Generates a new configuration and returns it in HJSON format. This is used // with -genconf. func doGenconf(isjson bool) string { @@ -106,6 +219,7 @@ func main() { flag.Parse() var cfg *nodeConfig + var err error switch { case *version: fmt.Println("Build name:", yggdrasil.GetBuildName()) @@ -116,114 +230,8 @@ func main() { // port numbers, and will use an automatically selected TUN/TAP interface. cfg = generateConfig(true) case *useconffile != "" || *useconf: - // Use a configuration file. If -useconf, the configuration will be read - // from stdin. If -useconffile, the configuration will be read from the - // filesystem. - var config []byte - var err error - if *useconffile != "" { - // Read the file from the filesystem - config, err = ioutil.ReadFile(*useconffile) - } else { - // Read the file from stdin. - config, err = ioutil.ReadAll(os.Stdin) - } - if err != nil { - panic(err) - } - // If there's a byte order mark - which Windows 10 is now incredibly fond of - // throwing everywhere when it's converting things into UTF-16 for the hell - // of it - remove it and decode back down into UTF-8. This is necessary - // because hjson doesn't know what to do with UTF-16 and will panic - if bytes.Compare(config[0:2], []byte{0xFF, 0xFE}) == 0 || - bytes.Compare(config[0:2], []byte{0xFE, 0xFF}) == 0 { - utf := unicode.UTF16(unicode.BigEndian, unicode.UseBOM) - decoder := utf.NewDecoder() - config, err = decoder.Bytes(config) - if err != nil { - panic(err) - } - } - // Generate a new configuration - this gives us a set of sane defaults - - // 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 = generateConfig(false) - var dat map[string]interface{} - if err := hjson.Unmarshal(config, &dat); err != nil { - panic(err) - } - confJson, err := json.Marshal(dat) - if err != nil { - panic(err) - } - json.Unmarshal(confJson, &cfg) - // For now we will do a little bit to help the user adjust their - // configuration to match the new configuration format, as some of the key - // names have changed recently. - changes := map[string]string{ - "Multicast": "", - "LinkLocal": "MulticastInterfaces", - "BoxPub": "EncryptionPublicKey", - "BoxPriv": "EncryptionPrivateKey", - "SigPub": "SigningPublicKey", - "SigPriv": "SigningPrivateKey", - "AllowedBoxPubs": "AllowedEncryptionPublicKeys", - } - // Loop over the mappings aove and see if we have anything to fix. - for from, to := range changes { - if _, ok := dat[from]; ok { - if to == "" { - if !*normaliseconf { - log.Println("Warning: Deprecated config option", from, "- please remove") - } - } else { - if !*normaliseconf { - log.Println("Warning: Deprecated config option", from, "- please rename to", to) - } - // If the configuration file doesn't already contain a line with the - // new name then set it to the old value. This makes sure that we - // don't overwrite something that was put there intentionally. - if _, ok := dat[to]; !ok { - dat[to] = dat[from] - } - } - } - } - // Check to see if the peers are in a parsable format, if not then default - // them to the TCP scheme - if peers, ok := dat["Peers"].([]interface{}); ok { - for index, peer := range peers { - uri := peer.(string) - if strings.HasPrefix(uri, "tcp://") || strings.HasPrefix(uri, "socks://") { - continue - } - if strings.HasPrefix(uri, "tcp:") { - uri = uri[4:] - } - (dat["Peers"].([]interface{}))[index] = "tcp://" + uri - } - } - // Now do the same with the interface peers - if interfacepeers, ok := dat["InterfacePeers"].(map[string]interface{}); ok { - for intf, peers := range interfacepeers { - for index, peer := range peers.([]interface{}) { - uri := peer.(string) - if strings.HasPrefix(uri, "tcp://") || strings.HasPrefix(uri, "socks://") { - continue - } - if strings.HasPrefix(uri, "tcp:") { - uri = uri[4:] - } - ((dat["InterfacePeers"].(map[string]interface{}))[intf]).([]interface{})[index] = "tcp://" + uri - } - } - } - // Overlay our newly mapped configuration onto the autoconf node config that - // we generated above. - if err = mapstructure.Decode(dat, &cfg); err != nil { - panic(err) - } + // Read the configuration from either stdin or from the filesystem + cfg = readConfig(useconf, useconffile, normaliseconf) // If the -normaliseconf option was specified then remarshal the above // configuration and print it back to stdout. This lets the user update // their configuration file with newly mapped names (like above) or to @@ -327,7 +335,12 @@ func main() { for { select { case _ = <-r: - n.core.UpdateConfig(cfg) + if *useconffile != "" { + cfg = readConfig(useconf, useconffile, normaliseconf) + n.core.UpdateConfig(cfg) + } else { + logger.Println("Reloading config at runtime is only possible with -useconffile") + } case _ = <-c: goto exit } diff --git a/src/yggdrasil/admin.go b/src/yggdrasil/admin.go index 90fb112..5682339 100644 --- a/src/yggdrasil/admin.go +++ b/src/yggdrasil/admin.go @@ -61,7 +61,9 @@ func (a *admin) init(c *Core) { case e := <-a.reconfigure: a.core.configMutex.RLock() if a.core.config.AdminListen != a.core.configOld.AdminListen { - a.core.log.Println("AdminListen has changed!") + a.listenaddr = a.core.config.AdminListen + a.close() + a.start() } a.core.configMutex.RUnlock() e <- nil From cb4495902bf9399b881d1a4aa6008b15715111fc Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 30 Dec 2018 15:21:09 +0000 Subject: [PATCH 014/109] Allow updating Listen during runtime --- src/yggdrasil/core.go | 13 ++++++----- src/yggdrasil/multicast.go | 16 ++++++++++--- src/yggdrasil/tcp.go | 48 +++++++++++++++++++++++++++++++++----- 3 files changed, 62 insertions(+), 15 deletions(-) diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index 435cd67..d4a2268 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -109,13 +109,14 @@ func (c *Core) UpdateConfig(config *config.NodeConfig) { components := []chan chan error{ c.admin.reconfigure, - c.searches.reconfigure, - c.dht.reconfigure, - c.sessions.reconfigure, + //c.searches.reconfigure, + //c.dht.reconfigure, + //c.sessions.reconfigure, + //c.peers.reconfigure, + //c.router.reconfigure, + //c.switchTable.reconfigure, + c.tcp.reconfigure, c.multicast.reconfigure, - c.peers.reconfigure, - c.router.reconfigure, - c.switchTable.reconfigure, } for _, component := range components { diff --git a/src/yggdrasil/multicast.go b/src/yggdrasil/multicast.go index 25c979c..218f516 100644 --- a/src/yggdrasil/multicast.go +++ b/src/yggdrasil/multicast.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "net" + "sync" "time" "golang.org/x/net/ipv6" @@ -14,6 +15,8 @@ type multicast struct { reconfigure chan chan error sock *ipv6.PacketConn groupAddr string + myAddr *net.TCPAddr + myAddrMutex sync.RWMutex } func (m *multicast) init(core *Core) { @@ -23,6 +26,9 @@ func (m *multicast) init(core *Core) { for { select { case e := <-m.reconfigure: + m.myAddrMutex.Lock() + m.myAddr = m.core.tcp.getAddr() + m.myAddrMutex.Unlock() e <- nil } } @@ -95,13 +101,14 @@ func (m *multicast) interfaces() []net.Interface { } func (m *multicast) announce() { + var anAddr net.TCPAddr + m.myAddrMutex.Lock() + m.myAddr = m.core.tcp.getAddr() + m.myAddrMutex.Unlock() groupAddr, err := net.ResolveUDPAddr("udp6", m.groupAddr) if err != nil { panic(err) } - var anAddr net.TCPAddr - myAddr := m.core.tcp.getAddr() - anAddr.Port = myAddr.Port destAddr, err := net.ResolveUDPAddr("udp6", m.groupAddr) if err != nil { panic(err) @@ -113,6 +120,9 @@ func (m *multicast) announce() { 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()) if addrIP.To4() != nil { diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index ad50d78..224aca0 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -38,7 +38,9 @@ const tcp_ping_interval = (default_tcp_timeout * 2 / 3) // The TCP listener and information about active TCP connections, to avoid duplication. type tcpInterface struct { core *Core + reconfigure chan chan error serv net.Listener + serv_stop chan bool tcp_timeout time.Duration tcp_addr string mutex sync.Mutex // Protecting the below @@ -83,10 +85,37 @@ func (iface *tcpInterface) connectSOCKS(socksaddr, peeraddr string) { // Initializes the struct. func (iface *tcpInterface) init(core *Core) (err error) { iface.core = core + iface.serv_stop = make(chan bool, 1) + iface.reconfigure = make(chan chan error, 1) + go func() { + for { + select { + case e := <-iface.reconfigure: + iface.core.configMutex.RLock() + updated := iface.core.config.Listen != iface.core.configOld.Listen + iface.core.configMutex.RUnlock() + if updated { + iface.serv_stop <- true + iface.serv.Close() + e <- iface.listen() + } else { + e <- nil + } + } + } + }() + + return iface.listen() +} + +func (iface *tcpInterface) listen() error { + var err error + iface.core.configMutex.RLock() iface.tcp_addr = iface.core.config.Listen iface.tcp_timeout = time.Duration(iface.core.config.ReadTimeout) * time.Millisecond iface.core.configMutex.RUnlock() + if iface.tcp_timeout >= 0 && iface.tcp_timeout < default_tcp_timeout { iface.tcp_timeout = default_tcp_timeout } @@ -96,6 +125,7 @@ func (iface *tcpInterface) init(core *Core) (err error) { iface.calls = make(map[string]struct{}) iface.conns = make(map[tcpInfo](chan struct{})) go iface.listener() + return nil } return err @@ -107,10 +137,16 @@ func (iface *tcpInterface) listener() { iface.core.log.Println("Listening for TCP on:", iface.serv.Addr().String()) for { sock, err := iface.serv.Accept() - if err != nil { - panic(err) + select { + case <-iface.serv_stop: + iface.core.log.Println("Stopping listener") + return + default: + if err != nil { + panic(err) + } + go iface.handler(sock, true) } - go iface.handler(sock, true) } } @@ -363,12 +399,12 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) { themAddr := address.AddrForNodeID(themNodeID) themAddrString := net.IP(themAddr[:]).String() themString := fmt.Sprintf("%s@%s", themAddrString, them) - iface.core.log.Println("Connected:", themString, "source", us) + iface.core.log.Printf("Connected: %s, source: %s", themString, us) err = iface.reader(sock, in) // In this goroutine, because of defers if err == nil { - iface.core.log.Println("Disconnected:", themString, "source", us) + iface.core.log.Printf("Disconnected: %s, source: %s", themString, us) } else { - iface.core.log.Println("Disconnected:", themString, "source", us, "with error:", err) + iface.core.log.Printf("Disconnected: %s, source: %s, error: %s", themString, us, err) } return } From 80c9a1bc12d90dee70411ce1f1995328a878ae46 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 30 Dec 2018 16:48:34 +0000 Subject: [PATCH 015/109] Don't track localAddr in conns as it is irrelevant --- src/yggdrasil/tcp.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index 224aca0..4fb3026 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -53,8 +53,8 @@ type tcpInterface struct { type tcpInfo struct { box crypto.BoxPubKey sig crypto.SigPubKey - localAddr string remoteAddr string + remotePort string } // Wrapper function to set additional options for specific connection types. @@ -313,8 +313,7 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) { } } // Check if we already have a connection to this node, close and block if yes - info.localAddr, _, _ = net.SplitHostPort(sock.LocalAddr().String()) - info.remoteAddr, _, _ = net.SplitHostPort(sock.RemoteAddr().String()) + info.remoteAddr, info.remotePort, _ = net.SplitHostPort(sock.RemoteAddr().String()) iface.mutex.Lock() if blockChan, isIn := iface.conns[info]; isIn { iface.mutex.Unlock() From cd86c338505003dc7779423e427c0e936885f727 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 30 Dec 2018 21:11:16 +0000 Subject: [PATCH 016/109] Try to tidy up a bit, move checks for if we are already calling/connected Something I noticed when working on reconfigure support for the "Listen" option is that we have some rather huge weaknesses in our multicasting design. Right now if we change our Listen address, it's not really possible for remote nodes to know whether they are still connected to us, so they start connecting in response to our changed beacons. They can't know that they already know about us until *after* the handshake but this registers in the local client log as repeated Connect/Disconnects even though the existing peerings never actually drop. --- src/yggdrasil/tcp.go | 74 +++++++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 28 deletions(-) diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index 4fb3026..ec8bca4 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -53,8 +53,8 @@ type tcpInterface struct { type tcpInfo struct { box crypto.BoxPubKey sig crypto.SigPubKey + localAddr string remoteAddr string - remotePort string } // Wrapper function to set additional options for specific connection types. @@ -150,6 +150,22 @@ func (iface *tcpInterface) listener() { } } +// 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] + 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] + return isIn +} + // Checks if a connection already exists. // If not, it adds it to the list of active outgoing calls (to block future attempts) and dials the address. // If the dial is successful, it launches the handler. @@ -161,25 +177,18 @@ func (iface *tcpInterface) call(saddr string, socksaddr *string, sintf string) { if sintf != "" { callname = fmt.Sprintf("%s/%s", saddr, sintf) } - quit := false - iface.mutex.Lock() - if _, isIn := iface.calls[callname]; isIn { - quit = true - } else { - iface.calls[callname] = struct{}{} - defer func() { - // Block new calls for a little while, to mitigate livelock scenarios - time.Sleep(default_tcp_timeout) - time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond) - iface.mutex.Lock() - delete(iface.calls, callname) - iface.mutex.Unlock() - }() - } - iface.mutex.Unlock() - if quit { + if iface.isAlreadyCalling(saddr) { return } + iface.calls[callname] = struct{}{} + defer func() { + // Block new calls for a little while, to mitigate livelock scenarios + time.Sleep(default_tcp_timeout) + time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond) + iface.mutex.Lock() + delete(iface.calls, callname) + iface.mutex.Unlock() + }() var conn net.Conn var err error if socksaddr != nil { @@ -284,9 +293,19 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) { // TODO? Block forever to prevent future connection attempts? suppress future messages about the same node? return } + remoteAddr, _, e1 := net.SplitHostPort(sock.RemoteAddr().String()) + localAddr, _, e2 := net.SplitHostPort(sock.LocalAddr().String()) + if e1 != nil || e2 != nil { + return + } info := tcpInfo{ // used as a map key, so don't include ephemeral link key - box: meta.box, - sig: meta.sig, + box: meta.box, + sig: meta.sig, + localAddr: localAddr, + remoteAddr: remoteAddr, + } + if iface.isAlreadyConnected(info) { + return } // Quit the parent call if this is a connection to ourself equiv := func(k1, k2 []byte) bool { @@ -297,14 +316,14 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) { } return true } - if equiv(info.box[:], iface.core.boxPub[:]) { + if equiv(meta.box[:], iface.core.boxPub[:]) { return } - if equiv(info.sig[:], iface.core.sigPub[:]) { + if equiv(meta.sig[:], iface.core.sigPub[:]) { return } // Check if we're authorized to connect to this key / IP - if incoming && !iface.core.peers.isAllowedEncryptionPublicKey(&info.box) { + if incoming && !iface.core.peers.isAllowedEncryptionPublicKey(&meta.box) { // Allow unauthorized peers if they're link-local raddrStr, _, _ := net.SplitHostPort(sock.RemoteAddr().String()) raddr := net.ParseIP(raddrStr) @@ -313,14 +332,13 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) { } } // Check if we already have a connection to this node, close and block if yes - info.remoteAddr, info.remotePort, _ = net.SplitHostPort(sock.RemoteAddr().String()) iface.mutex.Lock() - if blockChan, isIn := iface.conns[info]; isIn { + /*if blockChan, isIn := iface.conns[info]; isIn { iface.mutex.Unlock() sock.Close() <-blockChan return - } + }*/ blockChan := make(chan struct{}) iface.conns[info] = blockChan iface.mutex.Unlock() @@ -332,7 +350,7 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) { }() // Note that multiple connections to the same node are allowed // E.g. over different interfaces - p := iface.core.peers.newPeer(&info.box, &info.sig, crypto.GetSharedKey(myLinkPriv, &meta.link), sock.RemoteAddr().String()) + p := iface.core.peers.newPeer(&meta.box, &meta.sig, crypto.GetSharedKey(myLinkPriv, &meta.link), sock.RemoteAddr().String()) p.linkOut = make(chan []byte, 1) in := func(bs []byte) { p.handlePacket(bs) @@ -394,7 +412,7 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) { }() us, _, _ := net.SplitHostPort(sock.LocalAddr().String()) them, _, _ := net.SplitHostPort(sock.RemoteAddr().String()) - themNodeID := crypto.GetNodeID(&info.box) + themNodeID := crypto.GetNodeID(&meta.box) themAddr := address.AddrForNodeID(themNodeID) themAddrString := net.IP(themAddr[:]).String() themString := fmt.Sprintf("%s@%s", themAddrString, them) From fb47c9822fc62acae64e41aaf1cea6617ad42457 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 31 Dec 2018 11:48:50 +0000 Subject: [PATCH 017/109] getNodeInfo: Show own info if box_pub_key/coords not specified --- src/yggdrasil/admin.go | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/yggdrasil/admin.go b/src/yggdrasil/admin.go index bd3c905..2496b30 100644 --- a/src/yggdrasil/admin.go +++ b/src/yggdrasil/admin.go @@ -324,12 +324,27 @@ func (a *admin) init(c *Core, listenaddr string) { return admin_info{}, err } }) - a.addHandler("getNodeInfo", []string{"box_pub_key", "coords", "[nocache]"}, func(in admin_info) (admin_info, error) { + a.addHandler("getNodeInfo", []string{"[box_pub_key]", "[coords]", "[nocache]"}, func(in admin_info) (admin_info, error) { var nocache bool if in["nocache"] != nil { nocache = in["nocache"].(string) == "true" } - result, err := a.admin_getNodeInfo(in["box_pub_key"].(string), in["coords"].(string), nocache) + var box_pub_key, coords string + if in["box_pub_key"] == nil && in["coords"] == nil { + nodeinfo := []byte(a.core.nodeinfo.getNodeInfo()) + var jsoninfo interface{} + if err := json.Unmarshal(nodeinfo, &jsoninfo); err != nil { + return admin_info{}, err + } else { + return admin_info{"nodeinfo": jsoninfo}, nil + } + } else if in["box_pub_key"] == nil || in["coords"] == nil { + return admin_info{}, errors.New("Expecting both box_pub_key and coords") + } else { + box_pub_key = in["box_pub_key"].(string) + coords = in["coords"].(string) + } + result, err := a.admin_getNodeInfo(box_pub_key, coords, nocache) if err == nil { var m map[string]interface{} if err = json.Unmarshal(result, &m); err == nil { From 1e29465af12fdd430815cc5d9b6d83469340f834 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 31 Dec 2018 12:08:15 +0000 Subject: [PATCH 018/109] Fix debug builds (hopefully) --- src/yggdrasil/debug.go | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/src/yggdrasil/debug.go b/src/yggdrasil/debug.go index 4a32eb6..6bd5430 100644 --- a/src/yggdrasil/debug.go +++ b/src/yggdrasil/debug.go @@ -16,6 +16,7 @@ import "fmt" import "net" import "log" import "regexp" +import "encoding/hex" import _ "net/http/pprof" import "net/http" @@ -23,6 +24,7 @@ import "runtime" import "os" import "github.com/yggdrasil-network/yggdrasil-go/src/address" +import "github.com/yggdrasil-network/yggdrasil-go/src/config" import "github.com/yggdrasil-network/yggdrasil-go/src/crypto" import "github.com/yggdrasil-network/yggdrasil-go/src/defaults" @@ -52,7 +54,17 @@ func StartProfiler(log *log.Logger) error { func (c *Core) Init() { bpub, bpriv := crypto.NewBoxKeys() spub, spriv := crypto.NewSigKeys() - c.init(bpub, bpriv, spub, spriv) + hbpub := hex.EncodeToString(bpub[:]) + hbpriv := hex.EncodeToString(bpriv[:]) + hspub := hex.EncodeToString(spub[:]) + hspriv := hex.EncodeToString(spriv[:]) + c.config = config.NodeConfig{ + EncryptionPublicKey: hbpub, + EncryptionPrivateKey: hbpriv, + SigningPublicKey: hspub, + SigningPrivateKey: hspriv, + } + c.init( /*bpub, bpriv, spub, spriv*/ ) c.switchTable.start() c.router.start() } @@ -350,7 +362,7 @@ func (c *Core) DEBUG_init(bpub []byte, bpriv []byte, spub []byte, spriv []byte) { - var boxPub crypto.BoxPubKey + /*var boxPub crypto.BoxPubKey var boxPriv crypto.BoxPrivKey var sigPub crypto.SigPubKey var sigPriv crypto.SigPrivKey @@ -358,7 +370,18 @@ func (c *Core) DEBUG_init(bpub []byte, copy(boxPriv[:], bpriv) copy(sigPub[:], spub) copy(sigPriv[:], spriv) - c.init(&boxPub, &boxPriv, &sigPub, &sigPriv) + c.init(&boxPub, &boxPriv, &sigPub, &sigPriv)*/ + hbpub := hex.EncodeToString(bpub[:]) + hbpriv := hex.EncodeToString(bpriv[:]) + hspub := hex.EncodeToString(spub[:]) + hspriv := hex.EncodeToString(spriv[:]) + c.config = config.NodeConfig{ + EncryptionPublicKey: hbpub, + EncryptionPrivateKey: hbpriv, + SigningPublicKey: hspub, + SigningPrivateKey: hspriv, + } + c.init( /*bpub, bpriv, spub, spriv*/ ) if err := c.router.start(); err != nil { panic(err) @@ -427,7 +450,8 @@ func (c *Core) DEBUG_addSOCKSConn(socksaddr, peeraddr string) { //* func (c *Core) DEBUG_setupAndStartGlobalTCPInterface(addrport string) { - if err := c.tcp.init(c, addrport, 0); err != nil { + c.config.Listen = addrport + if err := c.tcp.init(c /*, addrport, 0*/); err != nil { c.log.Println("Failed to start TCP interface:", err) panic(err) } @@ -474,7 +498,8 @@ func (c *Core) DEBUG_addKCPConn(saddr string) { func (c *Core) DEBUG_setupAndStartAdminInterface(addrport string) { a := admin{} - a.init(c, addrport) + c.config.AdminListen = addrport + a.init(c /*, addrport*/) c.admin = a } From 53aeca8fa27ee8207081cedc67a0cf8c90ab7774 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 1 Jan 2019 23:25:20 +0000 Subject: [PATCH 019/109] Add some simple functions for Swift bindings (iOS) --- cmd/yggdrasil/main.go | 65 +++++++--------------------------------- src/config/config.go | 52 ++++++++++++++++++++++++++++++++ src/yggdrasil/adapter.go | 9 ------ src/yggdrasil/ios.go | 51 +++++++++++++++++++++++++++++++ 4 files changed, 113 insertions(+), 64 deletions(-) create mode 100644 src/yggdrasil/ios.go diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 2b6d2f0..43cf77a 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -2,13 +2,11 @@ package main import ( "bytes" - "encoding/hex" "encoding/json" "flag" "fmt" "io/ioutil" "log" - "math/rand" "os" "os/signal" "regexp" @@ -23,7 +21,6 @@ import ( "github.com/mitchellh/mapstructure" "github.com/yggdrasil-network/yggdrasil-go/src/config" - "github.com/yggdrasil-network/yggdrasil-go/src/defaults" "github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil" ) @@ -34,52 +31,10 @@ type node struct { core Core } -// Generates default configuration. This is used when outputting the -genconf -// parameter and also when using -autoconf. The isAutoconf flag is used to -// determine whether the operating system should select a free port by itself -// (which guarantees that there will not be a conflict with any other services) -// 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 { - // Create a new core. - core := Core{} - // Generate encryption keys. - bpub, bpriv := core.NewEncryptionKeys() - spub, spriv := core.NewSigningKeys() - // Create a node configuration and populate it. - cfg := nodeConfig{} - if isAutoconf { - cfg.Listen = "[::]:0" - } else { - r1 := rand.New(rand.NewSource(time.Now().UnixNano())) - cfg.Listen = fmt.Sprintf("[::]:%d", r1.Intn(65534-32768)+32768) - } - cfg.AdminListen = defaults.GetDefaults().DefaultAdminListen - cfg.EncryptionPublicKey = hex.EncodeToString(bpub[:]) - cfg.EncryptionPrivateKey = hex.EncodeToString(bpriv[:]) - cfg.SigningPublicKey = hex.EncodeToString(spub[:]) - cfg.SigningPrivateKey = hex.EncodeToString(spriv[:]) - cfg.Peers = []string{} - cfg.InterfacePeers = map[string][]string{} - cfg.AllowedEncryptionPublicKeys = []string{} - cfg.MulticastInterfaces = []string{".*"} - cfg.IfName = defaults.GetDefaults().DefaultIfName - cfg.IfMTU = defaults.GetDefaults().DefaultIfMTU - cfg.IfTAPMode = defaults.GetDefaults().DefaultIfTAPMode - cfg.SessionFirewall.Enable = false - cfg.SessionFirewall.AllowFromDirect = true - cfg.SessionFirewall.AllowFromRemote = true - cfg.SwitchOptions.MaxTotalQueueSize = yggdrasil.SwitchQueueTotalMinSize - cfg.NodeInfoPrivacy = false - - return &cfg -} - // Generates a new configuration and returns it in HJSON format. This is used // with -genconf. func doGenconf(isjson bool) string { - cfg := generateConfig(false) + cfg := config.GenerateConfig(false) var bs []byte var err error if isjson { @@ -114,19 +69,19 @@ 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 = generateConfig(true) + cfg = config.GenerateConfig(true) case *useconffile != "" || *useconf: // Use a configuration file. If -useconf, the configuration will be read // from stdin. If -useconffile, the configuration will be read from the // filesystem. - var config []byte + var configjson []byte var err error if *useconffile != "" { // Read the file from the filesystem - config, err = ioutil.ReadFile(*useconffile) + configjson, err = ioutil.ReadFile(*useconffile) } else { // Read the file from stdin. - config, err = ioutil.ReadAll(os.Stdin) + configjson, err = ioutil.ReadAll(os.Stdin) } if err != nil { panic(err) @@ -135,11 +90,11 @@ func main() { // throwing everywhere when it's converting things into UTF-16 for the hell // of it - remove it and decode back down into UTF-8. This is necessary // because hjson doesn't know what to do with UTF-16 and will panic - if bytes.Compare(config[0:2], []byte{0xFF, 0xFE}) == 0 || - bytes.Compare(config[0:2], []byte{0xFE, 0xFF}) == 0 { + if bytes.Compare(configjson[0:2], []byte{0xFF, 0xFE}) == 0 || + bytes.Compare(configjson[0:2], []byte{0xFE, 0xFF}) == 0 { utf := unicode.UTF16(unicode.BigEndian, unicode.UseBOM) decoder := utf.NewDecoder() - config, err = decoder.Bytes(config) + configjson, err = decoder.Bytes(configjson) if err != nil { panic(err) } @@ -148,9 +103,9 @@ func main() { // 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 = generateConfig(false) + cfg = config.GenerateConfig(false) var dat map[string]interface{} - if err := hjson.Unmarshal(config, &dat); err != nil { + if err := hjson.Unmarshal(configjson, &dat); err != nil { panic(err) } confJson, err := json.Marshal(dat) diff --git a/src/config/config.go b/src/config/config.go index 192f435..192b94e 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -1,5 +1,15 @@ 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" +) + // 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."` @@ -53,3 +63,45 @@ type TunnelRouting struct { type SwitchOptions struct { MaxTotalQueueSize uint64 `comment:"Maximum size of all switch queues combined (in bytes)."` } + +// Generates default configuration. This is used when outputting the -genconf +// parameter and also when using -autoconf. The isAutoconf flag is used to +// determine whether the operating system should select a free port by itself +// (which guarantees that there will not be a conflict with any other services) +// 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 { + // Create a new core. + //core := Core{} + // Generate encryption keys. + bpub, bpriv := crypto.NewBoxKeys() + spub, spriv := crypto.NewSigKeys() + // Create a node configuration and populate it. + cfg := NodeConfig{} + if isAutoconf { + cfg.Listen = "[::]:0" + } else { + r1 := rand.New(rand.NewSource(time.Now().UnixNano())) + cfg.Listen = fmt.Sprintf("[::]:%d", r1.Intn(65534-32768)+32768) + } + cfg.AdminListen = defaults.GetDefaults().DefaultAdminListen + cfg.EncryptionPublicKey = hex.EncodeToString(bpub[:]) + cfg.EncryptionPrivateKey = hex.EncodeToString(bpriv[:]) + cfg.SigningPublicKey = hex.EncodeToString(spub[:]) + cfg.SigningPrivateKey = hex.EncodeToString(spriv[:]) + cfg.Peers = []string{} + cfg.InterfacePeers = map[string][]string{} + cfg.AllowedEncryptionPublicKeys = []string{} + cfg.MulticastInterfaces = []string{".*"} + cfg.IfName = defaults.GetDefaults().DefaultIfName + cfg.IfMTU = defaults.GetDefaults().DefaultIfMTU + cfg.IfTAPMode = defaults.GetDefaults().DefaultIfTAPMode + cfg.SessionFirewall.Enable = false + cfg.SessionFirewall.AllowFromDirect = true + cfg.SessionFirewall.AllowFromRemote = true + cfg.SwitchOptions.MaxTotalQueueSize = 4 * 1024 * 1024 + cfg.NodeInfoPrivacy = false + + return &cfg +} diff --git a/src/yggdrasil/adapter.go b/src/yggdrasil/adapter.go index 4a43209..7fb6a19 100644 --- a/src/yggdrasil/adapter.go +++ b/src/yggdrasil/adapter.go @@ -1,17 +1,8 @@ package yggdrasil -// Defines the minimum required functions for an adapter type. -type AdapterInterface interface { - init(core *Core, send chan<- []byte, recv <-chan []byte) - read() error - write() error - close() error -} - // Defines the minimum required struct members for an adapter type (this is // now the base type for tunAdapter in tun.go) type Adapter struct { - AdapterInterface core *Core send chan<- []byte recv <-chan []byte diff --git a/src/yggdrasil/ios.go b/src/yggdrasil/ios.go new file mode 100644 index 0000000..5f32a85 --- /dev/null +++ b/src/yggdrasil/ios.go @@ -0,0 +1,51 @@ +// +build mobile + +package yggdrasil + +import ( + "log" + "os" + "regexp" + + "github.com/yggdrasil-network/yggdrasil-go/src/config" +) + +// This file is meant to "plug the gap" for Gomobile support, as Gomobile +// will not create headers for Swift/Obj-C if they have complex (read: non- +// native) types. Therefore for iOS we will expose some nice simple functions +// to do what we need to do. + +func (c *Core) StartAutoconfigure() error { + logger := log.New(os.Stdout, "", 0) + //logger.Println("Created logger") + //c := Core{} + //logger.Println("Created Core") + nc := config.GenerateConfig(true) + //logger.Println("Generated config") + nc.IfName = "none" + nc.AdminListen = "tcp://[::]:9001" + nc.Peers = []string{} + //logger.Println("Set some config options") + ifceExpr, err := regexp.Compile(".*") + if err == nil { + c.ifceExpr = append(c.ifceExpr, ifceExpr) + } + //logger.Println("Added multicast interface") + if err := c.Start(nc, logger); err != nil { + return err + } + //logger.Println("Started") + address := c.GetAddress() + subnet := c.GetSubnet() + logger.Printf("Your IPv6 address is %s", address.String()) + logger.Printf("Your IPv6 subnet is %s", subnet.String()) + return nil +} + +func (c *Core) GetAddressString() string { + return c.GetAddress().String() +} + +func (c *Core) GetSubetString() string { + return c.GetSubnet().String() +} From 4ff3db2309c61aff8c3aa3c263517ac9bf92c51f Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 2 Jan 2019 18:05:54 +0000 Subject: [PATCH 020/109] Add dummy tun, helper functions --- src/yggdrasil/ios.go | 51 --------------------------------- src/yggdrasil/mobile.go | 56 +++++++++++++++++++++++++++++++++++++ src/yggdrasil/tun.go | 10 ++++--- src/yggdrasil/tun_darwin.go | 2 ++ src/yggdrasil/tun_dummy.go | 19 +++++++++++++ src/yggdrasil/tun_linux.go | 2 ++ src/yggdrasil/tun_other.go | 2 +- 7 files changed, 86 insertions(+), 56 deletions(-) delete mode 100644 src/yggdrasil/ios.go create mode 100644 src/yggdrasil/mobile.go create mode 100644 src/yggdrasil/tun_dummy.go diff --git a/src/yggdrasil/ios.go b/src/yggdrasil/ios.go deleted file mode 100644 index 5f32a85..0000000 --- a/src/yggdrasil/ios.go +++ /dev/null @@ -1,51 +0,0 @@ -// +build mobile - -package yggdrasil - -import ( - "log" - "os" - "regexp" - - "github.com/yggdrasil-network/yggdrasil-go/src/config" -) - -// This file is meant to "plug the gap" for Gomobile support, as Gomobile -// will not create headers for Swift/Obj-C if they have complex (read: non- -// native) types. Therefore for iOS we will expose some nice simple functions -// to do what we need to do. - -func (c *Core) StartAutoconfigure() error { - logger := log.New(os.Stdout, "", 0) - //logger.Println("Created logger") - //c := Core{} - //logger.Println("Created Core") - nc := config.GenerateConfig(true) - //logger.Println("Generated config") - nc.IfName = "none" - nc.AdminListen = "tcp://[::]:9001" - nc.Peers = []string{} - //logger.Println("Set some config options") - ifceExpr, err := regexp.Compile(".*") - if err == nil { - c.ifceExpr = append(c.ifceExpr, ifceExpr) - } - //logger.Println("Added multicast interface") - if err := c.Start(nc, logger); err != nil { - return err - } - //logger.Println("Started") - address := c.GetAddress() - subnet := c.GetSubnet() - logger.Printf("Your IPv6 address is %s", address.String()) - logger.Printf("Your IPv6 subnet is %s", subnet.String()) - return nil -} - -func (c *Core) GetAddressString() string { - return c.GetAddress().String() -} - -func (c *Core) GetSubetString() string { - return c.GetSubnet().String() -} diff --git a/src/yggdrasil/mobile.go b/src/yggdrasil/mobile.go new file mode 100644 index 0000000..9142c36 --- /dev/null +++ b/src/yggdrasil/mobile.go @@ -0,0 +1,56 @@ +// +build mobile + +package yggdrasil + +import ( + "log" + "os" + "regexp" + + "github.com/yggdrasil-network/yggdrasil-go/src/config" + "github.com/yggdrasil-network/yggdrasil-go/src/util" +) + +// This file is meant to "plug the gap" for mobile support, as Gomobile will +// not create headers for Swift/Obj-C etc if they have complex (non-native) +// types. Therefore for iOS we will expose some nice simple functions. Note +// that in the case of iOS we handle reading/writing to/from TUN in Swift +// therefore we use the "dummy" TUN interface instead. + +func (c *Core) StartAutoconfigure() error { + logger := log.New(os.Stdout, "", 0) + nc := config.GenerateConfig(true) + nc.IfName = "dummy" + nc.AdminListen = "tcp://[::]:9001" + nc.Peers = []string{} + if hostname, err := os.Hostname(); err == nil { + nc.NodeInfo = map[string]interface{}{"name": hostname} + } + ifceExpr, err := regexp.Compile(".*") + if err == nil { + c.ifceExpr = append(c.ifceExpr, ifceExpr) + } + if err := c.Start(nc, logger); err != nil { + return err + } + return nil +} + +func (c *Core) GetAddressString() string { + return c.GetAddress().String() +} + +func (c *Core) GetSubnetString() string { + return c.GetSubnet().String() +} + +func (c *Core) RouterRecvPacket() ([]byte, error) { + packet := <-c.router.tun.recv + return packet, nil +} + +func (c *Core) RouterSendPacket(buf []byte) error { + packet := append(util.GetBytes(), buf[:]...) + c.router.tun.send <- packet + return nil +} diff --git a/src/yggdrasil/tun.go b/src/yggdrasil/tun.go index 8ed5333..8c0f91d 100644 --- a/src/yggdrasil/tun.go +++ b/src/yggdrasil/tun.go @@ -47,11 +47,13 @@ func (tun *tunAdapter) init(core *Core, send chan<- []byte, recv <-chan []byte) // Starts the setup process for the TUN/TAP adapter, and if successful, starts // the read/write goroutines to handle packets on that interface. func (tun *tunAdapter) start(ifname string, iftapmode bool, addr string, mtu int) error { - if ifname == "none" { - return nil + if ifname != "none" { + if err := tun.setup(ifname, iftapmode, addr, mtu); err != nil { + return err + } } - if err := tun.setup(ifname, iftapmode, addr, mtu); err != nil { - return err + if ifname == "none" || ifname == "dummy" { + return nil } tun.mutex.Lock() tun.isOpen = true diff --git a/src/yggdrasil/tun_darwin.go b/src/yggdrasil/tun_darwin.go index 943468e..828c01e 100644 --- a/src/yggdrasil/tun_darwin.go +++ b/src/yggdrasil/tun_darwin.go @@ -1,3 +1,5 @@ +// +build !mobile + package yggdrasil // The darwin platform specific tun parts diff --git a/src/yggdrasil/tun_dummy.go b/src/yggdrasil/tun_dummy.go new file mode 100644 index 0000000..234ab1d --- /dev/null +++ b/src/yggdrasil/tun_dummy.go @@ -0,0 +1,19 @@ +// +build mobile + +package yggdrasil + +// This is to catch unsupported platforms +// If your platform supports tun devices, you could try configuring it manually + +// Creates the TUN/TAP adapter, if supported by the Water library. Note that +// no guarantees are made at this point on an unsupported platform. +func (tun *tunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error { + tun.mtu = getSupportedMTU(mtu) + 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 { + return nil +} diff --git a/src/yggdrasil/tun_linux.go b/src/yggdrasil/tun_linux.go index 7a7c9cb..8ccdd30 100644 --- a/src/yggdrasil/tun_linux.go +++ b/src/yggdrasil/tun_linux.go @@ -1,3 +1,5 @@ +// +build !mobile + package yggdrasil // The linux platform specific tun parts diff --git a/src/yggdrasil/tun_other.go b/src/yggdrasil/tun_other.go index 625f9cd..22058c1 100644 --- a/src/yggdrasil/tun_other.go +++ b/src/yggdrasil/tun_other.go @@ -1,4 +1,4 @@ -// +build !linux,!darwin,!windows,!openbsd,!freebsd,!netbsd +// +build !linux,!darwin,!windows,!openbsd,!freebsd,!netbsd,!mobile package yggdrasil From f7b0a85b5e19d8e95a15629f54c640a6f5eec10a Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 2 Jan 2019 23:15:36 +0000 Subject: [PATCH 021/109] Add StartJSON --- src/yggdrasil/mobile.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/yggdrasil/mobile.go b/src/yggdrasil/mobile.go index 9142c36..c4bc8de 100644 --- a/src/yggdrasil/mobile.go +++ b/src/yggdrasil/mobile.go @@ -7,6 +7,8 @@ import ( "os" "regexp" + hjson "github.com/hjson/hjson-go" + "github.com/mitchellh/mapstructure" "github.com/yggdrasil-network/yggdrasil-go/src/config" "github.com/yggdrasil-network/yggdrasil-go/src/util" ) @@ -36,6 +38,23 @@ func (c *Core) StartAutoconfigure() error { return nil } +func (c *Core) StartJSON(configjson []byte) error { + logger := log.New(os.Stdout, "", 0) + nc := config.GenerateConfig(false) + var dat map[string]interface{} + if err := hjson.Unmarshal(configjson, &dat); err != nil { + return err + } + if err := mapstructure.Decode(dat, &nc); err != nil { + return err + } + nc.IfName = "dummy" + if err := c.Start(nc, logger); err != nil { + return err + } + return nil +} + func (c *Core) GetAddressString() string { return c.GetAddress().String() } From d10a0d6137811991d7c791a0a21f0eac7d36dd21 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 3 Jan 2019 22:50:08 +0000 Subject: [PATCH 022/109] Add GenerateConfigJSON, fix StartJSON --- src/yggdrasil/mobile.go | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/yggdrasil/mobile.go b/src/yggdrasil/mobile.go index c4bc8de..f579f53 100644 --- a/src/yggdrasil/mobile.go +++ b/src/yggdrasil/mobile.go @@ -3,6 +3,7 @@ package yggdrasil import ( + "encoding/json" "log" "os" "regexp" @@ -23,7 +24,7 @@ func (c *Core) StartAutoconfigure() error { logger := log.New(os.Stdout, "", 0) nc := config.GenerateConfig(true) nc.IfName = "dummy" - nc.AdminListen = "tcp://[::]:9001" + nc.AdminListen = "tcp://localhost:9001" nc.Peers = []string{} if hostname, err := os.Hostname(); err == nil { nc.NodeInfo = map[string]interface{}{"name": hostname} @@ -49,12 +50,29 @@ func (c *Core) StartJSON(configjson []byte) error { return err } nc.IfName = "dummy" + for _, ll := range nc.MulticastInterfaces { + ifceExpr, err := regexp.Compile(ll) + if err != nil { + panic(err) + } + c.AddMulticastInterfaceExpr(ifceExpr) + } if err := c.Start(nc, logger); err != nil { return err } return nil } +func GenerateConfigJSON() []byte { + nc := config.GenerateConfig(false) + nc.IfName = "dummy" + if json, err := json.Marshal(nc); err == nil { + return json + } else { + return nil + } +} + func (c *Core) GetAddressString() string { return c.GetAddress().String() } From f29a098488d885ea16db01879a7cc75b73b796e3 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Fri, 4 Jan 2019 17:14:40 +0000 Subject: [PATCH 023/109] Add experimental dummy interface for AWDL --- build | 7 +++- src/yggdrasil/awdl.go | 86 +++++++++++++++++++++++++++++++++++++++++ src/yggdrasil/core.go | 6 +++ src/yggdrasil/mobile.go | 9 +++++ 4 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 src/yggdrasil/awdl.go diff --git a/build b/build index e463c85..a3a679f 100755 --- a/build +++ b/build @@ -6,12 +6,13 @@ PKGVER=${PKGVER:-$(sh contrib/semver/version.sh --bare)} LDFLAGS="-X $PKGSRC.buildName=$PKGNAME -X $PKGSRC.buildVersion=$PKGVER" -while getopts "udtc:l:" option +while getopts "udmtc:l:" option do case "${option}" in u) UPX=true;; d) DEBUG=true;; + m) MOBILE=true;; t) TABLES=true;; c) GCFLAGS="$GCFLAGS $OPTARG";; l) LDFLAGS="$LDFLAGS $OPTARG";; @@ -25,7 +26,9 @@ fi for CMD in `ls cmd/` ; do echo "Building: $CMD" - if [ $DEBUG ]; then + if [ $MOBILE ]; then + go build -ldflags="$LDFLAGS" -gcflags="$GCFLAGS" -tags mobile -v ./cmd/$CMD + elif [ $DEBUG ]; then go build -ldflags="$LDFLAGS" -gcflags="$GCFLAGS" -tags debug -v ./cmd/$CMD else go build -ldflags="$LDFLAGS $STRIP" -gcflags="$GCFLAGS" -v ./cmd/$CMD diff --git a/src/yggdrasil/awdl.go b/src/yggdrasil/awdl.go new file mode 100644 index 0000000..b07c2fc --- /dev/null +++ b/src/yggdrasil/awdl.go @@ -0,0 +1,86 @@ +package yggdrasil + +import ( + "sync" + "github.com/yggdrasil-network/yggdrasil-go/src/crypto" +) + +type awdl struct { + core *Core + mutex sync.RWMutex // protects interfaces below + interfaces map[string]*awdlInterface +} + +type awdlInterface struct { + awdl *awdl + recv <-chan []byte // traffic received from the network + send chan<- []byte // traffic to send to the network + shutdown chan bool + peer *peer +} + +func (l *awdl) init(c *Core) error { + l.core = c + l.mutex.Lock() + l.interfaces = make(map[string]*awdlInterface) + l.mutex.Unlock() + + return nil +} + +func (l *awdl) create(boxPubKey *crypto.BoxPubKey, sigPubKey *crypto.SigPubKey, name string) *awdlInterface { + shared := crypto.GetSharedKey(&l.core.boxPriv, boxPubKey) + intf := awdlInterface{ + recv: make(<-chan []byte), + send: make(chan<- []byte), + shutdown: make(chan bool), + peer: l.core.peers.newPeer(boxPubKey, sigPubKey, shared, name), + } + if intf.peer != nil { + l.mutex.Lock() + l.interfaces[name] = &intf + l.mutex.Unlock() + intf.peer.linkOut = make(chan []byte, 1) // protocol traffic + intf.peer.out = func(msg []byte) { + defer func() { recover() }() + intf.send <- msg + } + go intf.handler() + l.core.switchTable.idleIn <- intf.peer.port + return &intf + } + return nil +} + +func (l *awdl) getInterface(identity string) *awdlInterface { + l.mutex.RLock() + defer l.mutex.RUnlock() + if intf, ok := l.interfaces[identity]; ok { + return intf + } + return nil +} + +func (l *awdl) shutdown(identity string) { + if intf, ok := l.interfaces[identity]; ok { + intf.shutdown <- true + l.core.peers.removePeer(intf.peer.port) + l.mutex.Lock() + delete(l.interfaces, identity) + l.mutex.Unlock() + } +} + +func (ai *awdlInterface) handler() { + for { + select { + case p := <-ai.peer.linkOut: + ai.send <- p + case r := <-ai.recv: // traffic received from AWDL + ai.peer.handlePacket(r) + case <-ai.shutdown: + return + default: + } + } +} diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index e38274f..b99d1f2 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -35,6 +35,7 @@ type Core struct { multicast multicast nodeinfo nodeinfo tcp tcpInterface + awdl awdl log *log.Logger ifceExpr []*regexp.Regexp // the zone of link-local IPv6 peers must match this } @@ -132,6 +133,11 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error { return err } + if err := c.awdl.init(c); err != nil { + c.log.Println("Failed to start AWDL interface") + return err + } + if nc.SwitchOptions.MaxTotalQueueSize >= SwitchQueueTotalMinSize { c.switchTable.queueTotalMaxSize = nc.SwitchOptions.MaxTotalQueueSize } diff --git a/src/yggdrasil/mobile.go b/src/yggdrasil/mobile.go index f579f53..36a98ea 100644 --- a/src/yggdrasil/mobile.go +++ b/src/yggdrasil/mobile.go @@ -11,6 +11,7 @@ import ( hjson "github.com/hjson/hjson-go" "github.com/mitchellh/mapstructure" "github.com/yggdrasil-network/yggdrasil-go/src/config" + "github.com/yggdrasil-network/yggdrasil-go/src/crypto" "github.com/yggdrasil-network/yggdrasil-go/src/util" ) @@ -91,3 +92,11 @@ func (c *Core) RouterSendPacket(buf []byte) error { c.router.tun.send <- packet return nil } + +func (c *Core) AWDLCreateInterface(boxPubKey []byte, sigPubKey []byte, name string) { + var box crypto.BoxPubKey + var sig crypto.SigPubKey + copy(box[:crypto.BoxPubKeyLen], boxPubKey[:]) + copy(sig[:crypto.SigPubKeyLen], sigPubKey[:]) + c.awdl.create(&box, &sig, name) +} From 3878197a59aa51b6713f1e95531956591d1549c1 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Fri, 4 Jan 2019 17:23:37 +0000 Subject: [PATCH 024/109] gofmt --- src/yggdrasil/awdl.go | 121 +++++++++++++++++++++--------------------- 1 file changed, 61 insertions(+), 60 deletions(-) diff --git a/src/yggdrasil/awdl.go b/src/yggdrasil/awdl.go index b07c2fc..da10d58 100644 --- a/src/yggdrasil/awdl.go +++ b/src/yggdrasil/awdl.go @@ -1,86 +1,87 @@ package yggdrasil import ( - "sync" - "github.com/yggdrasil-network/yggdrasil-go/src/crypto" + "sync" + + "github.com/yggdrasil-network/yggdrasil-go/src/crypto" ) type awdl struct { - core *Core - mutex sync.RWMutex // protects interfaces below - interfaces map[string]*awdlInterface + core *Core + mutex sync.RWMutex // protects interfaces below + interfaces map[string]*awdlInterface } type awdlInterface struct { - awdl *awdl - recv <-chan []byte // traffic received from the network - send chan<- []byte // traffic to send to the network - shutdown chan bool - peer *peer + awdl *awdl + recv <-chan []byte // traffic received from the network + send chan<- []byte // traffic to send to the network + shutdown chan bool + peer *peer } func (l *awdl) init(c *Core) error { - l.core = c - l.mutex.Lock() - l.interfaces = make(map[string]*awdlInterface) - l.mutex.Unlock() + l.core = c + l.mutex.Lock() + l.interfaces = make(map[string]*awdlInterface) + l.mutex.Unlock() - return nil + return nil } func (l *awdl) create(boxPubKey *crypto.BoxPubKey, sigPubKey *crypto.SigPubKey, name string) *awdlInterface { - shared := crypto.GetSharedKey(&l.core.boxPriv, boxPubKey) - intf := awdlInterface{ - recv: make(<-chan []byte), - send: make(chan<- []byte), - shutdown: make(chan bool), - peer: l.core.peers.newPeer(boxPubKey, sigPubKey, shared, name), - } - if intf.peer != nil { - l.mutex.Lock() - l.interfaces[name] = &intf - l.mutex.Unlock() - intf.peer.linkOut = make(chan []byte, 1) // protocol traffic - intf.peer.out = func(msg []byte) { - defer func() { recover() }() - intf.send <- msg - } - go intf.handler() - l.core.switchTable.idleIn <- intf.peer.port - return &intf - } - return nil + shared := crypto.GetSharedKey(&l.core.boxPriv, boxPubKey) + intf := awdlInterface{ + recv: make(<-chan []byte), + send: make(chan<- []byte), + shutdown: make(chan bool), + peer: l.core.peers.newPeer(boxPubKey, sigPubKey, shared, name), + } + if intf.peer != nil { + l.mutex.Lock() + l.interfaces[name] = &intf + l.mutex.Unlock() + intf.peer.linkOut = make(chan []byte, 1) + intf.peer.out = func(msg []byte) { + defer func() { recover() }() + intf.send <- msg + } + go intf.handler() + l.core.switchTable.idleIn <- intf.peer.port + return &intf + } + return nil } func (l *awdl) getInterface(identity string) *awdlInterface { - l.mutex.RLock() - defer l.mutex.RUnlock() - if intf, ok := l.interfaces[identity]; ok { - return intf - } - return nil + l.mutex.RLock() + defer l.mutex.RUnlock() + if intf, ok := l.interfaces[identity]; ok { + return intf + } + return nil } func (l *awdl) shutdown(identity string) { - if intf, ok := l.interfaces[identity]; ok { - intf.shutdown <- true - l.core.peers.removePeer(intf.peer.port) - l.mutex.Lock() - delete(l.interfaces, identity) - l.mutex.Unlock() - } + if intf, ok := l.interfaces[identity]; ok { + intf.shutdown <- true + l.core.peers.removePeer(intf.peer.port) + l.mutex.Lock() + delete(l.interfaces, identity) + l.mutex.Unlock() + } } func (ai *awdlInterface) handler() { - for { - select { - case p := <-ai.peer.linkOut: - ai.send <- p - case r := <-ai.recv: // traffic received from AWDL - ai.peer.handlePacket(r) - case <-ai.shutdown: - return - default: - } - } + for { + select { + case p := <-ai.peer.linkOut: + ai.send <- p + case r := <-ai.recv: // traffic received from AWDL + ai.peer.handlePacket(r) + case <-ai.shutdown: + return + default: + } + } } From 5a36b4723ab662fbfeb6aba4d8e7124404d2956d Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Fri, 4 Jan 2019 17:41:03 +0000 Subject: [PATCH 025/109] Add AWDL calls to exposed API, handle proto traffic first --- src/yggdrasil/awdl.go | 15 +++++++++++++++ src/yggdrasil/mobile.go | 17 +++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/src/yggdrasil/awdl.go b/src/yggdrasil/awdl.go index da10d58..84cd3fb 100644 --- a/src/yggdrasil/awdl.go +++ b/src/yggdrasil/awdl.go @@ -74,9 +74,24 @@ func (l *awdl) shutdown(identity string) { func (ai *awdlInterface) handler() { for { + /*timerInterval := tcp_ping_interval + timer := time.NewTimer(timerInterval) + defer timer.Stop()*/ select { case p := <-ai.peer.linkOut: ai.send <- p + continue + default: + } + /*timer.Stop() + select { + case <-timer.C: + default: + } + timer.Reset(timerInterval)*/ + select { + //case _ = <-timer.C: + // ai.send <- nil case r := <-ai.recv: // traffic received from AWDL ai.peer.handlePacket(r) case <-ai.shutdown: diff --git a/src/yggdrasil/mobile.go b/src/yggdrasil/mobile.go index 36a98ea..44886f8 100644 --- a/src/yggdrasil/mobile.go +++ b/src/yggdrasil/mobile.go @@ -4,6 +4,7 @@ package yggdrasil import ( "encoding/json" + "errors" "log" "os" "regexp" @@ -100,3 +101,19 @@ func (c *Core) AWDLCreateInterface(boxPubKey []byte, sigPubKey []byte, name stri copy(sig[:crypto.SigPubKeyLen], sigPubKey[:]) c.awdl.create(&box, &sig, name) } + +func (c *Core) AWDLRecvPacket(identity string) ([]byte, error) { + if intf := c.awdl.getInterface(identity); intf != nil { + return <-intf.recv, nil + } + return nil, errors.New("identity not known: " + identity) +} + +func (c *Core) AWDLSendPacket(identity string, buf []byte) error { + packet := append(util.GetBytes(), buf[:]...) + if intf := c.awdl.getInterface(identity); intf != nil { + intf.send <- packet + return nil + } + return errors.New("identity not known: " + identity) +} From 00bf71a09ade86bc5db484497a555d8cd10851b8 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Fri, 4 Jan 2019 23:31:44 +0000 Subject: [PATCH 026/109] Fight me Swift and your hexadecimal strings --- src/yggdrasil/mobile.go | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/yggdrasil/mobile.go b/src/yggdrasil/mobile.go index 44886f8..af7abb7 100644 --- a/src/yggdrasil/mobile.go +++ b/src/yggdrasil/mobile.go @@ -3,6 +3,7 @@ package yggdrasil import ( + "encoding/hex" "encoding/json" "errors" "log" @@ -94,12 +95,24 @@ func (c *Core) RouterSendPacket(buf []byte) error { return nil } -func (c *Core) AWDLCreateInterface(boxPubKey []byte, sigPubKey []byte, name string) { - var box crypto.BoxPubKey - var sig crypto.SigPubKey - copy(box[:crypto.BoxPubKeyLen], boxPubKey[:]) - copy(sig[:crypto.SigPubKeyLen], sigPubKey[:]) - c.awdl.create(&box, &sig, name) +func (c *Core) AWDLCreateInterface(boxPubKey string, sigPubKey string, name string) error { + var boxPub crypto.BoxPubKey + var sigPub crypto.SigPubKey + boxPubHex, err := hex.DecodeString(boxPubKey) + if err != nil { + return err + } + sigPubHex, err := hex.DecodeString(sigPubKey) + if err != nil { + return err + } + copy(boxPub[:], boxPubHex) + copy(sigPub[:], sigPubHex) + if intf := c.awdl.create(&boxPub, &sigPub, name); intf != nil { + return nil + } else { + return errors.New("No interface was created") + } } func (c *Core) AWDLRecvPacket(identity string) ([]byte, error) { From 4363283a6f6ec33a462aa37709ca6a35c90e1603 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 5 Jan 2019 00:32:28 +0000 Subject: [PATCH 027/109] Notify switch idle --- src/yggdrasil/awdl.go | 2 ++ src/yggdrasil/mobile.go | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/src/yggdrasil/awdl.go b/src/yggdrasil/awdl.go index 84cd3fb..3bd6360 100644 --- a/src/yggdrasil/awdl.go +++ b/src/yggdrasil/awdl.go @@ -45,6 +45,7 @@ func (l *awdl) create(boxPubKey *crypto.BoxPubKey, sigPubKey *crypto.SigPubKey, intf.peer.out = func(msg []byte) { defer func() { recover() }() intf.send <- msg + l.core.switchTable.idleIn <- intf.peer.port } go intf.handler() l.core.switchTable.idleIn <- intf.peer.port @@ -80,6 +81,7 @@ func (ai *awdlInterface) handler() { select { case p := <-ai.peer.linkOut: ai.send <- p + ai.awdl.core.switchTable.idleIn <- ai.peer.port continue default: } diff --git a/src/yggdrasil/mobile.go b/src/yggdrasil/mobile.go index af7abb7..a81d82c 100644 --- a/src/yggdrasil/mobile.go +++ b/src/yggdrasil/mobile.go @@ -115,6 +115,10 @@ func (c *Core) AWDLCreateInterface(boxPubKey string, sigPubKey string, name stri } } +func (c *Core) AWDLShutdownInterface(name string) { + c.awdl.shutdown(name) +} + func (c *Core) AWDLRecvPacket(identity string) ([]byte, error) { if intf := c.awdl.getInterface(identity); intf != nil { return <-intf.recv, nil From 1170ea9e989170467472860d2f369a3027ecf075 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 5 Jan 2019 00:52:41 +0000 Subject: [PATCH 028/109] Start linkloop --- src/yggdrasil/awdl.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/yggdrasil/awdl.go b/src/yggdrasil/awdl.go index 3bd6360..aac7f7d 100644 --- a/src/yggdrasil/awdl.go +++ b/src/yggdrasil/awdl.go @@ -47,6 +47,10 @@ func (l *awdl) create(boxPubKey *crypto.BoxPubKey, sigPubKey *crypto.SigPubKey, intf.send <- msg l.core.switchTable.idleIn <- intf.peer.port } + intf.peer.close = func() { + close(intf.send) + } + go intf.peer.linkLoop() go intf.handler() l.core.switchTable.idleIn <- intf.peer.port return &intf From 90366dd8537c93f44691f9181229d38d9efcceec Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 5 Jan 2019 01:02:22 +0000 Subject: [PATCH 029/109] Update handler behavior --- src/yggdrasil/awdl.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/yggdrasil/awdl.go b/src/yggdrasil/awdl.go index aac7f7d..d3d69d5 100644 --- a/src/yggdrasil/awdl.go +++ b/src/yggdrasil/awdl.go @@ -102,7 +102,10 @@ func (ai *awdlInterface) handler() { ai.peer.handlePacket(r) case <-ai.shutdown: return - default: + case p := <-ai.peer.linkOut: + ai.send <- p + ai.awdl.core.switchTable.idleIn <- ai.peer.port + continue } } } From 6bbd8c1b30865f10448251b52c8b4c3c23740d0c Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 5 Jan 2019 12:06:45 +0000 Subject: [PATCH 030/109] Rethink channels, more error throwing --- src/yggdrasil/awdl.go | 70 ++++++++++++++++++++++++----------------- src/yggdrasil/core.go | 2 +- src/yggdrasil/mobile.go | 18 ++++++----- 3 files changed, 53 insertions(+), 37 deletions(-) diff --git a/src/yggdrasil/awdl.go b/src/yggdrasil/awdl.go index d3d69d5..f38d101 100644 --- a/src/yggdrasil/awdl.go +++ b/src/yggdrasil/awdl.go @@ -1,9 +1,14 @@ package yggdrasil import ( + "errors" + "fmt" "sync" + "sync/atomic" + "time" "github.com/yggdrasil-network/yggdrasil-go/src/crypto" + "github.com/yggdrasil-network/yggdrasil-go/src/util" ) type awdl struct { @@ -14,8 +19,8 @@ type awdl struct { type awdlInterface struct { awdl *awdl - recv <-chan []byte // traffic received from the network - send chan<- []byte // traffic to send to the network + fromAWDL chan []byte + toAWDL chan []byte shutdown chan bool peer *peer } @@ -29,11 +34,11 @@ func (l *awdl) init(c *Core) error { return nil } -func (l *awdl) create(boxPubKey *crypto.BoxPubKey, sigPubKey *crypto.SigPubKey, name string) *awdlInterface { +func (l *awdl) create(boxPubKey *crypto.BoxPubKey, sigPubKey *crypto.SigPubKey, name string) (*awdlInterface, error) { shared := crypto.GetSharedKey(&l.core.boxPriv, boxPubKey) intf := awdlInterface{ - recv: make(<-chan []byte), - send: make(chan<- []byte), + fromAWDL: make(chan []byte, 32), + toAWDL: make(chan []byte, 32), shutdown: make(chan bool), peer: l.core.peers.newPeer(boxPubKey, sigPubKey, shared, name), } @@ -41,21 +46,21 @@ func (l *awdl) create(boxPubKey *crypto.BoxPubKey, sigPubKey *crypto.SigPubKey, l.mutex.Lock() l.interfaces[name] = &intf l.mutex.Unlock() - intf.peer.linkOut = make(chan []byte, 1) + intf.peer.linkOut = make(chan []byte, 1) // protocol traffic intf.peer.out = func(msg []byte) { defer func() { recover() }() - intf.send <- msg - l.core.switchTable.idleIn <- intf.peer.port - } + intf.toAWDL <- msg + } // called by peer.sendPacket() + l.core.switchTable.idleIn <- intf.peer.port // notify switch that we're idle intf.peer.close = func() { - close(intf.send) + close(intf.fromAWDL) + close(intf.toAWDL) } - go intf.peer.linkLoop() - go intf.handler() - l.core.switchTable.idleIn <- intf.peer.port - return &intf + go intf.handler() // start listening for packets from switch + go intf.peer.linkLoop() // start link loop + return &intf, nil } - return nil + return nil, errors.New("l.core.peers.newPeer failed") } func (l *awdl) getInterface(identity string) *awdlInterface { @@ -67,45 +72,52 @@ func (l *awdl) getInterface(identity string) *awdlInterface { return nil } -func (l *awdl) shutdown(identity string) { +func (l *awdl) shutdown(identity string) error { if intf, ok := l.interfaces[identity]; ok { intf.shutdown <- true l.core.peers.removePeer(intf.peer.port) l.mutex.Lock() delete(l.interfaces, identity) l.mutex.Unlock() + return nil + } else { + return errors.New(fmt.Sprintf("Interface '%s' doesn't exist or already shutdown", identity)) } } func (ai *awdlInterface) handler() { + send := func(msg []byte) { + ai.toAWDL <- msg + atomic.AddUint64(&ai.peer.bytesSent, uint64(len(msg))) + util.PutBytes(msg) + } for { - /*timerInterval := tcp_ping_interval + timerInterval := tcp_ping_interval timer := time.NewTimer(timerInterval) - defer timer.Stop()*/ + defer timer.Stop() select { case p := <-ai.peer.linkOut: - ai.send <- p - ai.awdl.core.switchTable.idleIn <- ai.peer.port + send(p) continue default: } - /*timer.Stop() + timer.Stop() select { case <-timer.C: default: } - timer.Reset(timerInterval)*/ + timer.Reset(timerInterval) select { - //case _ = <-timer.C: - // ai.send <- nil - case r := <-ai.recv: // traffic received from AWDL + case _ = <-timer.C: + send([]byte{'H', 'E', 'L', 'L', 'O'}) + case p := <-ai.peer.linkOut: + send(p) + continue + case r := <-ai.fromAWDL: ai.peer.handlePacket(r) + ai.awdl.core.switchTable.idleIn <- ai.peer.port case <-ai.shutdown: return - case p := <-ai.peer.linkOut: - ai.send <- p - ai.awdl.core.switchTable.idleIn <- ai.peer.port - continue } } } diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index b99d1f2..a1c2127 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -35,7 +35,7 @@ type Core struct { multicast multicast nodeinfo nodeinfo tcp tcpInterface - awdl awdl + awdl awdl log *log.Logger ifceExpr []*regexp.Regexp // the zone of link-local IPv6 peers must match this } diff --git a/src/yggdrasil/mobile.go b/src/yggdrasil/mobile.go index a81d82c..1c9a84f 100644 --- a/src/yggdrasil/mobile.go +++ b/src/yggdrasil/mobile.go @@ -108,20 +108,24 @@ func (c *Core) AWDLCreateInterface(boxPubKey string, sigPubKey string, name stri } copy(boxPub[:], boxPubHex) copy(sigPub[:], sigPubHex) - if intf := c.awdl.create(&boxPub, &sigPub, name); intf != nil { - return nil + if intf, err := c.awdl.create(&boxPub, &sigPub, name); err == nil { + if intf != nil { + return err + } else { + return errors.New("c.awdl.create didn't return an interface") + } } else { - return errors.New("No interface was created") + return err } } -func (c *Core) AWDLShutdownInterface(name string) { - c.awdl.shutdown(name) +func (c *Core) AWDLShutdownInterface(name string) error { + return c.awdl.shutdown(name) } func (c *Core) AWDLRecvPacket(identity string) ([]byte, error) { if intf := c.awdl.getInterface(identity); intf != nil { - return <-intf.recv, nil + return <-intf.toAWDL, nil } return nil, errors.New("identity not known: " + identity) } @@ -129,7 +133,7 @@ func (c *Core) AWDLRecvPacket(identity string) ([]byte, error) { func (c *Core) AWDLSendPacket(identity string, buf []byte) error { packet := append(util.GetBytes(), buf[:]...) if intf := c.awdl.getInterface(identity); intf != nil { - intf.send <- packet + intf.fromAWDL <- packet return nil } return errors.New("identity not known: " + identity) From 87362a21e27527acee12bb8ad790bfc2d2eb3b59 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 5 Jan 2019 21:59:07 +0000 Subject: [PATCH 031/109] Access NSLog through Cgo for iOS NetworkExtension logging --- src/yggdrasil/awdl.go | 3 ++- src/yggdrasil/mobile.go | 6 ++++-- src/yggdrasil/mobile_ios.go | 25 +++++++++++++++++++++++++ src/yggdrasil/peer.go | 1 + 4 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 src/yggdrasil/mobile_ios.go diff --git a/src/yggdrasil/awdl.go b/src/yggdrasil/awdl.go index f38d101..a2ec283 100644 --- a/src/yggdrasil/awdl.go +++ b/src/yggdrasil/awdl.go @@ -109,11 +109,12 @@ func (ai *awdlInterface) handler() { timer.Reset(timerInterval) select { case _ = <-timer.C: - send([]byte{'H', 'E', 'L', 'L', 'O'}) + send([]byte{}) case p := <-ai.peer.linkOut: send(p) continue case r := <-ai.fromAWDL: + //_ = append(util.GetBytes(), r...) ai.peer.handlePacket(r) ai.awdl.core.switchTable.idleIn <- ai.peer.port case <-ai.shutdown: diff --git a/src/yggdrasil/mobile.go b/src/yggdrasil/mobile.go index 1c9a84f..2f8a323 100644 --- a/src/yggdrasil/mobile.go +++ b/src/yggdrasil/mobile.go @@ -24,7 +24,8 @@ import ( // therefore we use the "dummy" TUN interface instead. func (c *Core) StartAutoconfigure() error { - logger := log.New(os.Stdout, "", 0) + mobilelog := MobileLogger{} + logger := log.New(mobilelog, "", 0) nc := config.GenerateConfig(true) nc.IfName = "dummy" nc.AdminListen = "tcp://localhost:9001" @@ -43,7 +44,8 @@ func (c *Core) StartAutoconfigure() error { } func (c *Core) StartJSON(configjson []byte) error { - logger := log.New(os.Stdout, "", 0) + mobilelog := MobileLogger{} + logger := log.New(mobilelog, "", 0) nc := config.GenerateConfig(false) var dat map[string]interface{} if err := hjson.Unmarshal(configjson, &dat); err != nil { diff --git a/src/yggdrasil/mobile_ios.go b/src/yggdrasil/mobile_ios.go new file mode 100644 index 0000000..4abc6e9 --- /dev/null +++ b/src/yggdrasil/mobile_ios.go @@ -0,0 +1,25 @@ +// +build mobile,darwin + +package yggdrasil + +/* +#cgo CFLAGS: -x objective-c +#cgo LDFLAGS: -framework Foundation +#import +void Log(const char *text) { + NSString *nss = [NSString stringWithUTF8String:text]; + NSLog(@"%@", nss); +} +*/ +import "C" +import "unsafe" + +type MobileLogger struct { +} + +func (nsl MobileLogger) Write(p []byte) (n int, err error) { + p = append(p, 0) + cstr := (*C.char)(unsafe.Pointer(&p[0])) + C.Log(cstr) + return len(p), nil +} diff --git a/src/yggdrasil/peer.go b/src/yggdrasil/peer.go index a2b94b6..333561e 100644 --- a/src/yggdrasil/peer.go +++ b/src/yggdrasil/peer.go @@ -217,6 +217,7 @@ func (p *peer) handlePacket(packet []byte) { default: util.PutBytes(packet) } + return } // Called to handle traffic or protocolTraffic packets. From 2034c9eab938e8f589c6f6b0798981b1194b7d7d Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 5 Jan 2019 23:00:49 +0000 Subject: [PATCH 032/109] Fix missing pointer from awdlInterface to awdl --- src/yggdrasil/awdl.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yggdrasil/awdl.go b/src/yggdrasil/awdl.go index a2ec283..f4ace36 100644 --- a/src/yggdrasil/awdl.go +++ b/src/yggdrasil/awdl.go @@ -37,6 +37,7 @@ func (l *awdl) init(c *Core) error { func (l *awdl) create(boxPubKey *crypto.BoxPubKey, sigPubKey *crypto.SigPubKey, name string) (*awdlInterface, error) { shared := crypto.GetSharedKey(&l.core.boxPriv, boxPubKey) intf := awdlInterface{ + awdl: l, fromAWDL: make(chan []byte, 32), toAWDL: make(chan []byte, 32), shutdown: make(chan bool), @@ -114,7 +115,6 @@ func (ai *awdlInterface) handler() { send(p) continue case r := <-ai.fromAWDL: - //_ = append(util.GetBytes(), r...) ai.peer.handlePacket(r) ai.awdl.core.switchTable.idleIn <- ai.peer.port case <-ai.shutdown: From 6efac9a377dd0c3d35e33e4a8ab828329a8173cb Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 6 Jan 2019 14:12:10 +0000 Subject: [PATCH 033/109] Add contexts --- src/yggdrasil/awdl.go | 33 ++++++++++++++++++++++++++++----- src/yggdrasil/mobile.go | 31 ++++++++++++++++++++++++++++--- 2 files changed, 56 insertions(+), 8 deletions(-) diff --git a/src/yggdrasil/awdl.go b/src/yggdrasil/awdl.go index f4ace36..65e84cb 100644 --- a/src/yggdrasil/awdl.go +++ b/src/yggdrasil/awdl.go @@ -34,14 +34,37 @@ func (l *awdl) init(c *Core) error { return nil } -func (l *awdl) create(boxPubKey *crypto.BoxPubKey, sigPubKey *crypto.SigPubKey, name string) (*awdlInterface, error) { +func (l *awdl) create(fromAWDL chan []byte, toAWDL chan []byte, boxPubKey *crypto.BoxPubKey, sigPubKey *crypto.SigPubKey, name string) (*awdlInterface, error) { + /* + myLinkPub, myLinkPriv := crypto.NewBoxKeys() + meta := version_getBaseMetadata() + meta.box = l.core.boxPub + meta.sig = l.core.sigPub + meta.link = *myLinkPub + metaBytes := meta.encode() + l.core.log.Println("toAWDL <- metaBytes") + toAWDL <- metaBytes + l.core.log.Println("metaBytes = <-fromAWDL") + metaBytes = <-fromAWDL + l.core.log.Println("version_metadata{}") + meta = version_metadata{} + if !meta.decode(metaBytes) || !meta.check() { + return nil, errors.New("Metadata decode failure") + } + base := version_getBaseMetadata() + if meta.ver > base.ver || meta.ver == base.ver && meta.minorVer > base.minorVer { + return nil, errors.New("Failed to connect to node: " + name + " version: " + fmt.Sprintf("%d.%d", meta.ver, meta.minorVer)) + } + shared := crypto.GetSharedKey(myLinkPriv, &meta.link) + */ shared := crypto.GetSharedKey(&l.core.boxPriv, boxPubKey) intf := awdlInterface{ awdl: l, - fromAWDL: make(chan []byte, 32), - toAWDL: make(chan []byte, 32), + fromAWDL: fromAWDL, + toAWDL: toAWDL, shutdown: make(chan bool), peer: l.core.peers.newPeer(boxPubKey, sigPubKey, shared, name), + //peer: l.core.peers.newPeer(&meta.box, &meta.sig, shared, name), } if intf.peer != nil { l.mutex.Lock() @@ -57,8 +80,8 @@ func (l *awdl) create(boxPubKey *crypto.BoxPubKey, sigPubKey *crypto.SigPubKey, close(intf.fromAWDL) close(intf.toAWDL) } - go intf.handler() // start listening for packets from switch - go intf.peer.linkLoop() // start link loop + go intf.handler() + go intf.peer.linkLoop() return &intf, nil } return nil, errors.New("l.core.peers.newPeer failed") diff --git a/src/yggdrasil/mobile.go b/src/yggdrasil/mobile.go index 2f8a323..300d132 100644 --- a/src/yggdrasil/mobile.go +++ b/src/yggdrasil/mobile.go @@ -98,29 +98,47 @@ func (c *Core) RouterSendPacket(buf []byte) error { } func (c *Core) AWDLCreateInterface(boxPubKey string, sigPubKey string, name string) error { + fromAWDL := make(chan []byte, 32) + toAWDL := make(chan []byte, 32) + var boxPub crypto.BoxPubKey var sigPub crypto.SigPubKey boxPubHex, err := hex.DecodeString(boxPubKey) if err != nil { + c.log.Println(err) return err } sigPubHex, err := hex.DecodeString(sigPubKey) if err != nil { + c.log.Println(err) return err } copy(boxPub[:], boxPubHex) copy(sigPub[:], sigPubHex) - if intf, err := c.awdl.create(&boxPub, &sigPub, name); err == nil { + + if intf, err := c.awdl.create(fromAWDL, toAWDL, &boxPub, &sigPub, name); err == nil { if intf != nil { + c.log.Println(err) return err } else { + c.log.Println("c.awdl.create didn't return an interface") return errors.New("c.awdl.create didn't return an interface") } } else { + c.log.Println(err) return err } } +func (c *Core) AWDLCreateInterfaceFromContext(context []byte, name string) error { + if len(context) < crypto.BoxPubKeyLen+crypto.SigPubKeyLen { + return errors.New("Not enough bytes in context") + } + boxPubKey := hex.EncodeToString(context[:crypto.BoxPubKeyLen]) + sigPubKey := hex.EncodeToString(context[crypto.BoxPubKeyLen:]) + return c.AWDLCreateInterface(boxPubKey, sigPubKey, name) +} + func (c *Core) AWDLShutdownInterface(name string) error { return c.awdl.shutdown(name) } @@ -129,7 +147,7 @@ func (c *Core) AWDLRecvPacket(identity string) ([]byte, error) { if intf := c.awdl.getInterface(identity); intf != nil { return <-intf.toAWDL, nil } - return nil, errors.New("identity not known: " + identity) + return nil, errors.New("AWDLRecvPacket identity not known: " + identity) } func (c *Core) AWDLSendPacket(identity string, buf []byte) error { @@ -138,5 +156,12 @@ func (c *Core) AWDLSendPacket(identity string, buf []byte) error { intf.fromAWDL <- packet return nil } - return errors.New("identity not known: " + identity) + return errors.New("AWDLSendPacket identity not known: " + identity) +} + +func (c *Core) AWDLConnectionContext() []byte { + var context []byte + context = append(context, c.boxPub[:]...) + context = append(context, c.sigPub[:]...) + return context } From ab4be3424b35822c06ce38f771c39351615412cf Mon Sep 17 00:00:00 2001 From: "Tristan B. Kildaire" Date: Wed, 9 Jan 2019 11:42:07 +0200 Subject: [PATCH 034/109] Spelling fixes for peer.go --- src/yggdrasil/peer.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/yggdrasil/peer.go b/src/yggdrasil/peer.go index 333561e..fd04541 100644 --- a/src/yggdrasil/peer.go +++ b/src/yggdrasil/peer.go @@ -14,7 +14,7 @@ import ( ) // The peers struct represents peers with an active connection. -// Incomping packets are passed to the corresponding peer, which handles them somehow. +// Incoming packets are passed to the corresponding peer, which handles them somehow. // In most cases, this involves passing the packet to the handler for outgoing traffic to another peer. // In other cases, it's link protocol traffic used to build the spanning tree, in which case this checks signatures and passes the message along to the switch. type peers struct { @@ -97,7 +97,7 @@ type peer struct { close func() // Called when a peer is removed, to close the underlying connection, or via admin api } -// Creates a new peer with the specified box, sig, and linkShared keys, using the lowest unocupied port number. +// 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) *peer { now := time.Now() p := peer{box: *box, @@ -342,7 +342,7 @@ func (p *peer) handleSwitchMsg(packet []byte) { } // This generates the bytes that we sign or check the signature of for a switchMsg. -// It begins with the next node's key, followed by the root and the timetsamp, followed by coords being advertised to the next node. +// It begins with the next node's key, followed by the root and the timestamp, followed by coords being advertised to the next node. func getBytesForSig(next *crypto.SigPubKey, msg *switchMsg) []byte { var loc switchLocator for _, hop := range msg.Hops { From 345979b502fdc45e1007ef12ecf34a80c573427c Mon Sep 17 00:00:00 2001 From: "Tristan B. Kildaire" Date: Wed, 9 Jan 2019 11:44:45 +0200 Subject: [PATCH 035/109] Spelling fixes for search.go --- 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 c85b719..756ad27 100644 --- a/src/yggdrasil/search.go +++ b/src/yggdrasil/search.go @@ -30,7 +30,7 @@ const search_MAX_SEARCH_SIZE = 16 const search_RETRY_TIME = time.Second // Information about an ongoing search. -// Includes the targed NodeID, the bitmask to match it to an IP, and the list of nodes to visit / already visited. +// Includes the target NodeID, the bitmask to match it to an IP, and the list of nodes to visit / already visited. type searchInfo struct { dest crypto.NodeID mask crypto.NodeID From 08a71af2d82883649c2f210cb1720f0251fe17a7 Mon Sep 17 00:00:00 2001 From: "Tristan B. Kildaire" Date: Wed, 9 Jan 2019 11:49:12 +0200 Subject: [PATCH 036/109] Spelling fixes for switch.go --- src/yggdrasil/switch.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yggdrasil/switch.go b/src/yggdrasil/switch.go index 3c1dae6..2b0d5d3 100644 --- a/src/yggdrasil/switch.go +++ b/src/yggdrasil/switch.go @@ -4,7 +4,7 @@ package yggdrasil // It routes packets based on distance on the spanning tree // In general, this is *not* equivalent to routing on the tree // It falls back to the tree in the worst case, but it can take shortcuts too -// This is the part that makse routing reasonably efficient on scale-free graphs +// This is the part that makes routing reasonably efficient on scale-free graphs // TODO document/comment everything in a lot more detail From a371e34a187cc3642a2bdaa7ff150f443b5e55dc Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 10 Jan 2019 10:44:44 +0000 Subject: [PATCH 037/109] Add Android support, add addStaticPeers --- src/yggdrasil/mobile.go | 24 ++++++++++++++++++++++++ src/yggdrasil/mobile_android.go | 12 ++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 src/yggdrasil/mobile_android.go diff --git a/src/yggdrasil/mobile.go b/src/yggdrasil/mobile.go index 300d132..5cd15ec 100644 --- a/src/yggdrasil/mobile.go +++ b/src/yggdrasil/mobile.go @@ -9,6 +9,7 @@ import ( "log" "os" "regexp" + "time" hjson "github.com/hjson/hjson-go" "github.com/mitchellh/mapstructure" @@ -23,6 +24,25 @@ import ( // that in the case of iOS we handle reading/writing to/from TUN in Swift // therefore we use the "dummy" TUN interface instead. +func (c *Core) addStaticPeers(cfg *config.NodeConfig) { + if len(cfg.Peers) == 0 && len(cfg.InterfacePeers) == 0 { + return + } + for { + for _, peer := range cfg.Peers { + c.AddPeer(peer, "") + time.Sleep(time.Second) + } + for intf, intfpeers := range cfg.InterfacePeers { + for _, peer := range intfpeers { + c.AddPeer(peer, intf) + time.Sleep(time.Second) + } + } + time.Sleep(time.Minute) + } +} + func (c *Core) StartAutoconfigure() error { mobilelog := MobileLogger{} logger := log.New(mobilelog, "", 0) @@ -40,6 +60,7 @@ func (c *Core) StartAutoconfigure() error { if err := c.Start(nc, logger); err != nil { return err } + go c.addStaticPeers(nc) return nil } @@ -55,7 +76,9 @@ func (c *Core) StartJSON(configjson []byte) error { return err } nc.IfName = "dummy" + //c.log.Println(nc.MulticastInterfaces) for _, ll := range nc.MulticastInterfaces { + //c.log.Println("Processing MC", ll) ifceExpr, err := regexp.Compile(ll) if err != nil { panic(err) @@ -65,6 +88,7 @@ func (c *Core) StartJSON(configjson []byte) error { if err := c.Start(nc, logger); err != nil { return err } + go c.addStaticPeers(nc) return nil } diff --git a/src/yggdrasil/mobile_android.go b/src/yggdrasil/mobile_android.go new file mode 100644 index 0000000..2476484 --- /dev/null +++ b/src/yggdrasil/mobile_android.go @@ -0,0 +1,12 @@ +// +build android + +package yggdrasil + +import "log" + +type MobileLogger struct{} + +func (nsl MobileLogger) Write(p []byte) (n int, err error) { + log.Println(string(p)) + return len(p), nil +} From e24e8592541a4862db1cd14ba137a46024c402ad Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 10 Jan 2019 11:31:04 +0000 Subject: [PATCH 038/109] Add some comments, move AWDL functions to iOS-specific build tags --- src/yggdrasil/mobile.go | 85 ++++++------------------------------- src/yggdrasil/mobile_ios.go | 78 +++++++++++++++++++++++++++++++++- 2 files changed, 90 insertions(+), 73 deletions(-) diff --git a/src/yggdrasil/mobile.go b/src/yggdrasil/mobile.go index 5cd15ec..b751061 100644 --- a/src/yggdrasil/mobile.go +++ b/src/yggdrasil/mobile.go @@ -3,9 +3,7 @@ package yggdrasil import ( - "encoding/hex" "encoding/json" - "errors" "log" "os" "regexp" @@ -14,7 +12,6 @@ import ( hjson "github.com/hjson/hjson-go" "github.com/mitchellh/mapstructure" "github.com/yggdrasil-network/yggdrasil-go/src/config" - "github.com/yggdrasil-network/yggdrasil-go/src/crypto" "github.com/yggdrasil-network/yggdrasil-go/src/util" ) @@ -43,6 +40,7 @@ func (c *Core) addStaticPeers(cfg *config.NodeConfig) { } } +// Starts a node with a randomly generated config. func (c *Core) StartAutoconfigure() error { mobilelog := MobileLogger{} logger := log.New(mobilelog, "", 0) @@ -64,6 +62,8 @@ func (c *Core) StartAutoconfigure() error { return nil } +// Starts a node with the given JSON config. You can get JSON config (rather +// than HJSON) by using the GenerateConfigJSON() function. func (c *Core) StartJSON(configjson []byte) error { mobilelog := MobileLogger{} logger := log.New(mobilelog, "", 0) @@ -92,6 +92,7 @@ func (c *Core) StartJSON(configjson []byte) error { return nil } +// Generates mobile-friendly configuration in JSON format. func GenerateConfigJSON() []byte { nc := config.GenerateConfig(false) nc.IfName = "dummy" @@ -102,90 +103,30 @@ func GenerateConfigJSON() []byte { } } +// Gets the node's IPv6 address. func (c *Core) GetAddressString() string { return c.GetAddress().String() } +// Gets the node's IPv6 subnet in CIDR notation. func (c *Core) GetSubnetString() string { return c.GetSubnet().String() } +// Wait for a packet from the router. You will use this when implementing a +// dummy adapter in place of real TUN - when this call returns a packet, you +// will probably want to give it to the OS to write to TUN. func (c *Core) RouterRecvPacket() ([]byte, error) { packet := <-c.router.tun.recv return packet, nil } +// Send a packet to the router. You will use this when implementing a +// dummy adapter in place of real TUN - when the operating system tells you +// that a new packet is available from TUN, call this function to give it to +// Yggdrasil. func (c *Core) RouterSendPacket(buf []byte) error { packet := append(util.GetBytes(), buf[:]...) c.router.tun.send <- packet return nil } - -func (c *Core) AWDLCreateInterface(boxPubKey string, sigPubKey string, name string) error { - fromAWDL := make(chan []byte, 32) - toAWDL := make(chan []byte, 32) - - var boxPub crypto.BoxPubKey - var sigPub crypto.SigPubKey - boxPubHex, err := hex.DecodeString(boxPubKey) - if err != nil { - c.log.Println(err) - return err - } - sigPubHex, err := hex.DecodeString(sigPubKey) - if err != nil { - c.log.Println(err) - return err - } - copy(boxPub[:], boxPubHex) - copy(sigPub[:], sigPubHex) - - if intf, err := c.awdl.create(fromAWDL, toAWDL, &boxPub, &sigPub, name); err == nil { - if intf != nil { - c.log.Println(err) - return err - } else { - c.log.Println("c.awdl.create didn't return an interface") - return errors.New("c.awdl.create didn't return an interface") - } - } else { - c.log.Println(err) - return err - } -} - -func (c *Core) AWDLCreateInterfaceFromContext(context []byte, name string) error { - if len(context) < crypto.BoxPubKeyLen+crypto.SigPubKeyLen { - return errors.New("Not enough bytes in context") - } - boxPubKey := hex.EncodeToString(context[:crypto.BoxPubKeyLen]) - sigPubKey := hex.EncodeToString(context[crypto.BoxPubKeyLen:]) - return c.AWDLCreateInterface(boxPubKey, sigPubKey, name) -} - -func (c *Core) AWDLShutdownInterface(name string) error { - return c.awdl.shutdown(name) -} - -func (c *Core) AWDLRecvPacket(identity string) ([]byte, error) { - if intf := c.awdl.getInterface(identity); intf != nil { - return <-intf.toAWDL, nil - } - return nil, errors.New("AWDLRecvPacket identity not known: " + identity) -} - -func (c *Core) AWDLSendPacket(identity string, buf []byte) error { - packet := append(util.GetBytes(), buf[:]...) - if intf := c.awdl.getInterface(identity); intf != nil { - intf.fromAWDL <- packet - return nil - } - return errors.New("AWDLSendPacket identity not known: " + identity) -} - -func (c *Core) AWDLConnectionContext() []byte { - var context []byte - context = append(context, c.boxPub[:]...) - context = append(context, c.sigPub[:]...) - return context -} diff --git a/src/yggdrasil/mobile_ios.go b/src/yggdrasil/mobile_ios.go index 4abc6e9..6d2570d 100644 --- a/src/yggdrasil/mobile_ios.go +++ b/src/yggdrasil/mobile_ios.go @@ -12,7 +12,14 @@ void Log(const char *text) { } */ import "C" -import "unsafe" +import ( + "encoding/hex" + "errors" + "unsafe" + + "github.com/yggdrasil-network/yggdrasil-go/src/crypto" + "github.com/yggdrasil-network/yggdrasil-go/src/util" +) type MobileLogger struct { } @@ -23,3 +30,72 @@ func (nsl MobileLogger) Write(p []byte) (n int, err error) { C.Log(cstr) return len(p), nil } + +func (c *Core) AWDLCreateInterface(boxPubKey string, sigPubKey string, name string) error { + fromAWDL := make(chan []byte, 32) + toAWDL := make(chan []byte, 32) + + var boxPub crypto.BoxPubKey + var sigPub crypto.SigPubKey + boxPubHex, err := hex.DecodeString(boxPubKey) + if err != nil { + c.log.Println(err) + return err + } + sigPubHex, err := hex.DecodeString(sigPubKey) + if err != nil { + c.log.Println(err) + return err + } + copy(boxPub[:], boxPubHex) + copy(sigPub[:], sigPubHex) + + if intf, err := c.awdl.create(fromAWDL, toAWDL, &boxPub, &sigPub, name); err == nil { + if intf != nil { + c.log.Println(err) + return err + } else { + c.log.Println("c.awdl.create didn't return an interface") + return errors.New("c.awdl.create didn't return an interface") + } + } else { + c.log.Println(err) + return err + } +} + +func (c *Core) AWDLCreateInterfaceFromContext(context []byte, name string) error { + if len(context) < crypto.BoxPubKeyLen+crypto.SigPubKeyLen { + return errors.New("Not enough bytes in context") + } + boxPubKey := hex.EncodeToString(context[:crypto.BoxPubKeyLen]) + sigPubKey := hex.EncodeToString(context[crypto.BoxPubKeyLen:]) + return c.AWDLCreateInterface(boxPubKey, sigPubKey, name) +} + +func (c *Core) AWDLShutdownInterface(name string) error { + return c.awdl.shutdown(name) +} + +func (c *Core) AWDLRecvPacket(identity string) ([]byte, error) { + if intf := c.awdl.getInterface(identity); intf != nil { + return <-intf.toAWDL, nil + } + return nil, errors.New("AWDLRecvPacket identity not known: " + identity) +} + +func (c *Core) AWDLSendPacket(identity string, buf []byte) error { + packet := append(util.GetBytes(), buf[:]...) + if intf := c.awdl.getInterface(identity); intf != nil { + intf.fromAWDL <- packet + return nil + } + return errors.New("AWDLSendPacket identity not known: " + identity) +} + +func (c *Core) AWDLConnectionContext() []byte { + var context []byte + context = append(context, c.boxPub[:]...) + context = append(context, c.sigPub[:]...) + return context +} From 4f7e8856b853c4b6e5bc456078c3abb915f30fae Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 10 Jan 2019 17:44:52 +0000 Subject: [PATCH 039/109] Update build script for iOS/Android --- build | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/build b/build index a3a679f..7ceb4c9 100755 --- a/build +++ b/build @@ -6,13 +6,14 @@ PKGVER=${PKGVER:-$(sh contrib/semver/version.sh --bare)} LDFLAGS="-X $PKGSRC.buildName=$PKGNAME -X $PKGSRC.buildVersion=$PKGVER" -while getopts "udmtc:l:" option +while getopts "udaitc:l:" option do case "${option}" in u) UPX=true;; d) DEBUG=true;; - m) MOBILE=true;; + i) IOS=true;; + a) ANDROID=true;; t) TABLES=true;; c) GCFLAGS="$GCFLAGS $OPTARG";; l) LDFLAGS="$LDFLAGS $OPTARG";; @@ -23,17 +24,23 @@ if [ -z $TABLES ]; then STRIP="-s -w" fi -for CMD in `ls cmd/` ; do - echo "Building: $CMD" +if [ $IOS ]; then + echo "Building framework for iOS" + gomobile bind -target ios -tags mobile -ldflags="$LDFLAGS $STRIP" -gcflags="$GCFLAGS" github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil +elif [ $ANDROID ]; then + echo "Building aar for Android" + gomobile bind -target android -tags mobile -ldflags="$LDFLAGS $STRIP" -gcflags="$GCFLAGS" github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil +else + for CMD in `ls cmd/` ; do + echo "Building: $CMD" - if [ $MOBILE ]; then - go build -ldflags="$LDFLAGS" -gcflags="$GCFLAGS" -tags mobile -v ./cmd/$CMD - elif [ $DEBUG ]; then - go build -ldflags="$LDFLAGS" -gcflags="$GCFLAGS" -tags debug -v ./cmd/$CMD - else - go build -ldflags="$LDFLAGS $STRIP" -gcflags="$GCFLAGS" -v ./cmd/$CMD - fi - if [ $UPX ]; then - upx --brute $CMD - fi -done + if [ $DEBUG ]; then + go build -ldflags="$LDFLAGS" -gcflags="$GCFLAGS" -tags debug -v ./cmd/$CMD + else + go build -ldflags="$LDFLAGS $STRIP" -gcflags="$GCFLAGS" -v ./cmd/$CMD + fi + if [ $UPX ]; then + upx --brute $CMD + fi + done +fi From 4622a85c34e2c548b415db69720e9605c9acffcd Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 13 Jan 2019 18:08:41 +0000 Subject: [PATCH 040/109] AWDL support for macOS/iOS --- src/yggdrasil/mobile.go | 11 +++++++++++ src/yggdrasil/multicast_darwin.go | 28 ++++++++++++++++++++++++++++ src/yggdrasil/multicast_unix.go | 2 +- src/yggdrasil/tcp.go | 11 +++++++++-- src/yggdrasil/tcp_darwin.go | 28 ++++++++++++++++++++++++++++ src/yggdrasil/tcp_other.go | 13 +++++++++++++ 6 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 src/yggdrasil/multicast_darwin.go create mode 100644 src/yggdrasil/tcp_darwin.go create mode 100644 src/yggdrasil/tcp_other.go diff --git a/src/yggdrasil/mobile.go b/src/yggdrasil/mobile.go index b751061..2ffeffb 100644 --- a/src/yggdrasil/mobile.go +++ b/src/yggdrasil/mobile.go @@ -3,6 +3,7 @@ package yggdrasil import ( + "encoding/hex" "encoding/json" "log" "os" @@ -113,6 +114,16 @@ func (c *Core) GetSubnetString() string { return c.GetSubnet().String() } +// Gets the node's public encryption key. +func (c *Core) GetBoxPubKeyString() string { + return hex.EncodeToString(c.boxPub[:]) +} + +// Gets the node's public signing key. +func (c *Core) GetSigPubKeyString() string { + return hex.EncodeToString(c.sigPub[:]) +} + // Wait for a packet from the router. You will use this when implementing a // dummy adapter in place of real TUN - when this call returns a packet, you // will probably want to give it to the OS to write to TUN. diff --git a/src/yggdrasil/multicast_darwin.go b/src/yggdrasil/multicast_darwin.go new file mode 100644 index 0000000..71eecce --- /dev/null +++ b/src/yggdrasil/multicast_darwin.go @@ -0,0 +1,28 @@ +// +build darwin + +package yggdrasil + +import "syscall" +import "golang.org/x/sys/unix" + +func multicastReuse(network string, address string, c syscall.RawConn) error { + var control error + var reuseport error + var recvanyif error + + control = c.Control(func(fd uintptr) { + reuseport = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1) + + // sys/socket.h: #define SO_RECV_ANYIF 0x1104 + recvanyif = unix.SetsockoptInt(int(fd), syscall.SOL_SOCKET, 0x1104, 1) + }) + + switch { + case reuseport != nil: + return reuseport + case recvanyif != nil: + return recvanyif + default: + return control + } +} diff --git a/src/yggdrasil/multicast_unix.go b/src/yggdrasil/multicast_unix.go index 9c6d1f1..54bbc64 100644 --- a/src/yggdrasil/multicast_unix.go +++ b/src/yggdrasil/multicast_unix.go @@ -1,4 +1,4 @@ -// +build linux darwin netbsd freebsd openbsd dragonflybsd +// +build linux netbsd freebsd openbsd dragonflybsd package yggdrasil diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index 6d92344..8d8fee3 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -15,6 +15,7 @@ package yggdrasil // See version.go for version metadata format import ( + "context" "errors" "fmt" "io" @@ -88,7 +89,11 @@ func (iface *tcpInterface) init(core *Core, addr string, readTimeout int32) (err iface.tcp_timeout = default_tcp_timeout } - iface.serv, err = net.Listen("tcp", addr) + ctx := context.Background() + lc := net.ListenConfig{ + Control: iface.tcpContext, + } + iface.serv, err = lc.Listen(ctx, "tcp", addr) if err == nil { iface.calls = make(map[string]struct{}) iface.conns = make(map[tcpInfo](chan struct{})) @@ -164,7 +169,9 @@ func (iface *tcpInterface) call(saddr string, socksaddr *string, sintf string) { }, } } else { - dialer := net.Dialer{} + dialer := net.Dialer{ + Control: iface.tcpContext, + } if sintf != "" { ief, err := net.InterfaceByName(sintf) if err != nil { diff --git a/src/yggdrasil/tcp_darwin.go b/src/yggdrasil/tcp_darwin.go new file mode 100644 index 0000000..6483ef8 --- /dev/null +++ b/src/yggdrasil/tcp_darwin.go @@ -0,0 +1,28 @@ +// +build darwin + +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 (iface *tcpInterface) tcpContext(network, address string, c syscall.RawConn) error { + var control error + var recvanyif error + + control = c.Control(func(fd uintptr) { + // sys/socket.h: #define SO_RECV_ANYIF 0x1104 + recvanyif = unix.SetsockoptInt(int(fd), syscall.SOL_SOCKET, 0x1104, 1) + }) + + switch { + case recvanyif != nil: + return recvanyif + default: + return control + } +} diff --git a/src/yggdrasil/tcp_other.go b/src/yggdrasil/tcp_other.go new file mode 100644 index 0000000..5d62b53 --- /dev/null +++ b/src/yggdrasil/tcp_other.go @@ -0,0 +1,13 @@ +// +build !darwin + +package yggdrasil + +import ( + "syscall" +) + +// 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 { + return nil +} From c8db66b17d6a5a7f88a6ffc9ce75492bd135b8d6 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 13 Jan 2019 18:11:36 +0000 Subject: [PATCH 041/109] Remove unneeded AWDL context functions --- src/yggdrasil/mobile_ios.go | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/yggdrasil/mobile_ios.go b/src/yggdrasil/mobile_ios.go index 6d2570d..789df57 100644 --- a/src/yggdrasil/mobile_ios.go +++ b/src/yggdrasil/mobile_ios.go @@ -64,15 +64,6 @@ func (c *Core) AWDLCreateInterface(boxPubKey string, sigPubKey string, name stri } } -func (c *Core) AWDLCreateInterfaceFromContext(context []byte, name string) error { - if len(context) < crypto.BoxPubKeyLen+crypto.SigPubKeyLen { - return errors.New("Not enough bytes in context") - } - boxPubKey := hex.EncodeToString(context[:crypto.BoxPubKeyLen]) - sigPubKey := hex.EncodeToString(context[crypto.BoxPubKeyLen:]) - return c.AWDLCreateInterface(boxPubKey, sigPubKey, name) -} - func (c *Core) AWDLShutdownInterface(name string) error { return c.awdl.shutdown(name) } @@ -92,10 +83,3 @@ func (c *Core) AWDLSendPacket(identity string, buf []byte) error { } return errors.New("AWDLSendPacket identity not known: " + identity) } - -func (c *Core) AWDLConnectionContext() []byte { - var context []byte - context = append(context, c.boxPub[:]...) - context = append(context, c.sigPub[:]...) - return context -} From 704e4a062fbf953b5fc3065f5d32be1d82494515 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 13 Jan 2019 22:51:34 +0000 Subject: [PATCH 042/109] Specify source interface when responding to multicast beacon --- src/yggdrasil/multicast.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yggdrasil/multicast.go b/src/yggdrasil/multicast.go index 749dfcd..c0a676a 100644 --- a/src/yggdrasil/multicast.go +++ b/src/yggdrasil/multicast.go @@ -157,6 +157,6 @@ func (m *multicast) listen() { } addr.Zone = from.Zone saddr := addr.String() - m.core.tcp.connect(saddr, "") + m.core.tcp.connect(saddr, addr.Zone) } } From f556f3e2a8ffd261f2a8cbcd809bb61f9812d1af Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 13 Jan 2019 22:57:37 +0000 Subject: [PATCH 043/109] Try to perform TCP-like key exchange --- src/yggdrasil/awdl.go | 59 +++++++++++++++++++------------------ src/yggdrasil/mobile_ios.go | 21 ++----------- 2 files changed, 32 insertions(+), 48 deletions(-) diff --git a/src/yggdrasil/awdl.go b/src/yggdrasil/awdl.go index 65e84cb..633d5f9 100644 --- a/src/yggdrasil/awdl.go +++ b/src/yggdrasil/awdl.go @@ -34,42 +34,42 @@ func (l *awdl) init(c *Core) error { return nil } -func (l *awdl) create(fromAWDL chan []byte, toAWDL chan []byte, boxPubKey *crypto.BoxPubKey, sigPubKey *crypto.SigPubKey, name string) (*awdlInterface, error) { - /* - myLinkPub, myLinkPriv := crypto.NewBoxKeys() - meta := version_getBaseMetadata() - meta.box = l.core.boxPub - meta.sig = l.core.sigPub - meta.link = *myLinkPub - metaBytes := meta.encode() - l.core.log.Println("toAWDL <- metaBytes") - toAWDL <- metaBytes - l.core.log.Println("metaBytes = <-fromAWDL") - metaBytes = <-fromAWDL - l.core.log.Println("version_metadata{}") - meta = version_metadata{} - if !meta.decode(metaBytes) || !meta.check() { - return nil, errors.New("Metadata decode failure") - } - base := version_getBaseMetadata() - if meta.ver > base.ver || meta.ver == base.ver && meta.minorVer > base.minorVer { - return nil, errors.New("Failed to connect to node: " + name + " version: " + fmt.Sprintf("%d.%d", meta.ver, meta.minorVer)) - } - shared := crypto.GetSharedKey(myLinkPriv, &meta.link) - */ - shared := crypto.GetSharedKey(&l.core.boxPriv, boxPubKey) +func (l *awdl) create(fromAWDL chan []byte, toAWDL chan []byte /*boxPubKey *crypto.BoxPubKey, sigPubKey *crypto.SigPubKey*/, name string) (*awdlInterface, error) { intf := awdlInterface{ awdl: l, fromAWDL: fromAWDL, toAWDL: toAWDL, shutdown: make(chan bool), - peer: l.core.peers.newPeer(boxPubKey, sigPubKey, shared, name), - //peer: l.core.peers.newPeer(&meta.box, &meta.sig, shared, name), } + l.mutex.Lock() + l.interfaces[name] = &intf + l.mutex.Unlock() + myLinkPub, myLinkPriv := crypto.NewBoxKeys() + meta := version_getBaseMetadata() + meta.box = l.core.boxPub + meta.sig = l.core.sigPub + meta.link = *myLinkPub + metaBytes := meta.encode() + l.core.log.Println("toAWDL <- metaBytes") + toAWDL <- metaBytes + l.core.log.Println("metaBytes = <-fromAWDL") + metaBytes = <-fromAWDL + l.core.log.Println("version_metadata{}") + meta = version_metadata{} + if !meta.decode(metaBytes) || !meta.check() { + return nil, errors.New("Metadata decode failure") + } + l.core.log.Println("version_getBaseMetadata{}") + base := version_getBaseMetadata() + if meta.ver > base.ver || meta.ver == base.ver && meta.minorVer > base.minorVer { + return nil, errors.New("Failed to connect to node: " + name + " version: " + fmt.Sprintf("%d.%d", meta.ver, meta.minorVer)) + } + l.core.log.Println("crypto.GetSharedKey") + shared := crypto.GetSharedKey(myLinkPriv, &meta.link) + //shared := crypto.GetSharedKey(&l.core.boxPriv, boxPubKey) + l.core.log.Println("l.core.peers.newPeer") + intf.peer = l.core.peers.newPeer(&meta.box, &meta.sig, shared, name) if intf.peer != nil { - l.mutex.Lock() - l.interfaces[name] = &intf - l.mutex.Unlock() intf.peer.linkOut = make(chan []byte, 1) // protocol traffic intf.peer.out = func(msg []byte) { defer func() { recover() }() @@ -84,6 +84,7 @@ func (l *awdl) create(fromAWDL chan []byte, toAWDL chan []byte, boxPubKey *crypt go intf.peer.linkLoop() return &intf, nil } + delete(l.interfaces, name) return nil, errors.New("l.core.peers.newPeer failed") } diff --git a/src/yggdrasil/mobile_ios.go b/src/yggdrasil/mobile_ios.go index 789df57..72920fe 100644 --- a/src/yggdrasil/mobile_ios.go +++ b/src/yggdrasil/mobile_ios.go @@ -13,11 +13,9 @@ void Log(const char *text) { */ import "C" import ( - "encoding/hex" "errors" "unsafe" - "github.com/yggdrasil-network/yggdrasil-go/src/crypto" "github.com/yggdrasil-network/yggdrasil-go/src/util" ) @@ -31,26 +29,11 @@ func (nsl MobileLogger) Write(p []byte) (n int, err error) { return len(p), nil } -func (c *Core) AWDLCreateInterface(boxPubKey string, sigPubKey string, name string) error { +func (c *Core) AWDLCreateInterface(name string) error { fromAWDL := make(chan []byte, 32) toAWDL := make(chan []byte, 32) - var boxPub crypto.BoxPubKey - var sigPub crypto.SigPubKey - boxPubHex, err := hex.DecodeString(boxPubKey) - if err != nil { - c.log.Println(err) - return err - } - sigPubHex, err := hex.DecodeString(sigPubKey) - if err != nil { - c.log.Println(err) - return err - } - copy(boxPub[:], boxPubHex) - copy(sigPub[:], sigPubHex) - - if intf, err := c.awdl.create(fromAWDL, toAWDL, &boxPub, &sigPub, name); err == nil { + if intf, err := c.awdl.create(fromAWDL, toAWDL, name); err == nil { if intf != nil { c.log.Println(err) return err From aed3c7e7845fbde73f720d6c82f55453038fe460 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 14 Jan 2019 14:25:52 +0000 Subject: [PATCH 044/109] Give nodeconfig to tun --- src/yggdrasil/adapter.go | 8 +++++--- src/yggdrasil/core.go | 17 ++++++++--------- src/yggdrasil/tun.go | 27 ++++++++++++++++++++++++++- 3 files changed, 39 insertions(+), 13 deletions(-) diff --git a/src/yggdrasil/adapter.go b/src/yggdrasil/adapter.go index 7fb6a19..3ce80d2 100644 --- a/src/yggdrasil/adapter.go +++ b/src/yggdrasil/adapter.go @@ -3,9 +3,10 @@ package yggdrasil // Defines the minimum required struct members for an adapter type (this is // now the base type for tunAdapter in tun.go) type Adapter struct { - core *Core - send chan<- []byte - recv <-chan []byte + core *Core + send chan<- []byte + recv <-chan []byte + reconfigure chan chan error } // Initialises the adapter. @@ -13,4 +14,5 @@ func (adapter *Adapter) init(core *Core, send chan<- []byte, recv <-chan []byte) adapter.core = core adapter.send = send adapter.recv = recv + adapter.reconfigure = make(chan chan error, 1) } diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index 7e10dbc..bee09ac 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -2,7 +2,6 @@ package yggdrasil import ( "encoding/hex" - "fmt" "io/ioutil" "log" "net" @@ -110,12 +109,13 @@ func (c *Core) UpdateConfig(config *config.NodeConfig) { components := []chan chan error{ c.admin.reconfigure, - //c.searches.reconfigure, - //c.dht.reconfigure, - //c.sessions.reconfigure, - //c.peers.reconfigure, - //c.router.reconfigure, - //c.switchTable.reconfigure, + c.searches.reconfigure, + c.dht.reconfigure, + c.sessions.reconfigure, + c.peers.reconfigure, + c.router.reconfigure, + c.router.tun.reconfigure, + c.switchTable.reconfigure, c.tcp.reconfigure, c.multicast.reconfigure, } @@ -240,8 +240,7 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error { return err } - ip := net.IP(c.router.addr[:]).String() - if err := c.router.tun.start(nc.IfName, nc.IfTAPMode, fmt.Sprintf("%s/%d", ip, 8*len(address.GetPrefix())-1), nc.IfMTU); err != nil { + if err := c.router.tun.start(); err != nil { c.log.Println("Failed to start TUN/TAP") return err } diff --git a/src/yggdrasil/tun.go b/src/yggdrasil/tun.go index 8c0f91d..0bda312 100644 --- a/src/yggdrasil/tun.go +++ b/src/yggdrasil/tun.go @@ -5,6 +5,8 @@ package yggdrasil import ( "bytes" "errors" + "fmt" + "net" "sync" "time" @@ -42,11 +44,34 @@ func getSupportedMTU(mtu int) int { func (tun *tunAdapter) init(core *Core, send chan<- []byte, recv <-chan []byte) { tun.Adapter.init(core, send, recv) tun.icmpv6.init(tun) + go func() { + for { + select { + case e := <-tun.reconfigure: + tun.core.configMutex.RLock() + updated := tun.core.config.IfName != tun.core.configOld.IfName || + tun.core.config.IfTAPMode != tun.core.configOld.IfTAPMode || + tun.core.config.IfMTU != tun.core.configOld.IfMTU + tun.core.configMutex.RUnlock() + if updated { + e <- nil + } else { + e <- nil + } + } + } + }() } // Starts the setup process for the TUN/TAP adapter, and if successful, starts // the read/write goroutines to handle packets on that interface. -func (tun *tunAdapter) start(ifname string, iftapmode bool, addr string, mtu int) error { +func (tun *tunAdapter) start() error { + tun.core.configMutex.RLock() + ifname := tun.core.config.IfName + iftapmode := tun.core.config.IfTAPMode + addr := fmt.Sprintf("%s/%d", net.IP(tun.core.router.addr[:]).String(), 8*len(address.GetPrefix())-1) + mtu := tun.core.config.IfMTU + tun.core.configMutex.RUnlock() if ifname != "none" { if err := tun.setup(ifname, iftapmode, addr, mtu); err != nil { return err From 87d393bd9f163039f01446ba968cc3e6d77b5e30 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 14 Jan 2019 17:21:15 +0000 Subject: [PATCH 045/109] Move add peer loop into Core, refresh it from active config --- cmd/yggdrasil/main.go | 22 ---------------------- src/yggdrasil/core.go | 38 +++++++++++++++++++++++++++++++++----- 2 files changed, 33 insertions(+), 27 deletions(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 262fb68..cc54d3d 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -12,7 +12,6 @@ import ( "regexp" "strings" "syscall" - "time" "golang.org/x/text/encoding/unicode" @@ -243,27 +242,6 @@ func main() { for _, pBoxStr := range cfg.AllowedEncryptionPublicKeys { n.core.AddAllowedEncryptionPublicKey(pBoxStr) } - // If any static peers were provided in the configuration above then we should - // configure them. The loop ensures that disconnected peers will eventually - // be reconnected with. - go func() { - if len(cfg.Peers) == 0 && len(cfg.InterfacePeers) == 0 { - return - } - for { - for _, peer := range cfg.Peers { - n.core.AddPeer(peer, "") - time.Sleep(time.Second) - } - for intf, intfpeers := range cfg.InterfacePeers { - for _, peer := range intfpeers { - n.core.AddPeer(peer, intf) - time.Sleep(time.Second) - } - } - time.Sleep(time.Minute) - } - }() // The Stop function ensures that the TUN/TAP adapter is correctly shut down // before the program exits. defer func() { diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index bee09ac..a53449b 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -7,6 +7,7 @@ import ( "net" "regexp" "sync" + "time" "github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/config" @@ -91,14 +92,39 @@ func (c *Core) init() error { c.router.init(c) c.switchTable.init(c) // TODO move before peers? before router? - if err := c.tcp.init(c); err != nil { - c.log.Println("Failed to start TCP interface") - return err - } - return nil } +// If any static peers were provided in the configuration above then we should +// configure them. The loop ensures that disconnected peers will eventually +// be reconnected with. +func (c *Core) addPeerLoop() { + for { + // Get the peers from the config - these could change! + c.configMutex.RLock() + peers := c.config.Peers + interfacepeers := c.config.InterfacePeers + c.configMutex.RUnlock() + + // Add peers from the Peers section + for _, peer := range peers { + c.AddPeer(peer, "") + time.Sleep(time.Second) + } + + // Add peers from the InterfacePeers section + for intf, intfpeers := range interfacepeers { + for _, peer := range intfpeers { + c.AddPeer(peer, intf) + time.Sleep(time.Second) + } + } + + // Sit for a while + time.Sleep(time.Minute) + } +} + // UpdateConfig updates the configuration in Core and then signals the // various module goroutines to reconfigure themselves if needed func (c *Core) UpdateConfig(config *config.NodeConfig) { @@ -245,6 +271,8 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error { return err } + go c.addPeerLoop() + c.log.Println("Startup complete") return nil } From 28072c9fe2f7587ec02ca212016c6e42809fd6d2 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 14 Jan 2019 17:41:08 +0000 Subject: [PATCH 046/109] Make CKR thread-safe --- src/yggdrasil/ckr.go | 66 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 58 insertions(+), 8 deletions(-) diff --git a/src/yggdrasil/ckr.go b/src/yggdrasil/ckr.go index a3df891..84d60e3 100644 --- a/src/yggdrasil/ckr.go +++ b/src/yggdrasil/ckr.go @@ -7,6 +7,7 @@ import ( "fmt" "net" "sort" + "sync" "github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/crypto" @@ -16,14 +17,19 @@ import ( // allow traffic for non-Yggdrasil ranges to be routed over Yggdrasil. type cryptokey struct { - core *Core - enabled bool - ipv4routes []cryptokey_route - ipv6routes []cryptokey_route - ipv4cache map[address.Address]cryptokey_route - ipv6cache map[address.Address]cryptokey_route - ipv4sources []net.IPNet - ipv6sources []net.IPNet + core *Core + enabled bool + reconfigure chan chan error + ipv4routes []cryptokey_route + ipv6routes []cryptokey_route + ipv4cache map[address.Address]cryptokey_route + ipv6cache map[address.Address]cryptokey_route + ipv4sources []net.IPNet + ipv6sources []net.IPNet + mutexenabled sync.RWMutex // protects enabled + mutexroutes sync.RWMutex // protects ipv4routes, ipv6routes + mutexcache sync.RWMutex // protects ipv4cache, ipv6cache + mutexsources sync.RWMutex // protects ipv4sources, ipv6sources } type cryptokey_route struct { @@ -34,21 +40,43 @@ type cryptokey_route struct { // Initialise crypto-key routing. This must be done before any other CKR calls. func (c *cryptokey) init(core *Core) { c.core = core + c.reconfigure = make(chan chan error, 1) + go func() { + for { + select { + case e := <-c.reconfigure: + e <- nil + } + } + }() + + c.mutexroutes.Lock() c.ipv4routes = make([]cryptokey_route, 0) c.ipv6routes = make([]cryptokey_route, 0) + c.mutexroutes.Unlock() + + c.mutexcache.Lock() c.ipv4cache = make(map[address.Address]cryptokey_route, 0) c.ipv6cache = make(map[address.Address]cryptokey_route, 0) + c.mutexcache.Unlock() + + c.mutexsources.Lock() c.ipv4sources = make([]net.IPNet, 0) c.ipv6sources = make([]net.IPNet, 0) + c.mutexsources.Unlock() } // Enable or disable crypto-key routing. func (c *cryptokey) setEnabled(enabled bool) { + c.mutexenabled.Lock() + defer c.mutexenabled.Unlock() c.enabled = enabled } // Check if crypto-key routing is enabled. func (c *cryptokey) isEnabled() bool { + c.mutexenabled.RLock() + defer c.mutexenabled.RUnlock() return c.enabled } @@ -72,6 +100,9 @@ func (c *cryptokey) isValidSource(addr address.Address, addrlen int) bool { // Does it match a configured CKR source? if c.isEnabled() { + c.mutexsources.RLock() + defer c.mutexsources.RUnlock() + // Build our references to the routing sources var routingsources *[]net.IPNet @@ -98,6 +129,9 @@ func (c *cryptokey) isValidSource(addr address.Address, addrlen int) bool { // Adds a source subnet, which allows traffic with these source addresses to // be tunnelled using crypto-key routing. func (c *cryptokey) addSourceSubnet(cidr string) error { + c.mutexsources.Lock() + defer c.mutexsources.Unlock() + // Is the CIDR we've been given valid? _, ipnet, err := net.ParseCIDR(cidr) if err != nil { @@ -135,6 +169,9 @@ func (c *cryptokey) addSourceSubnet(cidr string) error { // Adds a destination route for the given CIDR to be tunnelled to the node // with the given BoxPubKey. func (c *cryptokey) addRoute(cidr string, dest string) error { + c.mutexroutes.Lock() + defer c.mutexroutes.Unlock() + // Is the CIDR we've been given valid? ipaddr, ipnet, err := net.ParseCIDR(cidr) if err != nil { @@ -209,6 +246,11 @@ func (c *cryptokey) addRoute(cidr string, dest string) error { // length specified in bytes) from the crypto-key routing table. An error is // returned if the address is not suitable or no route was found. func (c *cryptokey) getPublicKeyForAddress(addr address.Address, addrlen int) (crypto.BoxPubKey, error) { + c.mutexroutes.RLock() + c.mutexcache.RLock() + defer c.mutexroutes.RUnlock() + defer c.mutexcache.RUnlock() + // Check if the address is a valid Yggdrasil address - if so it // is exempt from all CKR checking if addr.IsValid() { @@ -269,6 +311,9 @@ func (c *cryptokey) getPublicKeyForAddress(addr address.Address, addrlen int) (c // Removes a source subnet, which allows traffic with these source addresses to // be tunnelled using crypto-key routing. func (c *cryptokey) removeSourceSubnet(cidr string) error { + c.mutexsources.Lock() + defer c.mutexsources.Unlock() + // Is the CIDR we've been given valid? _, ipnet, err := net.ParseCIDR(cidr) if err != nil { @@ -304,6 +349,11 @@ func (c *cryptokey) removeSourceSubnet(cidr string) error { // Removes a destination route for the given CIDR to be tunnelled to the node // with the given BoxPubKey. func (c *cryptokey) removeRoute(cidr string, dest string) error { + c.mutexroutes.Lock() + c.mutexcache.Lock() + defer c.mutexroutes.Unlock() + defer c.mutexcache.Unlock() + // Is the CIDR we've been given valid? _, ipnet, err := net.ParseCIDR(cidr) if err != nil { From bd04124e43ff42d80740bb93b413fc04245198c8 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 14 Jan 2019 18:06:41 +0000 Subject: [PATCH 047/109] Reconfigure support for crypto-key routing --- src/yggdrasil/ckr.go | 60 ++++++++++++++++++++++++++++++++++++----- src/yggdrasil/core.go | 25 ----------------- src/yggdrasil/router.go | 8 ++++++ 3 files changed, 62 insertions(+), 31 deletions(-) diff --git a/src/yggdrasil/ckr.go b/src/yggdrasil/ckr.go index 84d60e3..bf569fb 100644 --- a/src/yggdrasil/ckr.go +++ b/src/yggdrasil/ckr.go @@ -45,25 +45,73 @@ func (c *cryptokey) init(core *Core) { for { select { case e := <-c.reconfigure: - e <- nil + e <- c.configure() } } }() + if err := c.configure(); err != nil { + c.core.log.Println("CKR configuration failed:", err) + } +} + +// Configure the CKR routes +func (c *cryptokey) configure() error { + c.core.configMutex.RLock() + defer c.core.configMutex.RUnlock() + + // Set enabled/disabled state + c.setEnabled(c.core.config.TunnelRouting.Enable) + + // Clear out existing routes c.mutexroutes.Lock() - c.ipv4routes = make([]cryptokey_route, 0) c.ipv6routes = make([]cryptokey_route, 0) + c.ipv4routes = make([]cryptokey_route, 0) c.mutexroutes.Unlock() + // Add IPv6 routes + for ipv6, pubkey := range c.core.config.TunnelRouting.IPv6Destinations { + if err := c.addRoute(ipv6, pubkey); err != nil { + return err + } + } + + // Add IPv4 routes + for ipv4, pubkey := range c.core.config.TunnelRouting.IPv4Destinations { + if err := c.addRoute(ipv4, pubkey); err != nil { + return err + } + } + + // Clear out existing sources + c.mutexsources.Lock() + c.ipv6sources = make([]net.IPNet, 0) + c.ipv4sources = make([]net.IPNet, 0) + c.mutexsources.Unlock() + + // Add IPv6 sources + c.ipv6sources = make([]net.IPNet, 0) + for _, source := range c.core.config.TunnelRouting.IPv6Sources { + if err := c.addSourceSubnet(source); err != nil { + return err + } + } + + // Add IPv4 sources + c.ipv4sources = make([]net.IPNet, 0) + for _, source := range c.core.config.TunnelRouting.IPv4Sources { + if err := c.addSourceSubnet(source); err != nil { + return err + } + } + + // Wipe the caches c.mutexcache.Lock() c.ipv4cache = make(map[address.Address]cryptokey_route, 0) c.ipv6cache = make(map[address.Address]cryptokey_route, 0) c.mutexcache.Unlock() - c.mutexsources.Lock() - c.ipv4sources = make([]net.IPNet, 0) - c.ipv6sources = make([]net.IPNet, 0) - c.mutexsources.Unlock() + return nil } // Enable or disable crypto-key routing. diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index a53449b..4b00fc3 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -231,31 +231,6 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error { return err } - c.router.cryptokey.setEnabled(nc.TunnelRouting.Enable) - if c.router.cryptokey.isEnabled() { - c.log.Println("Crypto-key routing enabled") - for ipv6, pubkey := range nc.TunnelRouting.IPv6Destinations { - if err := c.router.cryptokey.addRoute(ipv6, pubkey); err != nil { - panic(err) - } - } - for _, source := range nc.TunnelRouting.IPv6Sources { - if err := c.router.cryptokey.addSourceSubnet(source); err != nil { - panic(err) - } - } - for ipv4, pubkey := range nc.TunnelRouting.IPv4Destinations { - if err := c.router.cryptokey.addRoute(ipv4, pubkey); err != nil { - panic(err) - } - } - for _, source := range nc.TunnelRouting.IPv4Sources { - if err := c.router.cryptokey.addSourceSubnet(source); err != nil { - panic(err) - } - } - } - if err := c.admin.start(); err != nil { c.log.Println("Failed to start admin socket") return err diff --git a/src/yggdrasil/router.go b/src/yggdrasil/router.go index 68fb025..74fff3f 100644 --- a/src/yggdrasil/router.go +++ b/src/yggdrasil/router.go @@ -127,6 +127,14 @@ func (r *router) mainLoop() { case f := <-r.admin: f() case e := <-r.reconfigure: + // Send reconfigure notification to cryptokey + response := make(chan error) + r.cryptokey.reconfigure <- response + if err := <-response; err != nil { + e <- err + } + + // Anything else to do? e <- nil } } From 51026d762ef9958353327f0180770b3e60e1555c Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 14 Jan 2019 18:24:35 +0000 Subject: [PATCH 048/109] Make session firewall thread-safe for config updates --- src/yggdrasil/session.go | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/yggdrasil/session.go b/src/yggdrasil/session.go index 3c8e013..3cd1cf7 100644 --- a/src/yggdrasil/session.go +++ b/src/yggdrasil/session.go @@ -7,6 +7,7 @@ package yggdrasil import ( "bytes" "encoding/hex" + "sync" "time" "github.com/yggdrasil-network/yggdrasil-go/src/address" @@ -115,6 +116,7 @@ type sessions struct { addrToPerm map[address.Address]*crypto.BoxPubKey subnetToPerm map[address.Subnet]*crypto.BoxPubKey // Options from the session firewall + sessionFirewallMutex sync.RWMutex sessionFirewallEnabled bool sessionFirewallAllowsDirect bool sessionFirewallAllowsRemote bool @@ -157,12 +159,16 @@ func (ss *sessions) init(core *Core) { // Enable or disable the session firewall func (ss *sessions) setSessionFirewallState(enabled bool) { + ss.sessionFirewallMutex.Lock() + defer ss.sessionFirewallMutex.Unlock() ss.sessionFirewallEnabled = enabled } // Set the session firewall defaults (first parameter is whether to allow // sessions from direct peers, second is whether to allow from remote nodes). func (ss *sessions) setSessionFirewallDefaults(allowsDirect bool, allowsRemote bool, alwaysAllowsOutbound bool) { + ss.sessionFirewallMutex.Lock() + defer ss.sessionFirewallMutex.Unlock() ss.sessionFirewallAllowsDirect = allowsDirect ss.sessionFirewallAllowsRemote = allowsRemote ss.sessionFirewallAlwaysAllowsOutbound = alwaysAllowsOutbound @@ -170,17 +176,24 @@ func (ss *sessions) setSessionFirewallDefaults(allowsDirect bool, allowsRemote b // Set the session firewall whitelist - nodes always allowed to open sessions. func (ss *sessions) setSessionFirewallWhitelist(whitelist []string) { + ss.sessionFirewallMutex.Lock() + defer ss.sessionFirewallMutex.Unlock() ss.sessionFirewallWhitelist = whitelist } // Set the session firewall blacklist - nodes never allowed to open sessions. func (ss *sessions) setSessionFirewallBlacklist(blacklist []string) { + ss.sessionFirewallMutex.Lock() + defer ss.sessionFirewallMutex.Unlock() ss.sessionFirewallBlacklist = blacklist } // Determines whether the session with a given publickey is allowed based on // session firewall rules. func (ss *sessions) isSessionAllowed(pubkey *crypto.BoxPubKey, initiator bool) bool { + ss.sessionFirewallMutex.RLock() + defer ss.sessionFirewallMutex.RUnlock() + // Allow by default if the session firewall is disabled if !ss.sessionFirewallEnabled { return true @@ -286,10 +299,8 @@ func (ss *sessions) getByTheirSubnet(snet *address.Subnet) (*sessionInfo, bool) // Creates a new session and lazily cleans up old/timedout existing sessions. // This includse initializing session info to sane defaults (e.g. lowest supported MTU). func (ss *sessions) createSession(theirPermKey *crypto.BoxPubKey) *sessionInfo { - if ss.sessionFirewallEnabled { - if !ss.isSessionAllowed(theirPermKey, true) { - return nil - } + if !ss.isSessionAllowed(theirPermKey, true) { + return nil } sinfo := sessionInfo{} sinfo.core = ss.core @@ -465,11 +476,14 @@ func (ss *sessions) handlePing(ping *sessionPing) { // Get the corresponding session (or create a new session) sinfo, isIn := ss.getByTheirPerm(&ping.SendPermPub) // Check the session firewall + ss.sessionFirewallMutex.RLock() if !isIn && ss.sessionFirewallEnabled { if !ss.isSessionAllowed(&ping.SendPermPub, false) { + ss.sessionFirewallMutex.RUnlock() return } } + ss.sessionFirewallMutex.RUnlock() if !isIn || sinfo.timedout() { if isIn { sinfo.close() From 9e186bdd6730d6c55c61858da429e9bf5bca410e Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 14 Jan 2019 18:34:15 +0000 Subject: [PATCH 049/109] Remove mutexes from CKR and use router goroutine/doAdmin for update config --- src/yggdrasil/ckr.go | 64 +++++++++++------------------------------ src/yggdrasil/core.go | 1 + src/yggdrasil/router.go | 8 ------ 3 files changed, 17 insertions(+), 56 deletions(-) diff --git a/src/yggdrasil/ckr.go b/src/yggdrasil/ckr.go index bf569fb..14464f6 100644 --- a/src/yggdrasil/ckr.go +++ b/src/yggdrasil/ckr.go @@ -7,7 +7,6 @@ import ( "fmt" "net" "sort" - "sync" "github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/crypto" @@ -17,19 +16,15 @@ import ( // allow traffic for non-Yggdrasil ranges to be routed over Yggdrasil. type cryptokey struct { - core *Core - enabled bool - reconfigure chan chan error - ipv4routes []cryptokey_route - ipv6routes []cryptokey_route - ipv4cache map[address.Address]cryptokey_route - ipv6cache map[address.Address]cryptokey_route - ipv4sources []net.IPNet - ipv6sources []net.IPNet - mutexenabled sync.RWMutex // protects enabled - mutexroutes sync.RWMutex // protects ipv4routes, ipv6routes - mutexcache sync.RWMutex // protects ipv4cache, ipv6cache - mutexsources sync.RWMutex // protects ipv4sources, ipv6sources + core *Core + enabled bool + reconfigure chan chan error + ipv4routes []cryptokey_route + ipv6routes []cryptokey_route + ipv4cache map[address.Address]cryptokey_route + ipv6cache map[address.Address]cryptokey_route + ipv4sources []net.IPNet + ipv6sources []net.IPNet } type cryptokey_route struct { @@ -45,7 +40,11 @@ func (c *cryptokey) init(core *Core) { for { select { case e := <-c.reconfigure: - e <- c.configure() + var err error + c.core.router.doAdmin(func() { + err = c.core.router.cryptokey.configure() + }) + e <- err } } }() @@ -55,7 +54,8 @@ func (c *cryptokey) init(core *Core) { } } -// Configure the CKR routes +// Configure the CKR routes - this must only ever be called from the router +// goroutine, e.g. through router.doAdmin func (c *cryptokey) configure() error { c.core.configMutex.RLock() defer c.core.configMutex.RUnlock() @@ -64,10 +64,8 @@ func (c *cryptokey) configure() error { c.setEnabled(c.core.config.TunnelRouting.Enable) // Clear out existing routes - c.mutexroutes.Lock() c.ipv6routes = make([]cryptokey_route, 0) c.ipv4routes = make([]cryptokey_route, 0) - c.mutexroutes.Unlock() // Add IPv6 routes for ipv6, pubkey := range c.core.config.TunnelRouting.IPv6Destinations { @@ -84,10 +82,8 @@ func (c *cryptokey) configure() error { } // Clear out existing sources - c.mutexsources.Lock() c.ipv6sources = make([]net.IPNet, 0) c.ipv4sources = make([]net.IPNet, 0) - c.mutexsources.Unlock() // Add IPv6 sources c.ipv6sources = make([]net.IPNet, 0) @@ -106,25 +102,19 @@ func (c *cryptokey) configure() error { } // Wipe the caches - c.mutexcache.Lock() c.ipv4cache = make(map[address.Address]cryptokey_route, 0) c.ipv6cache = make(map[address.Address]cryptokey_route, 0) - c.mutexcache.Unlock() return nil } // Enable or disable crypto-key routing. func (c *cryptokey) setEnabled(enabled bool) { - c.mutexenabled.Lock() - defer c.mutexenabled.Unlock() c.enabled = enabled } // Check if crypto-key routing is enabled. func (c *cryptokey) isEnabled() bool { - c.mutexenabled.RLock() - defer c.mutexenabled.RUnlock() return c.enabled } @@ -148,9 +138,6 @@ func (c *cryptokey) isValidSource(addr address.Address, addrlen int) bool { // Does it match a configured CKR source? if c.isEnabled() { - c.mutexsources.RLock() - defer c.mutexsources.RUnlock() - // Build our references to the routing sources var routingsources *[]net.IPNet @@ -177,9 +164,6 @@ func (c *cryptokey) isValidSource(addr address.Address, addrlen int) bool { // Adds a source subnet, which allows traffic with these source addresses to // be tunnelled using crypto-key routing. func (c *cryptokey) addSourceSubnet(cidr string) error { - c.mutexsources.Lock() - defer c.mutexsources.Unlock() - // Is the CIDR we've been given valid? _, ipnet, err := net.ParseCIDR(cidr) if err != nil { @@ -217,9 +201,6 @@ func (c *cryptokey) addSourceSubnet(cidr string) error { // Adds a destination route for the given CIDR to be tunnelled to the node // with the given BoxPubKey. func (c *cryptokey) addRoute(cidr string, dest string) error { - c.mutexroutes.Lock() - defer c.mutexroutes.Unlock() - // Is the CIDR we've been given valid? ipaddr, ipnet, err := net.ParseCIDR(cidr) if err != nil { @@ -294,11 +275,6 @@ func (c *cryptokey) addRoute(cidr string, dest string) error { // length specified in bytes) from the crypto-key routing table. An error is // returned if the address is not suitable or no route was found. func (c *cryptokey) getPublicKeyForAddress(addr address.Address, addrlen int) (crypto.BoxPubKey, error) { - c.mutexroutes.RLock() - c.mutexcache.RLock() - defer c.mutexroutes.RUnlock() - defer c.mutexcache.RUnlock() - // Check if the address is a valid Yggdrasil address - if so it // is exempt from all CKR checking if addr.IsValid() { @@ -359,9 +335,6 @@ func (c *cryptokey) getPublicKeyForAddress(addr address.Address, addrlen int) (c // Removes a source subnet, which allows traffic with these source addresses to // be tunnelled using crypto-key routing. func (c *cryptokey) removeSourceSubnet(cidr string) error { - c.mutexsources.Lock() - defer c.mutexsources.Unlock() - // Is the CIDR we've been given valid? _, ipnet, err := net.ParseCIDR(cidr) if err != nil { @@ -397,11 +370,6 @@ func (c *cryptokey) removeSourceSubnet(cidr string) error { // Removes a destination route for the given CIDR to be tunnelled to the node // with the given BoxPubKey. func (c *cryptokey) removeRoute(cidr string, dest string) error { - c.mutexroutes.Lock() - c.mutexcache.Lock() - defer c.mutexroutes.Unlock() - defer c.mutexcache.Unlock() - // Is the CIDR we've been given valid? _, ipnet, err := net.ParseCIDR(cidr) if err != nil { diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index 4b00fc3..c6d6f4a 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -141,6 +141,7 @@ func (c *Core) UpdateConfig(config *config.NodeConfig) { c.peers.reconfigure, c.router.reconfigure, c.router.tun.reconfigure, + c.router.cryptokey.reconfigure, c.switchTable.reconfigure, c.tcp.reconfigure, c.multicast.reconfigure, diff --git a/src/yggdrasil/router.go b/src/yggdrasil/router.go index 74fff3f..68fb025 100644 --- a/src/yggdrasil/router.go +++ b/src/yggdrasil/router.go @@ -127,14 +127,6 @@ func (r *router) mainLoop() { case f := <-r.admin: f() case e := <-r.reconfigure: - // Send reconfigure notification to cryptokey - response := make(chan error) - r.cryptokey.reconfigure <- response - if err := <-response; err != nil { - e <- err - } - - // Anything else to do? e <- nil } } From 5cde3b5efc5ef4b72ae7e6fcba941b68f171a522 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 14 Jan 2019 18:51:49 +0000 Subject: [PATCH 050/109] Update nodeinfo in router reconfigure --- src/yggdrasil/admin.go | 5 ++++- src/yggdrasil/router.go | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/yggdrasil/admin.go b/src/yggdrasil/admin.go index 4c864b3..a44044a 100644 --- a/src/yggdrasil/admin.go +++ b/src/yggdrasil/admin.go @@ -350,7 +350,10 @@ func (a *admin) init(c *Core) { } var box_pub_key, coords string if in["box_pub_key"] == nil && in["coords"] == nil { - nodeinfo := []byte(a.core.nodeinfo.getNodeInfo()) + var nodeinfo []byte + a.core.router.doAdmin(func() { + nodeinfo = []byte(a.core.nodeinfo.getNodeInfo()) + }) var jsoninfo interface{} if err := json.Unmarshal(nodeinfo, &jsoninfo); err != nil { return admin_info{}, err diff --git a/src/yggdrasil/router.go b/src/yggdrasil/router.go index 68fb025..b4e16a3 100644 --- a/src/yggdrasil/router.go +++ b/src/yggdrasil/router.go @@ -127,7 +127,9 @@ func (r *router) mainLoop() { case f := <-r.admin: f() case e := <-r.reconfigure: - e <- nil + r.core.configMutex.RLock() + e <- r.core.nodeinfo.setNodeInfo(r.core.config.NodeInfo, r.core.config.NodeInfoPrivacy) + r.core.configMutex.RUnlock() } } } From 9e486ed4fe7ea68e4aa5618611a2eaf659c004ce Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 14 Jan 2019 19:05:16 +0000 Subject: [PATCH 051/109] Move nodeinfo into router --- src/yggdrasil/admin.go | 8 ++++---- src/yggdrasil/core.go | 8 ++------ src/yggdrasil/nodeinfo.go | 2 +- src/yggdrasil/router.go | 8 ++++++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/yggdrasil/admin.go b/src/yggdrasil/admin.go index a44044a..db85629 100644 --- a/src/yggdrasil/admin.go +++ b/src/yggdrasil/admin.go @@ -352,7 +352,7 @@ func (a *admin) init(c *Core) { if in["box_pub_key"] == nil && in["coords"] == nil { var nodeinfo []byte a.core.router.doAdmin(func() { - nodeinfo = []byte(a.core.nodeinfo.getNodeInfo()) + nodeinfo = []byte(a.core.router.nodeinfo.getNodeInfo()) }) var jsoninfo interface{} if err := json.Unmarshal(nodeinfo, &jsoninfo); err != nil { @@ -864,7 +864,7 @@ func (a *admin) admin_getNodeInfo(keyString, coordString string, nocache bool) ( copy(key[:], keyBytes) } if !nocache { - if response, err := a.core.nodeinfo.getCachedNodeInfo(key); err == nil { + if response, err := a.core.router.nodeinfo.getCachedNodeInfo(key); err == nil { return response, nil } } @@ -882,14 +882,14 @@ func (a *admin) admin_getNodeInfo(keyString, coordString string, nocache bool) ( } response := make(chan *nodeinfoPayload, 1) sendNodeInfoRequest := func() { - a.core.nodeinfo.addCallback(key, func(nodeinfo *nodeinfoPayload) { + a.core.router.nodeinfo.addCallback(key, func(nodeinfo *nodeinfoPayload) { defer func() { recover() }() select { case response <- nodeinfo: default: } }) - a.core.nodeinfo.sendNodeInfo(key, coords, false) + a.core.router.nodeinfo.sendNodeInfo(key, coords, false) } a.core.router.doAdmin(sendNodeInfoRequest) go func() { diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index c6d6f4a..dc1b8f0 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -44,7 +44,6 @@ type Core struct { admin admin searches searches multicast multicast - nodeinfo nodeinfo tcp tcpInterface awdl awdl log *log.Logger @@ -83,7 +82,6 @@ func (c *Core) init() error { copy(c.sigPriv[:], sigPrivHex) c.admin.init(c) - c.nodeinfo.init(c) c.searches.init(c) c.dht.init(c) c.sessions.init(c) @@ -197,8 +195,6 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error { c.init() - c.nodeinfo.setNodeInfo(nc.NodeInfo, nc.NodeInfoPrivacy) - if err := c.tcp.init(c); err != nil { c.log.Println("Failed to start TCP interface") return err @@ -297,12 +293,12 @@ func (c *Core) GetSubnet() *net.IPNet { // Gets the nodeinfo. func (c *Core) GetNodeInfo() nodeinfoPayload { - return c.nodeinfo.getNodeInfo() + return c.router.nodeinfo.getNodeInfo() } // Sets the nodeinfo. func (c *Core) SetNodeInfo(nodeinfo interface{}, nodeinfoprivacy bool) { - c.nodeinfo.setNodeInfo(nodeinfo, nodeinfoprivacy) + c.router.nodeinfo.setNodeInfo(nodeinfo, nodeinfoprivacy) } // Sets the output logger of the Yggdrasil node after startup. This may be diff --git a/src/yggdrasil/nodeinfo.go b/src/yggdrasil/nodeinfo.go index b907632..963a2fc 100644 --- a/src/yggdrasil/nodeinfo.go +++ b/src/yggdrasil/nodeinfo.go @@ -170,7 +170,7 @@ func (m *nodeinfo) sendNodeInfo(key crypto.BoxPubKey, coords []byte, isResponse nodeinfo := nodeinfoReqRes{ SendCoords: table.self.getCoords(), IsResponse: isResponse, - NodeInfo: m.core.nodeinfo.getNodeInfo(), + NodeInfo: m.getNodeInfo(), } bs := nodeinfo.encode() shared := m.core.sessions.getSharedKey(&m.core.boxPriv, &key) diff --git a/src/yggdrasil/router.go b/src/yggdrasil/router.go index b4e16a3..11509d4 100644 --- a/src/yggdrasil/router.go +++ b/src/yggdrasil/router.go @@ -51,6 +51,7 @@ type router struct { reset chan struct{} // signal that coords changed (re-init sessions/dht) admin chan func() // pass a lambda for the admin socket to query stuff cryptokey cryptokey + nodeinfo nodeinfo } // Packet and session info, used to check that the packet matches a valid IP range or CKR prefix before sending to the tun. @@ -85,6 +86,9 @@ func (r *router) init(core *Core) { r.send = send r.reset = make(chan struct{}, 1) r.admin = make(chan func(), 32) + r.core.configMutex.RLock() + r.nodeinfo.setNodeInfo(r.core.config.NodeInfo, r.core.config.NodeInfoPrivacy) + r.core.configMutex.RUnlock() r.cryptokey.init(r.core) r.tun.init(r.core, send, recv) } @@ -128,7 +132,7 @@ func (r *router) mainLoop() { f() case e := <-r.reconfigure: r.core.configMutex.RLock() - e <- r.core.nodeinfo.setNodeInfo(r.core.config.NodeInfo, r.core.config.NodeInfoPrivacy) + e <- r.nodeinfo.setNodeInfo(r.core.config.NodeInfo, r.core.config.NodeInfoPrivacy) r.core.configMutex.RUnlock() } } @@ -469,7 +473,7 @@ func (r *router) handleNodeInfo(bs []byte, fromKey *crypto.BoxPubKey) { return } req.SendPermPub = *fromKey - r.core.nodeinfo.handleNodeInfo(&req) + r.nodeinfo.handleNodeInfo(&req) } // Passed a function to call. From f6b663c2578ae5855e5c915233b55cb4342baa7f Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 14 Jan 2019 19:27:13 +0000 Subject: [PATCH 052/109] Make multicasting use config instead of ifceExpr in Core --- cmd/yggdrasil/main.go | 10 ---------- src/yggdrasil/core.go | 9 --------- src/yggdrasil/multicast.go | 21 ++++++++++++++------- 3 files changed, 14 insertions(+), 26 deletions(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index cc54d3d..c3add0c 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -9,7 +9,6 @@ import ( "log" "os" "os/signal" - "regexp" "strings" "syscall" @@ -221,15 +220,6 @@ func main() { // Setup the Yggdrasil node itself. The node{} type includes a Core, so we // don't need to create this manually. n := node{} - // Check to see if any multicast interface expressions were provided in the - // config. If they were then set them now. - for _, ll := range cfg.MulticastInterfaces { - ifceExpr, err := regexp.Compile(ll) - if err != nil { - panic(err) - } - n.core.AddMulticastInterfaceExpr(ifceExpr) - } // Now that we have a working configuration, we can now actually start // Yggdrasil. This will start the router, switch, DHT node, TCP and UDP // sockets, TUN/TAP adapter and multicast discovery port. diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index dc1b8f0..3382562 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -5,7 +5,6 @@ import ( "io/ioutil" "log" "net" - "regexp" "sync" "time" @@ -47,7 +46,6 @@ type Core struct { tcp tcpInterface awdl awdl log *log.Logger - ifceExpr []*regexp.Regexp // the zone of link-local IPv6 peers must match this } func (c *Core) init() error { @@ -313,13 +311,6 @@ func (c *Core) AddPeer(addr string, sintf string) error { return c.admin.addPeer(addr, sintf) } -// Adds an expression to select multicast interfaces for peer discovery. This -// should be done before calling Start. This function can be called multiple -// times to add multiple search expressions. -func (c *Core) AddMulticastInterfaceExpr(expr *regexp.Regexp) { - c.ifceExpr = append(c.ifceExpr, expr) -} - // Adds an allowed public key. This allow peerings to be restricted only to // keys that you have selected. func (c *Core) AddAllowedEncryptionPublicKey(boxStr string) error { diff --git a/src/yggdrasil/multicast.go b/src/yggdrasil/multicast.go index cf84fed..4087895 100644 --- a/src/yggdrasil/multicast.go +++ b/src/yggdrasil/multicast.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "net" + "regexp" "sync" "time" @@ -35,15 +36,13 @@ func (m *multicast) init(core *Core) { }() m.groupAddr = "[ff02::114]:9001" // Check if we've been given any expressions - if len(m.core.ifceExpr) == 0 { - return + if count := len(m.interfaces()); count != 0 { + m.core.log.Println("Found", count, "multicast interface(s)") } - // Ask the system for network interfaces - m.core.log.Println("Found", len(m.interfaces()), "multicast interface(s)") } func (m *multicast) start() error { - if len(m.core.ifceExpr) == 0 { + if len(m.interfaces()) == 0 { m.core.log.Println("Multicast discovery is disabled") } else { m.core.log.Println("Multicast discovery is enabled") @@ -71,6 +70,10 @@ func (m *multicast) start() error { } func (m *multicast) interfaces() []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 allifaces, err := net.Interfaces() @@ -91,8 +94,12 @@ func (m *multicast) interfaces() []net.Interface { // Ignore point-to-point interfaces continue } - for _, expr := range m.core.ifceExpr { - if expr.MatchString(iface.Name) { + for _, expr := range exprs { + e, err := regexp.Compile(expr) + if err != nil { + panic(err) + } + if e.MatchString(iface.Name) { interfaces = append(interfaces, iface) } } From d9ddf30faf2998c1ad814253a1dadf927527eeee Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 14 Jan 2019 19:29:22 +0000 Subject: [PATCH 053/109] Fix debug builds --- src/yggdrasil/debug.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yggdrasil/debug.go b/src/yggdrasil/debug.go index 6bd5430..7c6c757 100644 --- a/src/yggdrasil/debug.go +++ b/src/yggdrasil/debug.go @@ -517,7 +517,7 @@ func (c *Core) DEBUG_setLogger(log *log.Logger) { } func (c *Core) DEBUG_setIfceExpr(expr *regexp.Regexp) { - c.ifceExpr = append(c.ifceExpr, expr) + c.log.Println("DEBUG_setIfceExpr no longer implemented") } func (c *Core) DEBUG_addAllowedEncryptionPublicKey(boxStr string) { From 88cf6b768446e5b4f21fd3f5ea546d230deb3cd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Christian=20Gr=C3=BCnhage?= Date: Tue, 15 Jan 2019 02:28:27 +0100 Subject: [PATCH 054/109] add ansible key generator --- contrib/ansible/genkeys.go | 123 +++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 contrib/ansible/genkeys.go diff --git a/contrib/ansible/genkeys.go b/contrib/ansible/genkeys.go new file mode 100644 index 0000000..1578575 --- /dev/null +++ b/contrib/ansible/genkeys.go @@ -0,0 +1,123 @@ +/* + +This file generates crypto keys for [ansible-yggdrasil](https://github.com/jcgruenhage/ansible-yggdrasil/) + +*/ +package main + +import ( + "encoding/hex" + "flag" + "fmt" + "net" + "os" + + "github.com/yggdrasil-network/yggdrasil-go/src/address" + "github.com/yggdrasil-network/yggdrasil-go/src/crypto" +) + +var numHosts = flag.Int("hosts", 1, "number of host vars to generate") +var keyTries = flag.Int("tries", 1000, "number of tries before taking the best keys") + +type keySet struct { + priv []byte + pub []byte + id []byte + ip string +} + +func main() { + flag.Parse() + + if *numHosts > *keyTries { + println("Can't generate less keys than hosts.") + return + } + + var encryptionKeys []keySet + for i := 0; i < *numHosts + 1; i++ { + encryptionKeys = append(encryptionKeys, newBoxKey()) + } + encryptionKeys = sortKeySetArray(encryptionKeys) + for i := 0; i < *keyTries - *numHosts - 1; i++ { + encryptionKeys[0] = newBoxKey(); + encryptionKeys = bubbleUpTo(encryptionKeys, 0) + } + + var signatureKeys []keySet + for i := 0; i < *numHosts + 1; i++ { + signatureKeys = append(signatureKeys, newSigKey()) + } + signatureKeys = sortKeySetArray(signatureKeys) + for i := 0; i < *keyTries - *numHosts - 1; i++ { + signatureKeys[0] = newSigKey(); + signatureKeys = bubbleUpTo(signatureKeys, 0) + } + + os.MkdirAll("ansible/host_vars", 0755) + + for i := 1; i <= *numHosts; i++ { + os.MkdirAll(fmt.Sprintf("ansible/host_vars/%x", i), 0755) + file, err := os.Create(fmt.Sprintf("ansible/host_vars/%x/vars", i)) + if err != nil { + return + } + defer file.Close() + file.WriteString(fmt.Sprintf("yggdrasil_encryption_public_key: %v\n", hex.EncodeToString(encryptionKeys[i].pub))) + file.WriteString("yggdrasil_encryption_private_key: \"{{ vault_yggdrasil_encryption_private_key }}\"\n") + file.WriteString(fmt.Sprintf("yggdrasil_signing_public_key: %v\n", hex.EncodeToString(signatureKeys[i].pub))) + file.WriteString("yggdrasil_signing_public_key: \"{{ vault_yggdrasil_signing_private_key }}\"\n") + file.WriteString(fmt.Sprintf("ansible_host: %v\n", encryptionKeys[i].ip)) + + file, err = os.Create(fmt.Sprintf("ansible/host_vars/%x/vault", i)) + if err != nil { + return + } + defer file.Close() + file.WriteString(fmt.Sprintf("vault_yggdrasil_encryption_private_key: %v\n", hex.EncodeToString(encryptionKeys[i].priv))) + file.WriteString(fmt.Sprintf("vault_yggdrasil_signing_private_key: %v\n", hex.EncodeToString(signatureKeys[i].priv))) + } +} + +func newBoxKey() keySet { + pub, priv := crypto.NewBoxKeys() + id := crypto.GetNodeID(pub) + ip := net.IP(address.AddrForNodeID(id)[:]).String() + return keySet{priv[:], pub[:], id[:], ip} +} + +func newSigKey() keySet { + pub, priv := crypto.NewSigKeys() + id := crypto.GetTreeID(pub) + return keySet{priv[:], pub[:], id[:], ""} +} + +func isBetter(oldID, newID []byte) bool { + for idx := range oldID { + if newID[idx] > oldID[idx] { + return true + } + if newID[idx] < oldID[idx] { + return false + } + } + return false +} + +func sortKeySetArray(sets []keySet) []keySet { + for i := 0; i < len(sets); i++ { + sets = bubbleUpTo(sets, i) + } + return sets +} + +func bubbleUpTo(sets []keySet, num int) []keySet { + for i := 0; i < len(sets) - num - 1; i++ { + if isBetter(sets[i + 1].id, sets[i].id) { + var tmp = sets[i] + sets[i] = sets[i + 1] + sets[i + 1] = tmp + } + } + return sets +} From 39567bed83308cc1431943eb91ea321fb5e33d14 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 15 Jan 2019 08:44:33 +0000 Subject: [PATCH 055/109] Address some comments --- src/yggdrasil/ckr.go | 14 ++++++-------- src/yggdrasil/tun.go | 1 + 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/yggdrasil/ckr.go b/src/yggdrasil/ckr.go index 14464f6..a5ed455 100644 --- a/src/yggdrasil/ckr.go +++ b/src/yggdrasil/ckr.go @@ -38,14 +38,12 @@ func (c *cryptokey) init(core *Core) { c.reconfigure = make(chan chan error, 1) go func() { for { - select { - case e := <-c.reconfigure: - var err error - c.core.router.doAdmin(func() { - err = c.core.router.cryptokey.configure() - }) - e <- err - } + e := <-c.reconfigure + var err error + c.core.router.doAdmin(func() { + err = c.core.router.cryptokey.configure() + }) + e <- err } }() diff --git a/src/yggdrasil/tun.go b/src/yggdrasil/tun.go index 0bda312..46fabbc 100644 --- a/src/yggdrasil/tun.go +++ b/src/yggdrasil/tun.go @@ -54,6 +54,7 @@ func (tun *tunAdapter) init(core *Core, send chan<- []byte, recv <-chan []byte) tun.core.config.IfMTU != tun.core.configOld.IfMTU tun.core.configMutex.RUnlock() if updated { + tun.core.log.Println("Reconfiguring TUN/TAP is not supported yet") e <- nil } else { e <- nil From 2cd373fc1e41a115299dc8f56bd2d4e6b0b40ab6 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 15 Jan 2019 08:51:19 +0000 Subject: [PATCH 056/109] Remove unnecessary selects --- src/yggdrasil/admin.go | 18 ++++++++---------- src/yggdrasil/dht.go | 6 ++---- src/yggdrasil/multicast.go | 12 +++++------- src/yggdrasil/peer.go | 6 ++---- src/yggdrasil/search.go | 6 ++---- src/yggdrasil/session.go | 26 ++++++++++++-------------- src/yggdrasil/tcp.go | 22 ++++++++++------------ src/yggdrasil/tun.go | 24 +++++++++++------------- 8 files changed, 52 insertions(+), 68 deletions(-) diff --git a/src/yggdrasil/admin.go b/src/yggdrasil/admin.go index db85629..5524fe2 100644 --- a/src/yggdrasil/admin.go +++ b/src/yggdrasil/admin.go @@ -57,17 +57,15 @@ func (a *admin) init(c *Core) { a.reconfigure = make(chan chan error, 1) go func() { for { - select { - case e := <-a.reconfigure: - a.core.configMutex.RLock() - if a.core.config.AdminListen != a.core.configOld.AdminListen { - a.listenaddr = a.core.config.AdminListen - a.close() - a.start() - } - a.core.configMutex.RUnlock() - e <- nil + e := <-a.reconfigure + a.core.configMutex.RLock() + if a.core.config.AdminListen != a.core.configOld.AdminListen { + a.listenaddr = a.core.config.AdminListen + a.close() + a.start() } + a.core.configMutex.RUnlock() + e <- nil } }() a.core.configMutex.RLock() diff --git a/src/yggdrasil/dht.go b/src/yggdrasil/dht.go index bba6dfc..5427aca 100644 --- a/src/yggdrasil/dht.go +++ b/src/yggdrasil/dht.go @@ -82,10 +82,8 @@ func (t *dht) init(c *Core) { t.reconfigure = make(chan chan error, 1) go func() { for { - select { - case e := <-t.reconfigure: - e <- nil - } + e := <-t.reconfigure + e <- nil } }() t.nodeID = *t.core.GetNodeID() diff --git a/src/yggdrasil/multicast.go b/src/yggdrasil/multicast.go index 4087895..08f0954 100644 --- a/src/yggdrasil/multicast.go +++ b/src/yggdrasil/multicast.go @@ -25,13 +25,11 @@ func (m *multicast) init(core *Core) { m.reconfigure = make(chan chan error, 1) go func() { for { - select { - case e := <-m.reconfigure: - m.myAddrMutex.Lock() - m.myAddr = m.core.tcp.getAddr() - m.myAddrMutex.Unlock() - e <- nil - } + e := <-m.reconfigure + m.myAddrMutex.Lock() + m.myAddr = m.core.tcp.getAddr() + m.myAddrMutex.Unlock() + e <- nil } }() m.groupAddr = "[ff02::114]:9001" diff --git a/src/yggdrasil/peer.go b/src/yggdrasil/peer.go index 98cbe02..c83504f 100644 --- a/src/yggdrasil/peer.go +++ b/src/yggdrasil/peer.go @@ -35,10 +35,8 @@ func (ps *peers) init(c *Core) { ps.reconfigure = make(chan chan error, 1) go func() { for { - select { - case e := <-ps.reconfigure: - e <- nil - } + e := <-ps.reconfigure + e <- nil } }() ps.allowedEncryptionPublicKeys = make(map[crypto.BoxPubKey]struct{}) diff --git a/src/yggdrasil/search.go b/src/yggdrasil/search.go index 8106fb7..c391dda 100644 --- a/src/yggdrasil/search.go +++ b/src/yggdrasil/search.go @@ -53,10 +53,8 @@ func (s *searches) init(core *Core) { s.reconfigure = make(chan chan error, 1) go func() { for { - select { - case e := <-s.reconfigure: - e <- nil - } + e := <-s.reconfigure + e <- nil } }() s.searches = make(map[crypto.NodeID]*searchInfo) diff --git a/src/yggdrasil/session.go b/src/yggdrasil/session.go index 3cd1cf7..e29cd4f 100644 --- a/src/yggdrasil/session.go +++ b/src/yggdrasil/session.go @@ -131,21 +131,19 @@ func (ss *sessions) init(core *Core) { ss.reconfigure = make(chan chan error, 1) go func() { for { - select { - case e := <-ss.reconfigure: - responses := make(map[crypto.Handle]chan error) - for index, session := range ss.sinfos { - responses[index] = make(chan error) - session.reconfigure <- responses[index] - } - for _, response := range responses { - if err := <-response; err != nil { - e <- err - continue - } - } - e <- nil + e := <-ss.reconfigure + responses := make(map[crypto.Handle]chan error) + for index, session := range ss.sinfos { + responses[index] = make(chan error) + session.reconfigure <- responses[index] } + for _, response := range responses { + if err := <-response; err != nil { + e <- err + continue + } + } + e <- nil } }() ss.permShared = make(map[crypto.BoxPubKey]*crypto.BoxSharedKey) diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index b47b553..9cab9ea 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -90,18 +90,16 @@ func (iface *tcpInterface) init(core *Core) (err error) { iface.reconfigure = make(chan chan error, 1) go func() { for { - select { - case e := <-iface.reconfigure: - iface.core.configMutex.RLock() - updated := iface.core.config.Listen != iface.core.configOld.Listen - iface.core.configMutex.RUnlock() - if updated { - iface.serv_stop <- true - iface.serv.Close() - e <- iface.listen() - } else { - e <- nil - } + e := <-iface.reconfigure + iface.core.configMutex.RLock() + updated := iface.core.config.Listen != iface.core.configOld.Listen + iface.core.configMutex.RUnlock() + if updated { + iface.serv_stop <- true + iface.serv.Close() + e <- iface.listen() + } else { + e <- nil } } }() diff --git a/src/yggdrasil/tun.go b/src/yggdrasil/tun.go index 46fabbc..c0a7139 100644 --- a/src/yggdrasil/tun.go +++ b/src/yggdrasil/tun.go @@ -46,19 +46,17 @@ func (tun *tunAdapter) init(core *Core, send chan<- []byte, recv <-chan []byte) tun.icmpv6.init(tun) go func() { for { - select { - case e := <-tun.reconfigure: - tun.core.configMutex.RLock() - updated := tun.core.config.IfName != tun.core.configOld.IfName || - tun.core.config.IfTAPMode != tun.core.configOld.IfTAPMode || - tun.core.config.IfMTU != tun.core.configOld.IfMTU - tun.core.configMutex.RUnlock() - if updated { - tun.core.log.Println("Reconfiguring TUN/TAP is not supported yet") - e <- nil - } else { - e <- nil - } + e := <-tun.reconfigure + tun.core.configMutex.RLock() + updated := tun.core.config.IfName != tun.core.configOld.IfName || + tun.core.config.IfTAPMode != tun.core.configOld.IfTAPMode || + tun.core.config.IfMTU != tun.core.configOld.IfMTU + tun.core.configMutex.RUnlock() + if updated { + tun.core.log.Println("Reconfiguring TUN/TAP is not supported yet") + e <- nil + } else { + e <- nil } } }() From 53be1b02f3a8685bf56c1d7371fbf0822af954b7 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 15 Jan 2019 08:53:57 +0000 Subject: [PATCH 057/109] Check if accepting socket produced an error --- src/yggdrasil/tcp.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index 9cab9ea..c90c3ff 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -140,6 +140,10 @@ func (iface *tcpInterface) listener() { iface.core.log.Println("Listening for TCP on:", iface.serv.Addr().String()) for { sock, err := iface.serv.Accept() + if err != nil { + iface.core.log.Println("Failed to accept connection:", err) + return + } select { case <-iface.serv_stop: iface.core.log.Println("Stopping listener") From 21306532c1a6e49abe255cc6c809e6bfecf03c09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Christian=20Gr=C3=BCnhage?= Date: Tue, 15 Jan 2019 16:48:25 +0100 Subject: [PATCH 058/109] update ansible key generator It's probably easier to use without it having an ansible folder prefix, if people want to put it into a different dir then they should change their workdir. Apart from that, this fixes a bug where I defined yggdrasil_signing_public_key twice, but never the private key. --- contrib/ansible/genkeys.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/contrib/ansible/genkeys.go b/contrib/ansible/genkeys.go index 1578575..22418a0 100644 --- a/contrib/ansible/genkeys.go +++ b/contrib/ansible/genkeys.go @@ -54,11 +54,11 @@ func main() { signatureKeys = bubbleUpTo(signatureKeys, 0) } - os.MkdirAll("ansible/host_vars", 0755) + os.MkdirAll("host_vars", 0755) for i := 1; i <= *numHosts; i++ { - os.MkdirAll(fmt.Sprintf("ansible/host_vars/%x", i), 0755) - file, err := os.Create(fmt.Sprintf("ansible/host_vars/%x/vars", i)) + os.MkdirAll(fmt.Sprintf("host_vars/%x", i), 0755) + file, err := os.Create(fmt.Sprintf("host_vars/%x/vars", i)) if err != nil { return } @@ -66,10 +66,10 @@ func main() { file.WriteString(fmt.Sprintf("yggdrasil_encryption_public_key: %v\n", hex.EncodeToString(encryptionKeys[i].pub))) file.WriteString("yggdrasil_encryption_private_key: \"{{ vault_yggdrasil_encryption_private_key }}\"\n") file.WriteString(fmt.Sprintf("yggdrasil_signing_public_key: %v\n", hex.EncodeToString(signatureKeys[i].pub))) - file.WriteString("yggdrasil_signing_public_key: \"{{ vault_yggdrasil_signing_private_key }}\"\n") + file.WriteString("yggdrasil_signing_private_key: \"{{ vault_yggdrasil_signing_private_key }}\"\n") file.WriteString(fmt.Sprintf("ansible_host: %v\n", encryptionKeys[i].ip)) - file, err = os.Create(fmt.Sprintf("ansible/host_vars/%x/vault", i)) + file, err = os.Create(fmt.Sprintf("host_vars/%x/vault", i)) if err != nil { return } From 4fba5586380b59ba063fd5adaded1dec7c6f9cbf Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 16 Jan 2019 13:20:12 +0000 Subject: [PATCH 059/109] Fix concurrent map write in tcp.go --- src/yggdrasil/tcp.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index c90c3ff..1ebf0b6 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -125,8 +125,10 @@ func (iface *tcpInterface) listen() error { } iface.serv, err = lc.Listen(ctx, "tcp", iface.tcp_addr) 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() return nil } @@ -187,7 +189,9 @@ func (iface *tcpInterface) call(saddr string, socksaddr *string, sintf string) { if iface.isAlreadyCalling(saddr) { return } + iface.mutex.Lock() iface.calls[callname] = struct{}{} + iface.mutex.Unlock() defer func() { // Block new calls for a little while, to mitigate livelock scenarios time.Sleep(default_tcp_timeout) From c85dbaea958bd2be603dcbb1012c1bb3845664a5 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 16 Jan 2019 13:23:26 +0000 Subject: [PATCH 060/109] Fix missing nodeinfo.init --- src/yggdrasil/router.go | 1 + 1 file changed, 1 insertion(+) diff --git a/src/yggdrasil/router.go b/src/yggdrasil/router.go index 11509d4..31eefbb 100644 --- a/src/yggdrasil/router.go +++ b/src/yggdrasil/router.go @@ -86,6 +86,7 @@ func (r *router) init(core *Core) { r.send = send r.reset = make(chan struct{}, 1) r.admin = make(chan func(), 32) + r.nodeinfo.init(r.core) r.core.configMutex.RLock() r.nodeinfo.setNodeInfo(r.core.config.NodeInfo, r.core.config.NodeInfoPrivacy) r.core.configMutex.RUnlock() From 8baf593b62454b612c145179560ed1d312637e3e Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 16 Jan 2019 14:52:27 +0000 Subject: [PATCH 061/109] Update source address selection when sintf specified --- src/yggdrasil/tcp.go | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index 1ebf0b6..ca9ea09 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -230,35 +230,38 @@ func (iface *tcpInterface) call(saddr string, socksaddr *string, sintf string) { ief, err := net.InterfaceByName(sintf) if err != nil { return - } else { - if ief.Flags&net.FlagUp == 0 { + } + if ief.Flags&net.FlagUp == 0 { + return + } + addrs, err := ief.Addrs() + if err == nil { + dst, err := net.ResolveTCPAddr("tcp", saddr) + if err != nil { return } - addrs, err := ief.Addrs() - if err == nil { - dst, err := net.ResolveTCPAddr("tcp", saddr) + for addrindex, addr := range addrs { + src, _, err := net.ParseCIDR(addr.String()) if err != nil { - return + continue } - for _, addr := range addrs { - src, _, err := net.ParseCIDR(addr.String()) - if err != nil { - continue - } - if (src.To4() != nil) == (dst.IP.To4() != nil) && src.IsGlobalUnicast() { + if (src.To4() != nil) == (dst.IP.To4() != nil) { + if addrindex == len(addrs)-1 || src.IsGlobalUnicast() { dialer.LocalAddr = &net.TCPAddr{ IP: src, Port: 0, + Zone: sintf, } break } } - if dialer.LocalAddr == nil { - return - } + } + if dialer.LocalAddr == nil { + return } } } + conn, err = dialer.Dial("tcp", saddr) if err != nil { return From 68eb684f6df3aa158ff1a6f40864df93bcaabcda Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 16 Jan 2019 19:27:44 +0000 Subject: [PATCH 062/109] Fix mobile.go now that multicast interfaces are handled internally --- src/yggdrasil/mobile.go | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/yggdrasil/mobile.go b/src/yggdrasil/mobile.go index 2ffeffb..52215f0 100644 --- a/src/yggdrasil/mobile.go +++ b/src/yggdrasil/mobile.go @@ -7,7 +7,6 @@ import ( "encoding/json" "log" "os" - "regexp" "time" hjson "github.com/hjson/hjson-go" @@ -52,10 +51,6 @@ func (c *Core) StartAutoconfigure() error { if hostname, err := os.Hostname(); err == nil { nc.NodeInfo = map[string]interface{}{"name": hostname} } - ifceExpr, err := regexp.Compile(".*") - if err == nil { - c.ifceExpr = append(c.ifceExpr, ifceExpr) - } if err := c.Start(nc, logger); err != nil { return err } @@ -77,15 +72,6 @@ func (c *Core) StartJSON(configjson []byte) error { return err } nc.IfName = "dummy" - //c.log.Println(nc.MulticastInterfaces) - for _, ll := range nc.MulticastInterfaces { - //c.log.Println("Processing MC", ll) - ifceExpr, err := regexp.Compile(ll) - if err != nil { - panic(err) - } - c.AddMulticastInterfaceExpr(ifceExpr) - } if err := c.Start(nc, logger); err != nil { return err } From fdf300a1ffab2139a19ec13610fff40a669f9958 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 16 Jan 2019 20:26:39 +0000 Subject: [PATCH 063/109] Handle AllowedEncryptionPublicKeys internally --- cmd/yggdrasil/main.go | 5 ---- src/yggdrasil/admin.go | 25 ++++------------- src/yggdrasil/peer.go | 63 +++++++++++++++++++++++------------------- 3 files changed, 40 insertions(+), 53 deletions(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index c3add0c..f11bbc0 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -227,11 +227,6 @@ func main() { logger.Println("An error occurred during startup") panic(err) } - // Check to see if any allowed encryption keys were provided in the config. - // If they were then set them now. - for _, pBoxStr := range cfg.AllowedEncryptionPublicKeys { - n.core.AddAllowedEncryptionPublicKey(pBoxStr) - } // The Stop function ensures that the TUN/TAP adapter is correctly shut down // before the program exits. defer func() { diff --git a/src/yggdrasil/admin.go b/src/yggdrasil/admin.go index 5524fe2..f601776 100644 --- a/src/yggdrasil/admin.go +++ b/src/yggdrasil/admin.go @@ -765,35 +765,20 @@ func (a *admin) getData_getSessions() []admin_nodeInfo { // getAllowedEncryptionPublicKeys returns the public keys permitted for incoming peer connections. func (a *admin) getAllowedEncryptionPublicKeys() []string { - pubs := a.core.peers.getAllowedEncryptionPublicKeys() - var out []string - for _, pub := range pubs { - out = append(out, hex.EncodeToString(pub[:])) - } - return out + return a.core.peers.getAllowedEncryptionPublicKeys() } // addAllowedEncryptionPublicKey whitelists a key for incoming peer connections. func (a *admin) addAllowedEncryptionPublicKey(bstr string) (err error) { - boxBytes, err := hex.DecodeString(bstr) - if err == nil { - var box crypto.BoxPubKey - copy(box[:], boxBytes) - a.core.peers.addAllowedEncryptionPublicKey(&box) - } - return + a.core.peers.addAllowedEncryptionPublicKey(bstr) + return nil } // removeAllowedEncryptionPublicKey removes a key from the whitelist for incoming peer connections. // If none are set, an empty list permits all incoming connections. func (a *admin) removeAllowedEncryptionPublicKey(bstr string) (err error) { - boxBytes, err := hex.DecodeString(bstr) - if err == nil { - var box crypto.BoxPubKey - copy(box[:], boxBytes) - a.core.peers.removeAllowedEncryptionPublicKey(&box) - } - return + a.core.peers.removeAllowedEncryptionPublicKey(bstr) + return nil } // Send a DHT ping to the node with the provided key and coords, optionally looking up the specified target NodeID. diff --git a/src/yggdrasil/peer.go b/src/yggdrasil/peer.go index c83504f..2cd1afe 100644 --- a/src/yggdrasil/peer.go +++ b/src/yggdrasil/peer.go @@ -5,6 +5,7 @@ package yggdrasil // Live code should be better commented import ( + "encoding/hex" "sync" "sync/atomic" "time" @@ -18,12 +19,10 @@ import ( // In most cases, this involves passing the packet to the handler for outgoing traffic to another peer. // In other cases, it's link protocol traffic used to build the spanning tree, in which case this checks signatures and passes the message along to the switch. type peers struct { - core *Core - reconfigure chan chan error - mutex sync.Mutex // Synchronize writes to atomic - ports atomic.Value //map[switchPort]*peer, use CoW semantics - authMutex sync.RWMutex - allowedEncryptionPublicKeys map[crypto.BoxPubKey]struct{} + core *Core + reconfigure chan chan error + mutex sync.Mutex // Synchronize writes to atomic + ports atomic.Value //map[switchPort]*peer, use CoW semantics } // Initializes the peers struct. @@ -39,40 +38,48 @@ func (ps *peers) init(c *Core) { e <- nil } }() - ps.allowedEncryptionPublicKeys = make(map[crypto.BoxPubKey]struct{}) } -// Returns true if an incoming peer connection to a key is allowed, either because the key is in the whitelist or because the whitelist is empty. +// Returns true if an incoming peer connection to a key is allowed, either +// because the key is in the whitelist or because the whitelist is empty. func (ps *peers) isAllowedEncryptionPublicKey(box *crypto.BoxPubKey) bool { - ps.authMutex.RLock() - defer ps.authMutex.RUnlock() - _, isIn := ps.allowedEncryptionPublicKeys[*box] - return isIn || len(ps.allowedEncryptionPublicKeys) == 0 + boxstr := hex.EncodeToString(box[:]) + ps.core.configMutex.RLock() + defer ps.core.configMutex.RUnlock() + for _, v := range ps.core.config.AllowedEncryptionPublicKeys { + if v == boxstr { + return true + } + } + return len(ps.core.config.AllowedEncryptionPublicKeys) == 0 } // Adds a key to the whitelist. -func (ps *peers) addAllowedEncryptionPublicKey(box *crypto.BoxPubKey) { - ps.authMutex.Lock() - defer ps.authMutex.Unlock() - ps.allowedEncryptionPublicKeys[*box] = struct{}{} +func (ps *peers) addAllowedEncryptionPublicKey(box string) { + ps.core.configMutex.RLock() + defer ps.core.configMutex.RUnlock() + ps.core.config.AllowedEncryptionPublicKeys = + append(ps.core.config.AllowedEncryptionPublicKeys, box) } // Removes a key from the whitelist. -func (ps *peers) removeAllowedEncryptionPublicKey(box *crypto.BoxPubKey) { - ps.authMutex.Lock() - defer ps.authMutex.Unlock() - delete(ps.allowedEncryptionPublicKeys, *box) +func (ps *peers) removeAllowedEncryptionPublicKey(box string) { + ps.core.configMutex.RLock() + defer ps.core.configMutex.RUnlock() + for k, v := range ps.core.config.AllowedEncryptionPublicKeys { + if v == box { + ps.core.config.AllowedEncryptionPublicKeys = + append(ps.core.config.AllowedEncryptionPublicKeys[:k], + ps.core.config.AllowedEncryptionPublicKeys[k+1:]...) + } + } } // Gets the whitelist of allowed keys for incoming connections. -func (ps *peers) getAllowedEncryptionPublicKeys() []crypto.BoxPubKey { - ps.authMutex.RLock() - defer ps.authMutex.RUnlock() - keys := make([]crypto.BoxPubKey, 0, len(ps.allowedEncryptionPublicKeys)) - for key := range ps.allowedEncryptionPublicKeys { - keys = append(keys, key) - } - return keys +func (ps *peers) getAllowedEncryptionPublicKeys() []string { + ps.core.configMutex.RLock() + defer ps.core.configMutex.RUnlock() + return ps.core.config.AllowedEncryptionPublicKeys } // Atomically gets a map[switchPort]*peer of known peers. From 9d5085492ee045c658ee7d9eb3777b17828a6826 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 16 Jan 2019 20:38:51 +0000 Subject: [PATCH 064/109] Handle session firewall using central config --- src/yggdrasil/core.go | 9 ------ src/yggdrasil/session.go | 63 +++++++++------------------------------- 2 files changed, 14 insertions(+), 58 deletions(-) diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index 3382562..ed07581 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -212,15 +212,6 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error { return err } - c.sessions.setSessionFirewallState(nc.SessionFirewall.Enable) - c.sessions.setSessionFirewallDefaults( - nc.SessionFirewall.AllowFromDirect, - nc.SessionFirewall.AllowFromRemote, - nc.SessionFirewall.AlwaysAllowOutbound, - ) - c.sessions.setSessionFirewallWhitelist(nc.SessionFirewall.WhitelistEncryptionPublicKeys) - c.sessions.setSessionFirewallBlacklist(nc.SessionFirewall.BlacklistEncryptionPublicKeys) - if err := c.router.start(); err != nil { c.log.Println("Failed to start router") return err diff --git a/src/yggdrasil/session.go b/src/yggdrasil/session.go index e29cd4f..4f632fa 100644 --- a/src/yggdrasil/session.go +++ b/src/yggdrasil/session.go @@ -7,7 +7,6 @@ package yggdrasil import ( "bytes" "encoding/hex" - "sync" "time" "github.com/yggdrasil-network/yggdrasil-go/src/address" @@ -115,14 +114,6 @@ type sessions struct { byTheirPerm map[crypto.BoxPubKey]*crypto.Handle addrToPerm map[address.Address]*crypto.BoxPubKey subnetToPerm map[address.Subnet]*crypto.BoxPubKey - // Options from the session firewall - sessionFirewallMutex sync.RWMutex - sessionFirewallEnabled bool - sessionFirewallAllowsDirect bool - sessionFirewallAllowsRemote bool - sessionFirewallAlwaysAllowsOutbound bool - sessionFirewallWhitelist []string - sessionFirewallBlacklist []string } // Initializes the session struct. @@ -155,51 +146,28 @@ func (ss *sessions) init(core *Core) { ss.lastCleanup = time.Now() } -// Enable or disable the session firewall -func (ss *sessions) setSessionFirewallState(enabled bool) { - ss.sessionFirewallMutex.Lock() - defer ss.sessionFirewallMutex.Unlock() - ss.sessionFirewallEnabled = enabled -} +// Determines whether the session firewall is enabled. +func (ss *sessions) isSessionFirewallEnabled() bool { + ss.core.configMutex.RLock() + defer ss.core.configMutex.RUnlock() -// Set the session firewall defaults (first parameter is whether to allow -// sessions from direct peers, second is whether to allow from remote nodes). -func (ss *sessions) setSessionFirewallDefaults(allowsDirect bool, allowsRemote bool, alwaysAllowsOutbound bool) { - ss.sessionFirewallMutex.Lock() - defer ss.sessionFirewallMutex.Unlock() - ss.sessionFirewallAllowsDirect = allowsDirect - ss.sessionFirewallAllowsRemote = allowsRemote - ss.sessionFirewallAlwaysAllowsOutbound = alwaysAllowsOutbound -} - -// Set the session firewall whitelist - nodes always allowed to open sessions. -func (ss *sessions) setSessionFirewallWhitelist(whitelist []string) { - ss.sessionFirewallMutex.Lock() - defer ss.sessionFirewallMutex.Unlock() - ss.sessionFirewallWhitelist = whitelist -} - -// Set the session firewall blacklist - nodes never allowed to open sessions. -func (ss *sessions) setSessionFirewallBlacklist(blacklist []string) { - ss.sessionFirewallMutex.Lock() - defer ss.sessionFirewallMutex.Unlock() - ss.sessionFirewallBlacklist = blacklist + return ss.core.config.SessionFirewall.Enable } // Determines whether the session with a given publickey is allowed based on // session firewall rules. func (ss *sessions) isSessionAllowed(pubkey *crypto.BoxPubKey, initiator bool) bool { - ss.sessionFirewallMutex.RLock() - defer ss.sessionFirewallMutex.RUnlock() + ss.core.configMutex.RLock() + defer ss.core.configMutex.RUnlock() // Allow by default if the session firewall is disabled - if !ss.sessionFirewallEnabled { + if !ss.isSessionFirewallEnabled() { return true } // Prepare for checking whitelist/blacklist var box crypto.BoxPubKey // Reject blacklisted nodes - for _, b := range ss.sessionFirewallBlacklist { + for _, b := range ss.core.config.SessionFirewall.BlacklistEncryptionPublicKeys { key, err := hex.DecodeString(b) if err == nil { copy(box[:crypto.BoxPubKeyLen], key) @@ -209,7 +177,7 @@ func (ss *sessions) isSessionAllowed(pubkey *crypto.BoxPubKey, initiator bool) b } } // Allow whitelisted nodes - for _, b := range ss.sessionFirewallWhitelist { + for _, b := range ss.core.config.SessionFirewall.WhitelistEncryptionPublicKeys { key, err := hex.DecodeString(b) if err == nil { copy(box[:crypto.BoxPubKeyLen], key) @@ -219,7 +187,7 @@ func (ss *sessions) isSessionAllowed(pubkey *crypto.BoxPubKey, initiator bool) b } } // Allow outbound sessions if appropriate - if ss.sessionFirewallAlwaysAllowsOutbound { + if ss.core.config.SessionFirewall.AlwaysAllowOutbound { if initiator { return true } @@ -233,11 +201,11 @@ func (ss *sessions) isSessionAllowed(pubkey *crypto.BoxPubKey, initiator bool) b } } // Allow direct peers if appropriate - if ss.sessionFirewallAllowsDirect && isDirectPeer { + if ss.core.config.SessionFirewall.AllowFromDirect && isDirectPeer { return true } // Allow remote nodes if appropriate - if ss.sessionFirewallAllowsRemote && !isDirectPeer { + if ss.core.config.SessionFirewall.AllowFromRemote && !isDirectPeer { return true } // Finally, default-deny if not matching any of the above rules @@ -474,14 +442,11 @@ func (ss *sessions) handlePing(ping *sessionPing) { // Get the corresponding session (or create a new session) sinfo, isIn := ss.getByTheirPerm(&ping.SendPermPub) // Check the session firewall - ss.sessionFirewallMutex.RLock() - if !isIn && ss.sessionFirewallEnabled { + if !isIn && ss.isSessionFirewallEnabled() { if !ss.isSessionAllowed(&ping.SendPermPub, false) { - ss.sessionFirewallMutex.RUnlock() return } } - ss.sessionFirewallMutex.RUnlock() if !isIn || sinfo.timedout() { if isIn { sinfo.close() From c839012580cdf84a46121dc453a73811dd86d688 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 17 Jan 2019 23:06:59 +0000 Subject: [PATCH 065/109] Fix source address selection --- src/yggdrasil/tcp.go | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index ca9ea09..78e39ef 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -245,15 +245,27 @@ func (iface *tcpInterface) call(saddr string, socksaddr *string, sintf string) { if err != nil { continue } - if (src.To4() != nil) == (dst.IP.To4() != nil) { - if addrindex == len(addrs)-1 || src.IsGlobalUnicast() { - dialer.LocalAddr = &net.TCPAddr{ - IP: src, - Port: 0, - Zone: sintf, - } - break + if src.Equal(dst.IP) { + continue + } + if !src.IsGlobalUnicast() && !src.IsLinkLocalUnicast() { + continue + } + bothglobal := src.IsGlobalUnicast() == dst.IP.IsGlobalUnicast() + bothlinklocal := src.IsLinkLocalUnicast() == dst.IP.IsLinkLocalUnicast() + if !bothglobal && !bothlinklocal { + continue + } + if (src.To4() != nil) != (dst.IP.To4() != nil) { + continue + } + if bothglobal || bothlinklocal || addrindex == len(addrs)-1 { + dialer.LocalAddr = &net.TCPAddr{ + IP: src, + Port: 0, + Zone: sintf, } + break } } if dialer.LocalAddr == nil { From 6fe3b01e905b6f8ce482faa83f2e90315fcf72c0 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 19 Jan 2019 00:14:10 +0000 Subject: [PATCH 066/109] Rename awdl.go to link.go, add stream.go, update tcp.go --- src/yggdrasil/core.go | 12 +-- src/yggdrasil/{awdl.go => link.go} | 55 ++++++------- src/yggdrasil/stream.go | 111 +++++++++++++++++++++++++ src/yggdrasil/tcp.go | 128 ++++++++--------------------- 4 files changed, 177 insertions(+), 129 deletions(-) rename src/yggdrasil/{awdl.go => link.go} (72%) create mode 100644 src/yggdrasil/stream.go diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index ed07581..fa5d02a 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -44,7 +44,7 @@ type Core struct { searches searches multicast multicast tcp tcpInterface - awdl awdl + link link log *log.Logger } @@ -198,10 +198,12 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error { return err } - if err := c.awdl.init(c); err != nil { - c.log.Println("Failed to start AWDL interface") - return err - } + /* + if err := c.awdl.init(c); err != nil { + c.log.Println("Failed to start AWDL interface") + return err + } + */ if nc.SwitchOptions.MaxTotalQueueSize >= SwitchQueueTotalMinSize { c.switchTable.queueTotalMaxSize = nc.SwitchOptions.MaxTotalQueueSize diff --git a/src/yggdrasil/awdl.go b/src/yggdrasil/link.go similarity index 72% rename from src/yggdrasil/awdl.go rename to src/yggdrasil/link.go index 633d5f9..1bbd0c3 100644 --- a/src/yggdrasil/awdl.go +++ b/src/yggdrasil/link.go @@ -11,34 +11,35 @@ import ( "github.com/yggdrasil-network/yggdrasil-go/src/util" ) -type awdl struct { +type link struct { core *Core mutex sync.RWMutex // protects interfaces below - interfaces map[string]*awdlInterface + interfaces map[string]*linkInterface } -type awdlInterface struct { - awdl *awdl - fromAWDL chan []byte - toAWDL chan []byte +type linkInterface struct { + link *link + fromlink chan []byte + tolink chan []byte shutdown chan bool peer *peer + stream stream } -func (l *awdl) init(c *Core) error { +func (l *link) init(c *Core) error { l.core = c l.mutex.Lock() - l.interfaces = make(map[string]*awdlInterface) + l.interfaces = make(map[string]*linkInterface) l.mutex.Unlock() return nil } -func (l *awdl) create(fromAWDL chan []byte, toAWDL chan []byte /*boxPubKey *crypto.BoxPubKey, sigPubKey *crypto.SigPubKey*/, name string) (*awdlInterface, error) { - intf := awdlInterface{ - awdl: l, - fromAWDL: fromAWDL, - toAWDL: toAWDL, +func (l *link) create(fromlink chan []byte, tolink chan []byte /*boxPubKey *crypto.BoxPubKey, sigPubKey *crypto.SigPubKey*/, name string) (*linkInterface, error) { + intf := linkInterface{ + link: l, + fromlink: fromlink, + tolink: tolink, shutdown: make(chan bool), } l.mutex.Lock() @@ -50,35 +51,29 @@ func (l *awdl) create(fromAWDL chan []byte, toAWDL chan []byte /*boxPubKey *cryp meta.sig = l.core.sigPub meta.link = *myLinkPub metaBytes := meta.encode() - l.core.log.Println("toAWDL <- metaBytes") - toAWDL <- metaBytes - l.core.log.Println("metaBytes = <-fromAWDL") - metaBytes = <-fromAWDL - l.core.log.Println("version_metadata{}") + tolink <- metaBytes + metaBytes = <-fromlink meta = version_metadata{} if !meta.decode(metaBytes) || !meta.check() { return nil, errors.New("Metadata decode failure") } - l.core.log.Println("version_getBaseMetadata{}") base := version_getBaseMetadata() if meta.ver > base.ver || meta.ver == base.ver && meta.minorVer > base.minorVer { return nil, errors.New("Failed to connect to node: " + name + " version: " + fmt.Sprintf("%d.%d", meta.ver, meta.minorVer)) } - l.core.log.Println("crypto.GetSharedKey") shared := crypto.GetSharedKey(myLinkPriv, &meta.link) //shared := crypto.GetSharedKey(&l.core.boxPriv, boxPubKey) - l.core.log.Println("l.core.peers.newPeer") intf.peer = l.core.peers.newPeer(&meta.box, &meta.sig, shared, name) if intf.peer != nil { intf.peer.linkOut = make(chan []byte, 1) // protocol traffic intf.peer.out = func(msg []byte) { defer func() { recover() }() - intf.toAWDL <- msg + intf.tolink <- msg } // called by peer.sendPacket() l.core.switchTable.idleIn <- intf.peer.port // notify switch that we're idle intf.peer.close = func() { - close(intf.fromAWDL) - close(intf.toAWDL) + close(intf.fromlink) + close(intf.tolink) } go intf.handler() go intf.peer.linkLoop() @@ -88,7 +83,7 @@ func (l *awdl) create(fromAWDL chan []byte, toAWDL chan []byte /*boxPubKey *cryp return nil, errors.New("l.core.peers.newPeer failed") } -func (l *awdl) getInterface(identity string) *awdlInterface { +func (l *link) getInterface(identity string) *linkInterface { l.mutex.RLock() defer l.mutex.RUnlock() if intf, ok := l.interfaces[identity]; ok { @@ -97,7 +92,7 @@ func (l *awdl) getInterface(identity string) *awdlInterface { return nil } -func (l *awdl) shutdown(identity string) error { +func (l *link) shutdown(identity string) error { if intf, ok := l.interfaces[identity]; ok { intf.shutdown <- true l.core.peers.removePeer(intf.peer.port) @@ -110,9 +105,9 @@ func (l *awdl) shutdown(identity string) error { } } -func (ai *awdlInterface) handler() { +func (ai *linkInterface) handler() { send := func(msg []byte) { - ai.toAWDL <- msg + ai.tolink <- msg atomic.AddUint64(&ai.peer.bytesSent, uint64(len(msg))) util.PutBytes(msg) } @@ -138,9 +133,9 @@ func (ai *awdlInterface) handler() { case p := <-ai.peer.linkOut: send(p) continue - case r := <-ai.fromAWDL: + case r := <-ai.fromlink: ai.peer.handlePacket(r) - ai.awdl.core.switchTable.idleIn <- ai.peer.port + ai.link.core.switchTable.idleIn <- ai.peer.port case <-ai.shutdown: return } diff --git a/src/yggdrasil/stream.go b/src/yggdrasil/stream.go new file mode 100644 index 0000000..5d67ace --- /dev/null +++ b/src/yggdrasil/stream.go @@ -0,0 +1,111 @@ +package yggdrasil + +import ( + "errors" + "fmt" + + "github.com/yggdrasil-network/yggdrasil-go/src/util" +) + +type stream struct { + buffer []byte + cursor int +} + +const streamMsgSize = 2048 + 65535 + +var streamMsg = [...]byte{0xde, 0xad, 0xb1, 0x75} // "dead bits" + +func (s *stream) init() { + s.buffer = make([]byte, 2*streamMsgSize) + s.cursor = 0 +} + +// This reads from the channel into a []byte buffer for incoming messages. It +// copies completed messages out of the cache into a new slice, and passes them +// to the peer struct via the provided `in func([]byte)` argument. Then it +// shifts the incomplete fragments of data forward so future reads won't +// overwrite it. +func (s *stream) write(bs []byte, in func([]byte)) error { + frag := s.buffer[:0] + if n := len(bs); n > 0 { + frag = append(frag, bs[:n]...) + msg, ok, err2 := stream_chopMsg(&frag) + if err2 != nil { + return fmt.Errorf("message error: %v", err2) + } + if !ok { + // We didn't get the whole message yet + return nil + } + newMsg := append(util.GetBytes(), msg...) + in(newMsg) + util.Yield() + } + return nil +} + +// This takes a pointer to a slice as an argument. It checks if there's a +// complete message and, if so, slices out those parts and returns the message, +// true, and nil. If there's no error, but also no complete message, it returns +// nil, false, and nil. If there's an error, it returns nil, false, and the +// error, which the reader then handles (currently, by returning from the +// reader, which causes the connection to close). +func stream_chopMsg(bs *[]byte) ([]byte, bool, error) { + // Returns msg, ok, err + if len(*bs) < len(streamMsg) { + return nil, false, nil + } + for idx := range streamMsg { + if (*bs)[idx] != streamMsg[idx] { + return nil, false, errors.New("bad message") + } + } + msgLen, msgLenLen := wire_decode_uint64((*bs)[len(streamMsg):]) + if msgLen > streamMsgSize { + return nil, false, errors.New("oversized message") + } + msgBegin := len(streamMsg) + msgLenLen + msgEnd := msgBegin + int(msgLen) + if msgLenLen == 0 || len(*bs) < msgEnd { + // We don't have the full message + // Need to buffer this and wait for the rest to come in + return nil, false, nil + } + msg := (*bs)[msgBegin:msgEnd] + (*bs) = (*bs)[msgEnd:] + return msg, true, nil +} + +/* +func (s *stream) chopMsg() ([]byte, bool, error) { + // Returns msg, ok, err + if len(s.buffer) < len(streamMsg) { + fmt.Println("*** too short") + return nil, false, nil + } + for idx := range streamMsg { + if s.buffer[idx] != streamMsg[idx] { + fmt.Println("*** bad message") + return nil, false, errors.New("bad message") + } + } + msgLen, msgLenLen := wire_decode_uint64((s.buffer)[len(streamMsg):]) + if msgLen > streamMsgSize { + fmt.Println("*** oversized message") + return nil, false, errors.New("oversized message") + } + msgBegin := len(streamMsg) + msgLenLen + msgEnd := msgBegin + int(msgLen) + if msgLenLen == 0 || len(s.buffer) < msgEnd { + // We don't have the full message + // Need to buffer this and wait for the rest to come in + fmt.Println("*** still waiting") + return nil, false, nil + } + msg := s.buffer[msgBegin:msgEnd] + s.buffer = s.buffer[msgEnd:] + fmt.Println("*** done") + return msg, true, nil +} +*/ diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index 78e39ef..682796e 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -16,9 +16,7 @@ package yggdrasil import ( "context" - "errors" "fmt" - "io" "math/rand" "net" "sync" @@ -32,21 +30,21 @@ import ( "github.com/yggdrasil-network/yggdrasil-go/src/util" ) -const tcp_msgSize = 2048 + 65535 // TODO figure out what makes sense -const default_tcp_timeout = 6 * time.Second -const tcp_ping_interval = (default_tcp_timeout * 2 / 3) +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 { core *Core reconfigure chan chan error serv net.Listener - serv_stop chan bool - tcp_timeout time.Duration - tcp_addr string + stop chan bool + timeout time.Duration + addr string mutex sync.Mutex // Protecting the below calls map[string]struct{} conns map[tcpInfo](chan struct{}) + stream stream } // 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. @@ -86,7 +84,7 @@ func (iface *tcpInterface) connectSOCKS(socksaddr, peeraddr string) { // Initializes the struct. func (iface *tcpInterface) init(core *Core) (err error) { iface.core = core - iface.serv_stop = make(chan bool, 1) + iface.stop = make(chan bool, 1) iface.reconfigure = make(chan chan error, 1) go func() { for { @@ -95,7 +93,7 @@ func (iface *tcpInterface) init(core *Core) (err error) { updated := iface.core.config.Listen != iface.core.configOld.Listen iface.core.configMutex.RUnlock() if updated { - iface.serv_stop <- true + iface.stop <- true iface.serv.Close() e <- iface.listen() } else { @@ -111,19 +109,19 @@ func (iface *tcpInterface) listen() error { var err error iface.core.configMutex.RLock() - iface.tcp_addr = iface.core.config.Listen - iface.tcp_timeout = time.Duration(iface.core.config.ReadTimeout) * time.Millisecond + iface.addr = iface.core.config.Listen + iface.timeout = time.Duration(iface.core.config.ReadTimeout) * time.Millisecond iface.core.configMutex.RUnlock() - if iface.tcp_timeout >= 0 && iface.tcp_timeout < default_tcp_timeout { - iface.tcp_timeout = default_tcp_timeout + if iface.timeout >= 0 && iface.timeout < default_timeout { + iface.timeout = default_timeout } ctx := context.Background() lc := net.ListenConfig{ Control: iface.tcpContext, } - iface.serv, err = lc.Listen(ctx, "tcp", iface.tcp_addr) + iface.serv, err = lc.Listen(ctx, "tcp", iface.addr) if err == nil { iface.mutex.Lock() iface.calls = make(map[string]struct{}) @@ -147,7 +145,7 @@ func (iface *tcpInterface) listener() { return } select { - case <-iface.serv_stop: + case <-iface.stop: iface.core.log.Println("Stopping listener") return default: @@ -194,7 +192,7 @@ func (iface *tcpInterface) call(saddr string, socksaddr *string, sintf string) { iface.mutex.Unlock() defer func() { // Block new calls for a little while, to mitigate livelock scenarios - time.Sleep(default_tcp_timeout) + time.Sleep(default_timeout) time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond) iface.mutex.Lock() delete(iface.calls, callname) @@ -299,8 +297,8 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) { if err != nil { return } - if iface.tcp_timeout > 0 { - sock.SetReadDeadline(time.Now().Add(iface.tcp_timeout)) + if iface.timeout > 0 { + sock.SetReadDeadline(time.Now().Add(iface.timeout)) } _, err = sock.Read(metaBytes) if err != nil { @@ -389,9 +387,9 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) { // This goroutine waits for outgoing packets, link protocol traffic, or sends idle keep-alive traffic send := func(msg []byte) { msgLen := wire_encode_uint64(uint64(len(msg))) - buf := net.Buffers{tcp_msg[:], msgLen, msg} + buf := net.Buffers{streamMsg[:], msgLen, msg} buf.WriteTo(sock) - atomic.AddUint64(&p.bytesSent, uint64(len(tcp_msg)+len(msgLen)+len(msg))) + atomic.AddUint64(&p.bytesSent, uint64(len(streamMsg)+len(msgLen)+len(msg))) util.PutBytes(msg) } timerInterval := tcp_ping_interval @@ -445,7 +443,21 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) { themAddrString := net.IP(themAddr[:]).String() themString := fmt.Sprintf("%s@%s", themAddrString, them) iface.core.log.Printf("Connected: %s, source: %s", themString, us) - err = iface.reader(sock, in) // In this goroutine, because of defers + iface.stream.init() + bs := make([]byte, 2*streamMsgSize) + var n int + for { + if iface.timeout > 0 { + sock.SetReadDeadline(time.Now().Add(iface.timeout)) + } + n, err = sock.Read(bs) + if err != nil { + break + } + if n > 0 { + iface.stream.write(bs[:n], in) + } + } if err == nil { iface.core.log.Printf("Disconnected: %s, source: %s", themString, us) } else { @@ -453,75 +465,3 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) { } return } - -// This reads from the socket into a []byte buffer for incomping messages. -// It copies completed messages out of the cache into a new slice, and passes them to the peer struct via the provided `in func([]byte)` argument. -// Then it shifts the incomplete fragments of data forward so future reads won't overwrite it. -func (iface *tcpInterface) reader(sock net.Conn, in func([]byte)) error { - bs := make([]byte, 2*tcp_msgSize) - frag := bs[:0] - for { - if iface.tcp_timeout > 0 { - sock.SetReadDeadline(time.Now().Add(iface.tcp_timeout)) - } - n, err := sock.Read(bs[len(frag):]) - if n > 0 { - frag = bs[:len(frag)+n] - for { - msg, ok, err2 := tcp_chop_msg(&frag) - if err2 != nil { - return fmt.Errorf("Message error: %v", err2) - } - if !ok { - // We didn't get the whole message yet - break - } - newMsg := append(util.GetBytes(), msg...) - in(newMsg) - util.Yield() - } - frag = append(bs[:0], frag...) - } - if err != nil || n == 0 { - if err != io.EOF { - return err - } - return nil - } - } -} - -//////////////////////////////////////////////////////////////////////////////// - -// These are 4 bytes of padding used to catch if something went horribly wrong with the tcp connection. -var tcp_msg = [...]byte{0xde, 0xad, 0xb1, 0x75} // "dead bits" - -// This takes a pointer to a slice as an argument. -// It checks if there's a complete message and, if so, slices out those parts and returns the message, true, and nil. -// If there's no error, but also no complete message, it returns nil, false, and nil. -// If there's an error, it returns nil, false, and the error, which the reader then handles (currently, by returning from the reader, which causes the connection to close). -func tcp_chop_msg(bs *[]byte) ([]byte, bool, error) { - // Returns msg, ok, err - if len(*bs) < len(tcp_msg) { - return nil, false, nil - } - for idx := range tcp_msg { - if (*bs)[idx] != tcp_msg[idx] { - return nil, false, errors.New("Bad message!") - } - } - msgLen, msgLenLen := wire_decode_uint64((*bs)[len(tcp_msg):]) - if msgLen > tcp_msgSize { - return nil, false, errors.New("Oversized message!") - } - msgBegin := len(tcp_msg) + msgLenLen - msgEnd := msgBegin + int(msgLen) - if msgLenLen == 0 || len(*bs) < msgEnd { - // We don't have the full message - // Need to buffer this and wait for the rest to come in - return nil, false, nil - } - msg := (*bs)[msgBegin:msgEnd] - (*bs) = (*bs)[msgEnd:] - return msg, true, nil -} From c51a3340b14402d496082bcd29fef5ee91a3acbf Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 19 Jan 2019 00:42:53 +0000 Subject: [PATCH 067/109] Update awdl.go to use new link stuff (untested) --- src/yggdrasil/awdl.go | 86 +++++++++++++++++++++++++++++++++++++++++++ src/yggdrasil/core.go | 13 +++---- src/yggdrasil/link.go | 4 +- 3 files changed, 94 insertions(+), 9 deletions(-) create mode 100644 src/yggdrasil/awdl.go diff --git a/src/yggdrasil/awdl.go b/src/yggdrasil/awdl.go new file mode 100644 index 0000000..43db934 --- /dev/null +++ b/src/yggdrasil/awdl.go @@ -0,0 +1,86 @@ +package yggdrasil + +import ( + "fmt" + "sync" +) + +type awdl struct { + core *Core + mutex sync.RWMutex // protects interfaces below + interfaces map[string]*awdlInterface +} + +type awdlInterface struct { + awdl *awdl + fromAWDL chan []byte + toAWDL chan []byte + shutdown chan bool + peer *peer + link *linkInterface +} + +func (l *awdl) init(c *Core) error { + l.core = c + l.mutex.Lock() + l.interfaces = make(map[string]*awdlInterface) + l.mutex.Unlock() + + return nil +} + +func (l *awdl) create(fromAWDL chan []byte, toAWDL chan []byte, name string) (*awdlInterface, error) { + link, err := l.core.link.create(fromAWDL, toAWDL, name) + if err != nil { + return nil, err + } + intf := awdlInterface{ + awdl: l, + link: link, + fromAWDL: fromAWDL, + toAWDL: toAWDL, + shutdown: make(chan bool), + } + l.mutex.Lock() + l.interfaces[name] = &intf + l.mutex.Unlock() + return &intf, nil +} + +func (l *awdl) getInterface(identity string) *awdlInterface { + l.mutex.RLock() + defer l.mutex.RUnlock() + if intf, ok := l.interfaces[identity]; ok { + return intf + } + return nil +} + +func (l *awdl) shutdown(identity string) error { + if err := l.core.link.shutdown(identity); err != nil { + return err + } + if intf, ok := l.interfaces[identity]; ok { + intf.shutdown <- true + l.mutex.Lock() + delete(l.interfaces, identity) + l.mutex.Unlock() + return nil + } + return fmt.Errorf("interface '%s' doesn't exist or already shutdown", identity) +} + +func (ai *awdlInterface) handler() { + for { + select { + case <-ai.shutdown: + return + case <-ai.link.shutdown: + return + case in := <-ai.fromAWDL: + ai.link.fromlink <- in + case out := <-ai.link.tolink: + ai.toAWDL <- out + } + } +} diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index fa5d02a..1f26b69 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -44,7 +44,8 @@ type Core struct { searches searches multicast multicast tcp tcpInterface - link link + link link // TODO: not sure if this wants to be here? + awdl awdl log *log.Logger } @@ -198,12 +199,10 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error { return err } - /* - if err := c.awdl.init(c); err != nil { - c.log.Println("Failed to start AWDL interface") - return err - } - */ + if err := c.awdl.init(c); err != nil { + c.log.Println("Failed to start AWDL interface") + return err + } if nc.SwitchOptions.MaxTotalQueueSize >= SwitchQueueTotalMinSize { c.switchTable.queueTotalMaxSize = nc.SwitchOptions.MaxTotalQueueSize diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index 1bbd0c3..386fef8 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -35,7 +35,7 @@ func (l *link) init(c *Core) error { return nil } -func (l *link) create(fromlink chan []byte, tolink chan []byte /*boxPubKey *crypto.BoxPubKey, sigPubKey *crypto.SigPubKey*/, name string) (*linkInterface, error) { +func (l *link) create(fromlink chan []byte, tolink chan []byte, name string) (*linkInterface, error) { intf := linkInterface{ link: l, fromlink: fromlink, @@ -101,7 +101,7 @@ func (l *link) shutdown(identity string) error { l.mutex.Unlock() return nil } else { - return errors.New(fmt.Sprintf("Interface '%s' doesn't exist or already shutdown", identity)) + return fmt.Errorf("interface '%s' doesn't exist or already shutdown", identity) } } From 41a410f2a1d019fc061efa3b265e201844bae967 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 19 Jan 2019 12:19:24 +0000 Subject: [PATCH 068/109] Initialise awdl.go from link.go, remove deadlock between awdl.create and link.create, other bits and pieces --- src/yggdrasil/awdl.go | 8 ++++- src/yggdrasil/core.go | 4 +-- src/yggdrasil/link.go | 75 ++++++++++++++++++++++++++--------------- src/yggdrasil/stream.go | 35 ------------------- 4 files changed, 57 insertions(+), 65 deletions(-) diff --git a/src/yggdrasil/awdl.go b/src/yggdrasil/awdl.go index 43db934..4c4d83c 100644 --- a/src/yggdrasil/awdl.go +++ b/src/yggdrasil/awdl.go @@ -18,6 +18,7 @@ type awdlInterface struct { shutdown chan bool peer *peer link *linkInterface + stream stream } func (l *awdl) init(c *Core) error { @@ -41,6 +42,8 @@ func (l *awdl) create(fromAWDL chan []byte, toAWDL chan []byte, name string) (*a toAWDL: toAWDL, shutdown: make(chan bool), } + intf.stream.init() + go intf.handler() l.mutex.Lock() l.interfaces[name] = &intf l.mutex.Unlock() @@ -71,6 +74,9 @@ func (l *awdl) shutdown(identity string) error { } func (ai *awdlInterface) handler() { + inPacket := func(packet []byte) { + ai.link.fromlink <- packet + } for { select { case <-ai.shutdown: @@ -78,7 +84,7 @@ func (ai *awdlInterface) handler() { case <-ai.link.shutdown: return case in := <-ai.fromAWDL: - ai.link.fromlink <- in + ai.stream.write(in, inPacket) case out := <-ai.link.tolink: ai.toAWDL <- out } diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index 1f26b69..7090561 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -199,8 +199,8 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error { return err } - if err := c.awdl.init(c); err != nil { - c.log.Println("Failed to start AWDL interface") + if err := c.link.init(c); err != nil { + c.log.Println("Failed to start link interfaces") return err } diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index 386fef8..32b5ea7 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -18,6 +18,7 @@ type link struct { } type linkInterface struct { + name string link *link fromlink chan []byte tolink chan []byte @@ -32,55 +33,75 @@ func (l *link) init(c *Core) error { l.interfaces = make(map[string]*linkInterface) l.mutex.Unlock() + if err := l.core.awdl.init(c); err != nil { + l.core.log.Println("Failed to start AWDL interface") + return err + } + return nil } func (l *link) create(fromlink chan []byte, tolink chan []byte, name string) (*linkInterface, error) { + l.mutex.Lock() + defer l.mutex.Unlock() + if _, ok := l.interfaces[name]; ok { + return nil, errors.New("Interface with this name already exists") + } intf := linkInterface{ + name: name, link: l, fromlink: fromlink, tolink: tolink, shutdown: make(chan bool), } - l.mutex.Lock() - l.interfaces[name] = &intf - l.mutex.Unlock() + l.interfaces[intf.name] = &intf + go intf.start() + return &intf, nil +} + +func (intf *linkInterface) start() { myLinkPub, myLinkPriv := crypto.NewBoxKeys() meta := version_getBaseMetadata() - meta.box = l.core.boxPub - meta.sig = l.core.sigPub + meta.box = intf.link.core.boxPub + meta.sig = intf.link.core.sigPub meta.link = *myLinkPub metaBytes := meta.encode() - tolink <- metaBytes - metaBytes = <-fromlink + //intf.link.core.log.Println("start: intf.tolink <- metaBytes") + intf.tolink <- metaBytes + //intf.link.core.log.Println("finish: intf.tolink <- metaBytes") + //intf.link.core.log.Println("start: metaBytes = <-intf.fromlink") + metaBytes = <-intf.fromlink + //intf.link.core.log.Println("finish: metaBytes = <-intf.fromlink") meta = version_metadata{} if !meta.decode(metaBytes) || !meta.check() { - return nil, errors.New("Metadata decode failure") + intf.link.core.log.Println("Metadata decode failure") + return } base := version_getBaseMetadata() if meta.ver > base.ver || meta.ver == base.ver && meta.minorVer > base.minorVer { - return nil, errors.New("Failed to connect to node: " + name + " version: " + fmt.Sprintf("%d.%d", meta.ver, meta.minorVer)) + intf.link.core.log.Println("Failed to connect to node: " + intf.name + " version: " + fmt.Sprintf("%d.%d", meta.ver, meta.minorVer)) + return } shared := crypto.GetSharedKey(myLinkPriv, &meta.link) - //shared := crypto.GetSharedKey(&l.core.boxPriv, boxPubKey) - intf.peer = l.core.peers.newPeer(&meta.box, &meta.sig, shared, name) - if intf.peer != nil { - intf.peer.linkOut = make(chan []byte, 1) // protocol traffic - intf.peer.out = func(msg []byte) { - defer func() { recover() }() - intf.tolink <- msg - } // called by peer.sendPacket() - l.core.switchTable.idleIn <- intf.peer.port // notify switch that we're idle - intf.peer.close = func() { - close(intf.fromlink) - close(intf.tolink) - } - go intf.handler() - go intf.peer.linkLoop() - return &intf, nil + intf.peer = intf.link.core.peers.newPeer(&meta.box, &meta.sig, shared, intf.name) + if intf.peer == nil { + intf.link.mutex.Lock() + delete(intf.link.interfaces, intf.name) + intf.link.mutex.Unlock() + return } - delete(l.interfaces, name) - return nil, errors.New("l.core.peers.newPeer failed") + intf.peer.linkOut = make(chan []byte, 1) // protocol traffic + intf.peer.out = func(msg []byte) { + defer func() { recover() }() + intf.tolink <- msg + } // called by peer.sendPacket() + intf.link.core.switchTable.idleIn <- intf.peer.port // notify switch that we're idle + intf.peer.close = func() { + close(intf.fromlink) + close(intf.tolink) + } + go intf.handler() + go intf.peer.linkLoop() } func (l *link) getInterface(identity string) *linkInterface { diff --git a/src/yggdrasil/stream.go b/src/yggdrasil/stream.go index 5d67ace..a4e84c9 100644 --- a/src/yggdrasil/stream.go +++ b/src/yggdrasil/stream.go @@ -9,7 +9,6 @@ import ( type stream struct { buffer []byte - cursor int } const streamMsgSize = 2048 + 65535 @@ -18,7 +17,6 @@ var streamMsg = [...]byte{0xde, 0xad, 0xb1, 0x75} // "dead bits" func (s *stream) init() { s.buffer = make([]byte, 2*streamMsgSize) - s.cursor = 0 } // This reads from the channel into a []byte buffer for incoming messages. It @@ -76,36 +74,3 @@ func stream_chopMsg(bs *[]byte) ([]byte, bool, error) { (*bs) = (*bs)[msgEnd:] return msg, true, nil } - -/* -func (s *stream) chopMsg() ([]byte, bool, error) { - // Returns msg, ok, err - if len(s.buffer) < len(streamMsg) { - fmt.Println("*** too short") - return nil, false, nil - } - for idx := range streamMsg { - if s.buffer[idx] != streamMsg[idx] { - fmt.Println("*** bad message") - return nil, false, errors.New("bad message") - } - } - msgLen, msgLenLen := wire_decode_uint64((s.buffer)[len(streamMsg):]) - if msgLen > streamMsgSize { - fmt.Println("*** oversized message") - return nil, false, errors.New("oversized message") - } - msgBegin := len(streamMsg) + msgLenLen - msgEnd := msgBegin + int(msgLen) - if msgLenLen == 0 || len(s.buffer) < msgEnd { - // We don't have the full message - // Need to buffer this and wait for the rest to come in - fmt.Println("*** still waiting") - return nil, false, nil - } - msg := s.buffer[msgBegin:msgEnd] - s.buffer = s.buffer[msgEnd:] - fmt.Println("*** done") - return msg, true, nil -} -*/ From c8e1be0f73255dddbc5ac5845c0ba2c1edc6666a Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sat, 19 Jan 2019 16:37:45 -0600 Subject: [PATCH 069/109] link/stream refactoring bugfixes and gofmt --- contrib/ansible/genkeys.go | 20 ++++++++++---------- src/yggdrasil/awdl.go | 10 +++++----- src/yggdrasil/stream.go | 21 +++++++++++---------- src/yggdrasil/tcp.go | 7 ++----- 4 files changed, 28 insertions(+), 30 deletions(-) diff --git a/contrib/ansible/genkeys.go b/contrib/ansible/genkeys.go index 22418a0..8139738 100644 --- a/contrib/ansible/genkeys.go +++ b/contrib/ansible/genkeys.go @@ -35,22 +35,22 @@ func main() { } var encryptionKeys []keySet - for i := 0; i < *numHosts + 1; i++ { + for i := 0; i < *numHosts+1; i++ { encryptionKeys = append(encryptionKeys, newBoxKey()) } encryptionKeys = sortKeySetArray(encryptionKeys) - for i := 0; i < *keyTries - *numHosts - 1; i++ { - encryptionKeys[0] = newBoxKey(); + for i := 0; i < *keyTries-*numHosts-1; i++ { + encryptionKeys[0] = newBoxKey() encryptionKeys = bubbleUpTo(encryptionKeys, 0) } var signatureKeys []keySet - for i := 0; i < *numHosts + 1; i++ { + for i := 0; i < *numHosts+1; i++ { signatureKeys = append(signatureKeys, newSigKey()) } signatureKeys = sortKeySetArray(signatureKeys) - for i := 0; i < *keyTries - *numHosts - 1; i++ { - signatureKeys[0] = newSigKey(); + for i := 0; i < *keyTries-*numHosts-1; i++ { + signatureKeys[0] = newSigKey() signatureKeys = bubbleUpTo(signatureKeys, 0) } @@ -112,11 +112,11 @@ func sortKeySetArray(sets []keySet) []keySet { } func bubbleUpTo(sets []keySet, num int) []keySet { - for i := 0; i < len(sets) - num - 1; i++ { - if isBetter(sets[i + 1].id, sets[i].id) { + for i := 0; i < len(sets)-num-1; i++ { + if isBetter(sets[i+1].id, sets[i].id) { var tmp = sets[i] - sets[i] = sets[i + 1] - sets[i + 1] = tmp + sets[i] = sets[i+1] + sets[i+1] = tmp } } return sets diff --git a/src/yggdrasil/awdl.go b/src/yggdrasil/awdl.go index 4c4d83c..573b6e7 100644 --- a/src/yggdrasil/awdl.go +++ b/src/yggdrasil/awdl.go @@ -42,7 +42,10 @@ func (l *awdl) create(fromAWDL chan []byte, toAWDL chan []byte, name string) (*a toAWDL: toAWDL, shutdown: make(chan bool), } - intf.stream.init() + inPacket := func(packet []byte) { + intf.link.fromlink <- packet + } + intf.stream.init(inPacket) go intf.handler() l.mutex.Lock() l.interfaces[name] = &intf @@ -74,9 +77,6 @@ func (l *awdl) shutdown(identity string) error { } func (ai *awdlInterface) handler() { - inPacket := func(packet []byte) { - ai.link.fromlink <- packet - } for { select { case <-ai.shutdown: @@ -84,7 +84,7 @@ func (ai *awdlInterface) handler() { case <-ai.link.shutdown: return case in := <-ai.fromAWDL: - ai.stream.write(in, inPacket) + ai.stream.handleInput(in) case out := <-ai.link.tolink: ai.toAWDL <- out } diff --git a/src/yggdrasil/stream.go b/src/yggdrasil/stream.go index a4e84c9..43eff3f 100644 --- a/src/yggdrasil/stream.go +++ b/src/yggdrasil/stream.go @@ -8,15 +8,16 @@ import ( ) type stream struct { - buffer []byte + inputBuffer []byte + handlePacket func([]byte) } const streamMsgSize = 2048 + 65535 var streamMsg = [...]byte{0xde, 0xad, 0xb1, 0x75} // "dead bits" -func (s *stream) init() { - s.buffer = make([]byte, 2*streamMsgSize) +func (s *stream) init(in func([]byte)) { + s.handlePacket = in } // This reads from the channel into a []byte buffer for incoming messages. It @@ -24,11 +25,10 @@ func (s *stream) init() { // to the peer struct via the provided `in func([]byte)` argument. Then it // shifts the incomplete fragments of data forward so future reads won't // overwrite it. -func (s *stream) write(bs []byte, in func([]byte)) error { - frag := s.buffer[:0] - if n := len(bs); n > 0 { - frag = append(frag, bs[:n]...) - msg, ok, err2 := stream_chopMsg(&frag) +func (s *stream) handleInput(bs []byte) error { + if len(bs) > 0 { + s.inputBuffer = append(s.inputBuffer, bs...) + msg, ok, err2 := stream_chopMsg(&s.inputBuffer) if err2 != nil { return fmt.Errorf("message error: %v", err2) } @@ -37,8 +37,9 @@ func (s *stream) write(bs []byte, in func([]byte)) error { return nil } newMsg := append(util.GetBytes(), msg...) - in(newMsg) - util.Yield() + s.inputBuffer = append(s.inputBuffer[:0], s.inputBuffer...) + s.handlePacket(newMsg) + util.Yield() // Make sure we give up control to the scheduler } return nil } diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index 682796e..1d4ec99 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -378,9 +378,6 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) { // E.g. over different interfaces p := iface.core.peers.newPeer(&meta.box, &meta.sig, crypto.GetSharedKey(myLinkPriv, &meta.link), sock.RemoteAddr().String()) p.linkOut = make(chan []byte, 1) - in := func(bs []byte) { - p.handlePacket(bs) - } out := make(chan []byte, 1) defer close(out) go func() { @@ -443,7 +440,7 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) { themAddrString := net.IP(themAddr[:]).String() themString := fmt.Sprintf("%s@%s", themAddrString, them) iface.core.log.Printf("Connected: %s, source: %s", themString, us) - iface.stream.init() + iface.stream.init(p.handlePacket) bs := make([]byte, 2*streamMsgSize) var n int for { @@ -455,7 +452,7 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) { break } if n > 0 { - iface.stream.write(bs[:n], in) + iface.stream.handleInput(bs[:n]) } } if err == nil { From cdfb930703e5b1b81ccba962b9110524698cf6fa Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 21 Jan 2019 12:27:29 +0000 Subject: [PATCH 070/109] Update switch flow separation for IPv4 --- src/yggdrasil/session.go | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/src/yggdrasil/session.go b/src/yggdrasil/session.go index 4f632fa..b756ec9 100644 --- a/src/yggdrasil/session.go +++ b/src/yggdrasil/session.go @@ -554,20 +554,33 @@ func (sinfo *sessionInfo) doSend(bs []byte) { } // code isn't multithreaded so appending to this is safe coords := sinfo.coords - // Read IPv6 flowlabel field (20 bits). - // Assumes packet at least contains IPv6 header. - flowkey := uint64(bs[1]&0x0f)<<16 | uint64(bs[2])<<8 | uint64(bs[3]) - // Check if the flowlabel was specified + var flowkey uint64 + // Try to read IPv6 flowlabel field (20 bits) + if bs[0]&0xf0 == 0x60 { + flowkey = uint64(bs[1]&0x0f)<<16 | uint64(bs[2])<<8 | uint64(bs[3]) + } + // Check if the flowlabel was specified. If not then try to use known + // protocols' ports: protokey: proto | sport | dport if flowkey == 0 { - // Does the packet meet the minimum UDP packet size? (others are bigger) - if len(bs) >= 48 { - // Is the protocol TCP, UDP, SCTP? - if bs[6] == 0x06 || bs[6] == 0x11 || bs[6] == 0x84 { - // if flowlabel was unspecified (0), try to use known protocols' ports - // protokey: proto | sport | dport - flowkey = uint64(bs[6])<<32 /* proto */ | - uint64(bs[40])<<24 | uint64(bs[41])<<16 /* sport */ | - uint64(bs[42])<<8 | uint64(bs[43]) /* dport */ + // Is the protocol TCP, UDP, SCTP? + switch bs[0] & 0xf0 { + case 0x40: // IPv4 packet + if len(bs) >= 24 { + if bs[9] == 0x06 || bs[9] == 0x11 || bs[9] == 0x84 { + ihl := bs[0] & 0x0f * 4 // Header length + flowkey = uint64(bs[9])<<32 /* proto */ | + uint64(bs[ihl+0])<<24 | uint64(bs[ihl+1])<<16 /* sport */ | + uint64(bs[ihl+2])<<8 | uint64(bs[ihl+3]) /* dport */ + } + } + case 0x60: // IPv6 packet + // Does the packet meet the minimum UDP packet size? (others are bigger) + if len(bs) >= 48 { + if bs[6] == 0x06 || bs[6] == 0x11 || bs[6] == 0x84 { + flowkey = uint64(bs[6])<<32 /* proto */ | + uint64(bs[40])<<24 | uint64(bs[41])<<16 /* sport */ | + uint64(bs[42])<<8 | uint64(bs[43]) /* dport */ + } } } } From d3f67ad0170cba1e8f4c9d0e668875864fdcb3a5 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 21 Jan 2019 16:22:49 +0000 Subject: [PATCH 071/109] Improve command flow --- src/yggdrasil/session.go | 48 +++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/src/yggdrasil/session.go b/src/yggdrasil/session.go index b756ec9..23b27a3 100644 --- a/src/yggdrasil/session.go +++ b/src/yggdrasil/session.go @@ -554,33 +554,31 @@ func (sinfo *sessionInfo) doSend(bs []byte) { } // code isn't multithreaded so appending to this is safe coords := sinfo.coords + // Work out the flowkey - this is used to determine which switch queue + // traffic will be pushed to in the event of congestion var flowkey uint64 - // Try to read IPv6 flowlabel field (20 bits) - if bs[0]&0xf0 == 0x60 { - flowkey = uint64(bs[1]&0x0f)<<16 | uint64(bs[2])<<8 | uint64(bs[3]) - } - // Check if the flowlabel was specified. If not then try to use known - // protocols' ports: protokey: proto | sport | dport - if flowkey == 0 { - // Is the protocol TCP, UDP, SCTP? - switch bs[0] & 0xf0 { - case 0x40: // IPv4 packet - if len(bs) >= 24 { - if bs[9] == 0x06 || bs[9] == 0x11 || bs[9] == 0x84 { - ihl := bs[0] & 0x0f * 4 // Header length - flowkey = uint64(bs[9])<<32 /* proto */ | - uint64(bs[ihl+0])<<24 | uint64(bs[ihl+1])<<16 /* sport */ | - uint64(bs[ihl+2])<<8 | uint64(bs[ihl+3]) /* dport */ - } + switch bs[0] & 0xf0 { + case 0x40: // IPv4 packet + // Check the packet meets minimum UDP packet length + if len(bs) >= 24 { + if bs[9] == 0x06 || bs[9] == 0x11 || bs[9] == 0x84 { + ihl := bs[0] & 0x0f * 4 // Header length + flowkey = uint64(bs[9])<<32 /* proto */ | + uint64(bs[ihl+0])<<24 | uint64(bs[ihl+1])<<16 /* sport */ | + uint64(bs[ihl+2])<<8 | uint64(bs[ihl+3]) /* dport */ } - case 0x60: // IPv6 packet - // Does the packet meet the minimum UDP packet size? (others are bigger) - if len(bs) >= 48 { - if bs[6] == 0x06 || bs[6] == 0x11 || bs[6] == 0x84 { - flowkey = uint64(bs[6])<<32 /* proto */ | - uint64(bs[40])<<24 | uint64(bs[41])<<16 /* sport */ | - uint64(bs[42])<<8 | uint64(bs[43]) /* dport */ - } + } + case 0x60: // IPv6 packet + // Check if the flowlabel was specified in the packet header + flowkey = uint64(bs[1]&0x0f)<<16 | uint64(bs[2])<<8 | uint64(bs[3]) + // If the flowlabel isn't present, make protokey from proto | sport | dport + // if the packet meets minimum UDP packet length + if flowkey == 0 && len(bs) >= 48 { + // Is the protocol TCP, UDP or SCTP? + if bs[6] == 0x06 || bs[6] == 0x11 || bs[6] == 0x84 { + flowkey = uint64(bs[6])<<32 /* proto */ | + uint64(bs[40])<<24 | uint64(bs[41])<<16 /* sport */ | + uint64(bs[42])<<8 | uint64(bs[43]) /* dport */ } } } From 62d4d62a775ca2ff624f76d2e02ea46c1628dc2d Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 21 Jan 2019 16:24:29 +0000 Subject: [PATCH 072/109] Update comments --- src/yggdrasil/session.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/yggdrasil/session.go b/src/yggdrasil/session.go index 23b27a3..6b7f62d 100644 --- a/src/yggdrasil/session.go +++ b/src/yggdrasil/session.go @@ -557,10 +557,12 @@ func (sinfo *sessionInfo) doSend(bs []byte) { // Work out the flowkey - this is used to determine which switch queue // traffic will be pushed to in the event of congestion var flowkey uint64 + // Get the IP protocol version from the packet switch bs[0] & 0xf0 { case 0x40: // IPv4 packet // Check the packet meets minimum UDP packet length if len(bs) >= 24 { + // Is the protocol TCP, UDP or SCTP? if bs[9] == 0x06 || bs[9] == 0x11 || bs[9] == 0x84 { ihl := bs[0] & 0x0f * 4 // Header length flowkey = uint64(bs[9])<<32 /* proto */ | From 5a4d6481ddad8161061d396e2f4fa3d1dc0f14ff Mon Sep 17 00:00:00 2001 From: Arceliar Date: Mon, 21 Jan 2019 21:27:52 -0600 Subject: [PATCH 073/109] Work in progress, add a linkInterfaceMsgIO interface type and make stream implement it, this will be used by link --- src/yggdrasil/awdl.go | 2 +- src/yggdrasil/link.go | 9 ++++ src/yggdrasil/stream.go | 103 ++++++++++++++++++++++++++++++++++++++-- src/yggdrasil/tcp.go | 2 +- 4 files changed, 110 insertions(+), 6 deletions(-) diff --git a/src/yggdrasil/awdl.go b/src/yggdrasil/awdl.go index 573b6e7..7207b22 100644 --- a/src/yggdrasil/awdl.go +++ b/src/yggdrasil/awdl.go @@ -45,7 +45,7 @@ func (l *awdl) create(fromAWDL chan []byte, toAWDL chan []byte, name string) (*a inPacket := func(packet []byte) { intf.link.fromlink <- packet } - intf.stream.init(inPacket) + intf.stream.init(nil, inPacket) // FIXME nil = ReadWriteCloser go intf.handler() l.mutex.Lock() l.interfaces[name] = &intf diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index 32b5ea7..423a968 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -17,6 +17,15 @@ type link struct { interfaces map[string]*linkInterface } +type linkInterfaceMsgIO interface { + readMsg() ([]byte, error) + writeMsg([]byte) (int, error) + close() error + // These are temporary workarounds to stream semantics + _sendMetaBytes([]byte) error + _recvMetaBytes() ([]byte, error) +} + type linkInterface struct { name string link *link diff --git a/src/yggdrasil/stream.go b/src/yggdrasil/stream.go index 43eff3f..ecfa245 100644 --- a/src/yggdrasil/stream.go +++ b/src/yggdrasil/stream.go @@ -3,21 +3,115 @@ package yggdrasil import ( "errors" "fmt" + "io" "github.com/yggdrasil-network/yggdrasil-go/src/util" ) +// Test that this matches the interface we expect +var _ = linkInterfaceMsgIO(&stream{}) + type stream struct { - inputBuffer []byte + rwc io.ReadWriteCloser + inputBuffer []byte // Incoming packet stream + didFirstSend bool // Used for metadata exchange + didFirstRecv bool // Used for metadata exchange + // TODO remove the rest, it shouldn't matter in the long run handlePacket func([]byte) } +func (s *stream) close() error { + return s.rwc.Close() +} + const streamMsgSize = 2048 + 65535 var streamMsg = [...]byte{0xde, 0xad, 0xb1, 0x75} // "dead bits" -func (s *stream) init(in func([]byte)) { +func (s *stream) init(rwc io.ReadWriteCloser, in func([]byte)) { + // TODO have this also do the metadata handshake and create the peer struct + s.rwc = rwc s.handlePacket = in + + // TODO call something to do the metadata exchange +} + +// writeMsg writes a message with stream padding, and is *not* thread safe. +func (s *stream) writeMsg(bs []byte) (int, error) { + buf := util.GetBytes() + defer util.PutBytes(buf) + buf = append(buf, streamMsg[:]...) + buf = append(buf, wire_encode_uint64(uint64(len(bs)))...) + padLen := len(buf) + buf = append(buf, bs...) + var bn int + for bn < len(buf) { + n, err := s.rwc.Write(buf[bn:]) + bn += n + if err != nil { + l := bn - padLen + if l < 0 { + l = 0 + } + return l, err + } + } + return len(bs), nil +} + +// readMsg reads a message from the stream, accounting for stream padding, and is *not* thread safe. +func (s *stream) readMsg() ([]byte, error) { + for { + buf := s.inputBuffer + msg, ok, err := stream_chopMsg(&buf) + switch { + case err != nil: + // Something in the stream format is corrupt + return nil, fmt.Errorf("message error: %v", err) + case ok: + // Copy the packet into bs, shift the buffer, and return + msg = append(util.GetBytes(), msg...) + s.inputBuffer = append(s.inputBuffer[:0], buf...) + return msg, nil + default: + // Wait for the underlying reader to return enough info for us to proceed + frag := make([]byte, 2*streamMsgSize) + n, err := s.rwc.Read(frag) + if n > 0 { + s.inputBuffer = append(s.inputBuffer, frag[:n]...) + } else if err != nil { + return nil, err + } + } + } +} + +// Writes metadata bytes without stream padding, meant to be temporary +func (s *stream) _sendMetaBytes(metaBytes []byte) error { + var written int + for written < len(metaBytes) { + n, err := s.rwc.Write(metaBytes) + written += n + if err != nil { + return err + } + } + return nil +} + +// Reads metadata bytes without stream padding, meant to be temporary +func (s *stream) _recvMetaBytes() ([]byte, error) { + var meta version_metadata + frag := meta.encode() + metaBytes := make([]byte, 0, len(frag)) + for len(metaBytes) < len(frag) { + n, err := s.rwc.Read(frag) + if err != nil { + return nil, err + } + metaBytes = append(metaBytes, frag[:n]...) + } + return metaBytes, nil } // This reads from the channel into a []byte buffer for incoming messages. It @@ -28,7 +122,8 @@ func (s *stream) init(in func([]byte)) { func (s *stream) handleInput(bs []byte) error { if len(bs) > 0 { s.inputBuffer = append(s.inputBuffer, bs...) - msg, ok, err2 := stream_chopMsg(&s.inputBuffer) + buf := s.inputBuffer + msg, ok, err2 := stream_chopMsg(&buf) if err2 != nil { return fmt.Errorf("message error: %v", err2) } @@ -37,7 +132,7 @@ func (s *stream) handleInput(bs []byte) error { return nil } newMsg := append(util.GetBytes(), msg...) - s.inputBuffer = append(s.inputBuffer[:0], s.inputBuffer...) + s.inputBuffer = append(s.inputBuffer[:0], buf...) s.handlePacket(newMsg) util.Yield() // Make sure we give up control to the scheduler } diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index 1d4ec99..975da46 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -440,7 +440,7 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) { themAddrString := net.IP(themAddr[:]).String() themString := fmt.Sprintf("%s@%s", themAddrString, them) iface.core.log.Printf("Connected: %s, source: %s", themString, us) - iface.stream.init(p.handlePacket) + iface.stream.init(sock, p.handlePacket) bs := make([]byte, 2*streamMsgSize) var n int for { From 137212d8cf1e811c2cf696d37a4ff5104997a9d7 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Mon, 21 Jan 2019 23:08:50 -0600 Subject: [PATCH 074/109] work in progress, establishes TCP connections and gets through metadata handshake using the link code, but doesn't seem to send traffic yet (no switch peers are created) --- src/yggdrasil/awdl.go | 4 +- src/yggdrasil/link.go | 129 +++++++++++++++++++++++++++++++++++----- src/yggdrasil/stream.go | 6 +- src/yggdrasil/tcp.go | 23 +++++-- 4 files changed, 137 insertions(+), 25 deletions(-) diff --git a/src/yggdrasil/awdl.go b/src/yggdrasil/awdl.go index 7207b22..ce8e1d7 100644 --- a/src/yggdrasil/awdl.go +++ b/src/yggdrasil/awdl.go @@ -1,7 +1,7 @@ package yggdrasil import ( - "fmt" + //"fmt" "sync" ) @@ -30,6 +30,7 @@ func (l *awdl) init(c *Core) error { return nil } +/* temporarily disabled while getting the TCP side to work func (l *awdl) create(fromAWDL chan []byte, toAWDL chan []byte, name string) (*awdlInterface, error) { link, err := l.core.link.create(fromAWDL, toAWDL, name) if err != nil { @@ -90,3 +91,4 @@ func (ai *awdlInterface) handler() { } } } +*/ diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index 423a968..b53242a 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -4,11 +4,11 @@ import ( "errors" "fmt" "sync" - "sync/atomic" + //"sync/atomic" "time" "github.com/yggdrasil-network/yggdrasil-go/src/crypto" - "github.com/yggdrasil-network/yggdrasil-go/src/util" + //"github.com/yggdrasil-network/yggdrasil-go/src/util" ) type link struct { @@ -27,13 +27,10 @@ type linkInterfaceMsgIO interface { } type linkInterface struct { - name string - link *link - fromlink chan []byte - tolink chan []byte - shutdown chan bool - peer *peer - stream stream + name string + link *link + peer *peer + msgIO linkInterfaceMsgIO } func (l *link) init(c *Core) error { @@ -50,24 +47,123 @@ func (l *link) init(c *Core) error { return nil } -func (l *link) create(fromlink chan []byte, tolink chan []byte, name string) (*linkInterface, error) { +func (l *link) create(msgIO linkInterfaceMsgIO, name string) (*linkInterface, error) { l.mutex.Lock() defer l.mutex.Unlock() if _, ok := l.interfaces[name]; ok { return nil, errors.New("Interface with this name already exists") } intf := linkInterface{ - name: name, - link: l, - fromlink: fromlink, - tolink: tolink, - shutdown: make(chan bool), + name: name, + link: l, + msgIO: msgIO, } l.interfaces[intf.name] = &intf - go intf.start() + //go intf.start() return &intf, nil } +func (intf *linkInterface) handler() error { + // TODO split some of this into shorter functions, so it's easier to read, and for the FIXME duplicate peer issue mentioned later + myLinkPub, myLinkPriv := crypto.NewBoxKeys() + meta := version_getBaseMetadata() + meta.box = intf.link.core.boxPub + meta.sig = intf.link.core.sigPub + meta.link = *myLinkPub + metaBytes := meta.encode() + // TODO timeouts on send/recv (goroutine for send/recv, channel select w/ timer) + err := intf.msgIO._sendMetaBytes(metaBytes) + if err != nil { + return err + } + metaBytes, err = intf.msgIO._recvMetaBytes() + if err != nil { + return err + } + meta = version_metadata{} + if !meta.decode(metaBytes) || !meta.check() { + return errors.New("failed to decode metadata") + } + base := version_getBaseMetadata() + if meta.ver > base.ver || meta.ver == base.ver && meta.minorVer > base.minorVer { + intf.link.core.log.Println("Failed to connect to node: " + intf.name + " version: " + fmt.Sprintf("%d.%d", meta.ver, meta.minorVer)) + return errors.New("failed to connect: wrong version") + } + // FIXME we *must* stop here and check that we don't already have a connection to this peer. Need to figure out a sane way how to do that. Otherwise you'll have things like duplicate connections (one in each direction) for auto-discovered peers. + shared := crypto.GetSharedKey(myLinkPriv, &meta.link) + intf.peer = intf.link.core.peers.newPeer(&meta.box, &meta.sig, shared, intf.name) + if intf.peer == nil { + return errors.New("failed to create peer") + } + defer func() { + // More cleanup can go here + intf.link.core.peers.removePeer(intf.peer.port) + }() + // Finish setting up the peer struct + out := make(chan []byte, 1) + defer close(out) + intf.peer.out = func(msg []byte) { + defer func() { recover() }() + out <- msg + } + intf.peer.close = func() { intf.msgIO.close() } + go intf.peer.linkLoop() + // Start the writer + go func() { + interval := 4 * time.Second + timer := time.NewTimer(interval) + clearTimer := func() { + if !timer.Stop() { + <-timer.C + } + } + defer clearTimer() + for { + // First try to send any link protocol traffic + select { + case msg := <-intf.peer.linkOut: + intf.msgIO.writeMsg(msg) + continue + default: + } + // No protocol traffic to send, so reset the timer + clearTimer() + timer.Reset(interval) + // Now block until something is ready or the timer triggers keepalive traffic + select { + case <-timer.C: + intf.msgIO.writeMsg(nil) + case msg := <-intf.peer.linkOut: + intf.msgIO.writeMsg(msg) + case msg, ok := <-out: + if !ok { + return + } + intf.msgIO.writeMsg(msg) + if true { + // TODO *don't* do this if we're not reading any traffic + // In such a case, the reader is responsible for resetting it the next time we read something + intf.link.core.switchTable.idleIn <- intf.peer.port + } + } + } + }() + intf.link.core.switchTable.idleIn <- intf.peer.port // notify switch that we're idle + // Run reader loop + for { + msg, err := intf.msgIO.readMsg() + if len(msg) > 0 { + intf.peer.handlePacket(msg) + } + if err != nil { + return err + } + } + //////////////////////////////////////////////////////////////////////////////// + return nil +} + +/* func (intf *linkInterface) start() { myLinkPub, myLinkPriv := crypto.NewBoxKeys() meta := version_getBaseMetadata() @@ -171,3 +267,4 @@ func (ai *linkInterface) handler() { } } } +*/ diff --git a/src/yggdrasil/stream.go b/src/yggdrasil/stream.go index ecfa245..966319a 100644 --- a/src/yggdrasil/stream.go +++ b/src/yggdrasil/stream.go @@ -12,10 +12,8 @@ import ( var _ = linkInterfaceMsgIO(&stream{}) type stream struct { - rwc io.ReadWriteCloser - inputBuffer []byte // Incoming packet stream - didFirstSend bool // Used for metadata exchange - didFirstRecv bool // Used for metadata exchange + rwc io.ReadWriteCloser + inputBuffer []byte // Incoming packet stream // TODO remove the rest, it shouldn't matter in the long run handlePacket func([]byte) } diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index 975da46..d2d20a7 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -44,7 +44,6 @@ type tcpInterface struct { mutex sync.Mutex // Protecting the below calls map[string]struct{} conns map[tcpInfo](chan struct{}) - stream stream } // 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. @@ -281,9 +280,25 @@ func (iface *tcpInterface) call(saddr string, socksaddr *string, sintf string) { }() } +func (iface *tcpInterface) handler(sock net.Conn, incoming bool) { + defer sock.Close() + iface.setExtraOptions(sock) + stream := stream{} + stream.init(sock, nil) + name := sock.LocalAddr().String() + sock.RemoteAddr().String() + link, err := iface.core.link.create(&stream, name) + if err != nil { + iface.core.log.Println(err) + panic(err) + } + iface.core.log.Println("DEBUG: starting handler") + link.handler() + iface.core.log.Println("DEBUG: stopped handler") +} + // This exchanges/checks connection metadata, sets up the peer struct, sets up the writer goroutine, and then runs the reader within the current goroutine. // It defers a bunch of cleanup stuff to tear down all of these things when the reader exists (e.g. due to a closed connection or a timeout). -func (iface *tcpInterface) handler(sock net.Conn, incoming bool) { +func (iface *tcpInterface) handler_old(sock net.Conn, incoming bool) { defer sock.Close() iface.setExtraOptions(sock) // Get our keys @@ -440,7 +455,7 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) { themAddrString := net.IP(themAddr[:]).String() themString := fmt.Sprintf("%s@%s", themAddrString, them) iface.core.log.Printf("Connected: %s, source: %s", themString, us) - iface.stream.init(sock, p.handlePacket) + //iface.stream.init(sock, p.handlePacket) bs := make([]byte, 2*streamMsgSize) var n int for { @@ -452,7 +467,7 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) { break } if n > 0 { - iface.stream.handleInput(bs[:n]) + //iface.stream.handleInput(bs[:n]) } } if err == nil { From f95663e9230d906d30de5b53a5f80cceb150b426 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Tue, 22 Jan 2019 18:24:15 -0600 Subject: [PATCH 075/109] actually finish initializing peers --- src/yggdrasil/link.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index b53242a..ddd7db9 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -106,10 +106,12 @@ func (intf *linkInterface) handler() error { defer func() { recover() }() out <- msg } + intf.peer.linkOut = make(chan []byte, 1) intf.peer.close = func() { intf.msgIO.close() } go intf.peer.linkLoop() // Start the writer go func() { + // TODO util.PutBytes etc. interval := 4 * time.Second timer := time.NewTimer(interval) clearTimer := func() { From 12c0e019dc6114626fe4ed76f6f17e379a576484 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Tue, 22 Jan 2019 21:16:41 -0600 Subject: [PATCH 076/109] only create one interface, but still opens duplicate connections before it catches this, so more work is needed --- src/yggdrasil/link.go | 61 +++++++++++++++++++++++++++++++------------ src/yggdrasil/tcp.go | 10 ++++--- 2 files changed, 51 insertions(+), 20 deletions(-) diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index ddd7db9..0953e4f 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -8,13 +8,21 @@ import ( "time" "github.com/yggdrasil-network/yggdrasil-go/src/crypto" - //"github.com/yggdrasil-network/yggdrasil-go/src/util" + "github.com/yggdrasil-network/yggdrasil-go/src/util" ) type link struct { core *Core mutex sync.RWMutex // protects interfaces below - interfaces map[string]*linkInterface + interfaces map[linkInfo]*linkInterface +} + +type linkInfo struct { + box crypto.BoxPubKey // Their encryption key + sig crypto.SigPubKey // Their signing key + linkType string // Type of link, e.g. TCP, AWDL + local string // Local name or address + remote string // Remote name or address } type linkInterfaceMsgIO interface { @@ -27,16 +35,18 @@ type linkInterfaceMsgIO interface { } type linkInterface struct { - name string - link *link - peer *peer - msgIO linkInterfaceMsgIO + name string + link *link + peer *peer + msgIO linkInterfaceMsgIO + info linkInfo + closed chan struct{} } func (l *link) init(c *Core) error { l.core = c l.mutex.Lock() - l.interfaces = make(map[string]*linkInterface) + l.interfaces = make(map[linkInfo]*linkInterface) l.mutex.Unlock() if err := l.core.awdl.init(c); err != nil { @@ -47,18 +57,19 @@ func (l *link) init(c *Core) error { return nil } -func (l *link) create(msgIO linkInterfaceMsgIO, name string) (*linkInterface, error) { - l.mutex.Lock() - defer l.mutex.Unlock() - if _, ok := l.interfaces[name]; ok { - return nil, errors.New("Interface with this name already exists") - } +func (l *link) create(msgIO linkInterfaceMsgIO, name, linkType, local, remote string) (*linkInterface, error) { + // Technically anything unique would work for names, but lets pick something human readable, just for debugging intf := linkInterface{ name: name, link: l, msgIO: msgIO, + info: linkInfo{ + linkType: linkType, + local: local, + remote: remote, + }, } - l.interfaces[intf.name] = &intf + //l.interfaces[intf.name] = &intf //go intf.start() return &intf, nil } @@ -89,7 +100,25 @@ func (intf *linkInterface) handler() error { intf.link.core.log.Println("Failed to connect to node: " + intf.name + " version: " + fmt.Sprintf("%d.%d", meta.ver, meta.minorVer)) return errors.New("failed to connect: wrong version") } - // FIXME we *must* stop here and check that we don't already have a connection to this peer. Need to figure out a sane way how to do that. Otherwise you'll have things like duplicate connections (one in each direction) for auto-discovered peers. + // Check if we already have a link to this node + intf.info.box = meta.box + intf.info.sig = meta.sig + intf.link.mutex.Lock() + if oldIntf, isIn := intf.link.interfaces[intf.info]; isIn { + intf.link.mutex.Unlock() + // FIXME we should really return an error and let the caller block instead + // That lets them do things like close connections before blocking + intf.link.core.log.Println("DEBUG: found existing interface for", intf.name) + <-oldIntf.closed + return nil + } else { + intf.closed = make(chan struct{}) + intf.link.interfaces[intf.info] = intf + defer close(intf.closed) + intf.link.core.log.Println("DEBUG: registered interface for", intf.name) + } + 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) if intf.peer == nil { @@ -111,7 +140,6 @@ func (intf *linkInterface) handler() error { go intf.peer.linkLoop() // Start the writer go func() { - // TODO util.PutBytes etc. interval := 4 * time.Second timer := time.NewTimer(interval) clearTimer := func() { @@ -142,6 +170,7 @@ func (intf *linkInterface) handler() error { return } intf.msgIO.writeMsg(msg) + util.PutBytes(msg) if true { // TODO *don't* do this if we're not reading any traffic // In such a case, the reader is responsible for resetting it the next time we read something diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index d2d20a7..b2efbbe 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -285,15 +285,17 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) { iface.setExtraOptions(sock) stream := stream{} stream.init(sock, nil) - name := sock.LocalAddr().String() + sock.RemoteAddr().String() - link, err := iface.core.link.create(&stream, name) + local, _, _ := net.SplitHostPort(sock.LocalAddr().String()) + remote, _, _ := net.SplitHostPort(sock.RemoteAddr().String()) + name := "tcp://" + sock.RemoteAddr().String() + link, err := iface.core.link.create(&stream, name, "tcp", local, remote) if err != nil { iface.core.log.Println(err) panic(err) } - iface.core.log.Println("DEBUG: starting handler") + iface.core.log.Println("DEBUG: starting handler for", name) link.handler() - iface.core.log.Println("DEBUG: stopped handler") + iface.core.log.Println("DEBUG: stopped handler for", name) } // This exchanges/checks connection metadata, sets up the peer struct, sets up the writer goroutine, and then runs the reader within the current goroutine. From eb8951081d167059457e9bfaa57b7cfdc63e6a09 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Tue, 22 Jan 2019 21:23:57 -0600 Subject: [PATCH 077/109] fix duplicate connection bug, I think this is also in develop --- src/yggdrasil/tcp.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index b2efbbe..09ed7d0 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -183,7 +183,7 @@ func (iface *tcpInterface) call(saddr string, socksaddr *string, sintf string) { if sintf != "" { callname = fmt.Sprintf("%s/%s", saddr, sintf) } - if iface.isAlreadyCalling(saddr) { + if iface.isAlreadyCalling(callname) { return } iface.mutex.Lock() From f936151f2dc0de0caed3950c3c3d7f43959db21c Mon Sep 17 00:00:00 2001 From: Arceliar Date: Tue, 22 Jan 2019 21:48:43 -0600 Subject: [PATCH 078/109] correctly clean up various things --- src/yggdrasil/link.go | 12 ++++++++++-- src/yggdrasil/tcp.go | 4 ++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index 0953e4f..660c854 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -114,7 +114,12 @@ func (intf *linkInterface) handler() error { } else { intf.closed = make(chan struct{}) intf.link.interfaces[intf.info] = intf - defer close(intf.closed) + defer func() { + intf.link.mutex.Lock() + delete(intf.link.interfaces, intf.info) + intf.link.mutex.Unlock() + close(intf.closed) + }() intf.link.core.log.Println("DEBUG: registered interface for", intf.name) } intf.link.mutex.Unlock() @@ -144,7 +149,10 @@ func (intf *linkInterface) handler() error { timer := time.NewTimer(interval) clearTimer := func() { if !timer.Stop() { - <-timer.C + select { + case <-timer.C: + default: + } } } defer clearTimer() diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index 09ed7d0..e1f490e 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -294,8 +294,8 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) { panic(err) } iface.core.log.Println("DEBUG: starting handler for", name) - link.handler() - iface.core.log.Println("DEBUG: stopped handler for", name) + err = link.handler() + iface.core.log.Println("DEBUG: stopped handler for", name, err) } // This exchanges/checks connection metadata, sets up the peer struct, sets up the writer goroutine, and then runs the reader within the current goroutine. From 7b2460662d6c4dc4ebf12940e29bff43b8ef4106 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Tue, 22 Jan 2019 21:53:39 -0600 Subject: [PATCH 079/109] close the connection before blocking a duplicate link --- src/yggdrasil/link.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index 660c854..1899582 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -107,8 +107,9 @@ func (intf *linkInterface) handler() error { if oldIntf, isIn := intf.link.interfaces[intf.info]; isIn { intf.link.mutex.Unlock() // FIXME we should really return an error and let the caller block instead - // That lets them do things like close connections before blocking + // That lets them do things like close connections on its own, avoid printing a connection message in the first place, etc. intf.link.core.log.Println("DEBUG: found existing interface for", intf.name) + intf.msgIO.close() <-oldIntf.closed return nil } else { From 9c6cf5068424ede379dd53880648a3b002333fb1 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 23 Jan 2019 15:08:19 +0000 Subject: [PATCH 080/109] Adapt AWDL to link --- src/yggdrasil/awdl.go | 79 ++++++++++++++++++------------------- src/yggdrasil/link.go | 6 +++ src/yggdrasil/mobile_ios.go | 20 +++------- 3 files changed, 51 insertions(+), 54 deletions(-) diff --git a/src/yggdrasil/awdl.go b/src/yggdrasil/awdl.go index ce8e1d7..8df98cc 100644 --- a/src/yggdrasil/awdl.go +++ b/src/yggdrasil/awdl.go @@ -1,7 +1,7 @@ package yggdrasil import ( - //"fmt" + "errors" "sync" ) @@ -12,13 +12,31 @@ type awdl struct { } type awdlInterface struct { - awdl *awdl + link *linkInterface + rwc awdlReadWriteCloser + peer *peer + stream stream +} + +type awdlReadWriteCloser struct { fromAWDL chan []byte toAWDL chan []byte - shutdown chan bool - peer *peer - link *linkInterface - stream stream +} + +func (c awdlReadWriteCloser) Read(p []byte) (n int, err error) { + p = <-c.fromAWDL + return len(p), nil +} + +func (c awdlReadWriteCloser) Write(p []byte) (n int, err error) { + c.toAWDL <- p + return len(p), nil +} + +func (c awdlReadWriteCloser) Close() error { + close(c.fromAWDL) + close(c.toAWDL) + return nil } func (l *awdl) init(c *Core) error { @@ -30,27 +48,26 @@ func (l *awdl) init(c *Core) error { return nil } -/* temporarily disabled while getting the TCP side to work -func (l *awdl) create(fromAWDL chan []byte, toAWDL chan []byte, name string) (*awdlInterface, error) { - link, err := l.core.link.create(fromAWDL, toAWDL, name) +func (l *awdl) create(fromAWDL chan []byte, toAWDL chan []byte, name, local, remote string) (*awdlInterface, error) { + rwc := awdlReadWriteCloser{ + fromAWDL: fromAWDL, + toAWDL: toAWDL, + } + s := stream{} + s.init(rwc, nil) + link, err := l.core.link.create(&s, name, "awdl", local, remote) if err != nil { return nil, err } intf := awdlInterface{ - awdl: l, - link: link, - fromAWDL: fromAWDL, - toAWDL: toAWDL, - shutdown: make(chan bool), + link: link, + rwc: rwc, } - inPacket := func(packet []byte) { - intf.link.fromlink <- packet - } - intf.stream.init(nil, inPacket) // FIXME nil = ReadWriteCloser - go intf.handler() + intf.stream.init(intf.rwc, nil) l.mutex.Lock() l.interfaces[name] = &intf l.mutex.Unlock() + go link.handler() return &intf, nil } @@ -64,31 +81,13 @@ func (l *awdl) getInterface(identity string) *awdlInterface { } func (l *awdl) shutdown(identity string) error { - if err := l.core.link.shutdown(identity); err != nil { - return err - } if intf, ok := l.interfaces[identity]; ok { - intf.shutdown <- true + close(intf.link.closed) + intf.rwc.Close() l.mutex.Lock() delete(l.interfaces, identity) l.mutex.Unlock() return nil } - return fmt.Errorf("interface '%s' doesn't exist or already shutdown", identity) + return errors.New("Interface not found or already closed") } - -func (ai *awdlInterface) handler() { - for { - select { - case <-ai.shutdown: - return - case <-ai.link.shutdown: - return - case in := <-ai.fromAWDL: - ai.stream.handleInput(in) - case out := <-ai.link.tolink: - ai.toAWDL <- out - } - } -} -*/ diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index 1899582..c056e6b 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -87,10 +87,12 @@ func (intf *linkInterface) handler() error { if err != nil { return err } + intf.link.core.log.Println("Sent my metadata") metaBytes, err = intf.msgIO._recvMetaBytes() if err != nil { return err } + intf.link.core.log.Println("Received their metadata") meta = version_metadata{} if !meta.decode(metaBytes) || !meta.check() { return errors.New("failed to decode metadata") @@ -100,6 +102,7 @@ func (intf *linkInterface) handler() error { intf.link.core.log.Println("Failed to connect to node: " + intf.name + " version: " + fmt.Sprintf("%d.%d", meta.ver, meta.minorVer)) return errors.New("failed to connect: wrong version") } + intf.link.core.log.Println("Do we have a link already?") // Check if we already have a link to this node intf.info.box = meta.box intf.info.sig = meta.sig @@ -124,6 +127,7 @@ func (intf *linkInterface) handler() error { intf.link.core.log.Println("DEBUG: registered interface for", intf.name) } intf.link.mutex.Unlock() + intf.link.core.log.Println("Create peer") // Create peer shared := crypto.GetSharedKey(myLinkPriv, &meta.link) intf.peer = intf.link.core.peers.newPeer(&meta.box, &meta.sig, shared, intf.name) @@ -145,6 +149,7 @@ func (intf *linkInterface) handler() error { intf.peer.close = func() { intf.msgIO.close() } go intf.peer.linkLoop() // Start the writer + intf.link.core.log.Println("Start writer") go func() { interval := 4 * time.Second timer := time.NewTimer(interval) @@ -190,6 +195,7 @@ func (intf *linkInterface) handler() error { }() intf.link.core.switchTable.idleIn <- intf.peer.port // notify switch that we're idle // Run reader loop + intf.link.core.log.Println("Start reader") for { msg, err := intf.msgIO.readMsg() if len(msg) > 0 { diff --git a/src/yggdrasil/mobile_ios.go b/src/yggdrasil/mobile_ios.go index 72920fe..e864c88 100644 --- a/src/yggdrasil/mobile_ios.go +++ b/src/yggdrasil/mobile_ios.go @@ -29,22 +29,14 @@ func (nsl MobileLogger) Write(p []byte) (n int, err error) { return len(p), nil } -func (c *Core) AWDLCreateInterface(name string) error { +func (c *Core) AWDLCreateInterface(name, local, remote string) error { fromAWDL := make(chan []byte, 32) toAWDL := make(chan []byte, 32) - - if intf, err := c.awdl.create(fromAWDL, toAWDL, name); err == nil { - if intf != nil { - c.log.Println(err) - return err - } else { - c.log.Println("c.awdl.create didn't return an interface") - return errors.New("c.awdl.create didn't return an interface") - } - } else { - c.log.Println(err) + if intf, err := c.awdl.create(fromAWDL, toAWDL, name, local, remote); err != nil || intf == nil { + c.log.Println("c.awdl.create:", err) return err } + return nil } func (c *Core) AWDLShutdownInterface(name string) error { @@ -53,7 +45,7 @@ func (c *Core) AWDLShutdownInterface(name string) error { func (c *Core) AWDLRecvPacket(identity string) ([]byte, error) { if intf := c.awdl.getInterface(identity); intf != nil { - return <-intf.toAWDL, nil + return <-intf.rwc.toAWDL, nil } return nil, errors.New("AWDLRecvPacket identity not known: " + identity) } @@ -61,7 +53,7 @@ func (c *Core) AWDLRecvPacket(identity string) ([]byte, error) { func (c *Core) AWDLSendPacket(identity string, buf []byte) error { packet := append(util.GetBytes(), buf[:]...) if intf := c.awdl.getInterface(identity); intf != nil { - intf.fromAWDL <- packet + intf.rwc.fromAWDL <- packet return nil } return errors.New("AWDLSendPacket identity not known: " + identity) From 81545fd9bfc2d05cd7144ede8e0d231c62c1668f Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 23 Jan 2019 15:16:22 +0000 Subject: [PATCH 081/109] Clean up --- src/yggdrasil/awdl.go | 3 +- src/yggdrasil/link.go | 106 ----------------------- src/yggdrasil/stream.go | 31 +------ src/yggdrasil/tcp.go | 187 +--------------------------------------- 4 files changed, 3 insertions(+), 324 deletions(-) diff --git a/src/yggdrasil/awdl.go b/src/yggdrasil/awdl.go index 8df98cc..ba3131f 100644 --- a/src/yggdrasil/awdl.go +++ b/src/yggdrasil/awdl.go @@ -54,7 +54,7 @@ func (l *awdl) create(fromAWDL chan []byte, toAWDL chan []byte, name, local, rem toAWDL: toAWDL, } s := stream{} - s.init(rwc, nil) + s.init(rwc) link, err := l.core.link.create(&s, name, "awdl", local, remote) if err != nil { return nil, err @@ -63,7 +63,6 @@ func (l *awdl) create(fromAWDL chan []byte, toAWDL chan []byte, name, local, rem link: link, rwc: rwc, } - intf.stream.init(intf.rwc, nil) l.mutex.Lock() l.interfaces[name] = &intf l.mutex.Unlock() diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index c056e6b..e763fb1 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -208,109 +208,3 @@ func (intf *linkInterface) handler() error { //////////////////////////////////////////////////////////////////////////////// return nil } - -/* -func (intf *linkInterface) start() { - myLinkPub, myLinkPriv := crypto.NewBoxKeys() - meta := version_getBaseMetadata() - meta.box = intf.link.core.boxPub - meta.sig = intf.link.core.sigPub - meta.link = *myLinkPub - metaBytes := meta.encode() - //intf.link.core.log.Println("start: intf.tolink <- metaBytes") - intf.tolink <- metaBytes - //intf.link.core.log.Println("finish: intf.tolink <- metaBytes") - //intf.link.core.log.Println("start: metaBytes = <-intf.fromlink") - metaBytes = <-intf.fromlink - //intf.link.core.log.Println("finish: metaBytes = <-intf.fromlink") - meta = version_metadata{} - if !meta.decode(metaBytes) || !meta.check() { - intf.link.core.log.Println("Metadata decode failure") - return - } - base := version_getBaseMetadata() - if meta.ver > base.ver || meta.ver == base.ver && meta.minorVer > base.minorVer { - intf.link.core.log.Println("Failed to connect to node: " + intf.name + " version: " + fmt.Sprintf("%d.%d", meta.ver, meta.minorVer)) - return - } - shared := crypto.GetSharedKey(myLinkPriv, &meta.link) - intf.peer = intf.link.core.peers.newPeer(&meta.box, &meta.sig, shared, intf.name) - if intf.peer == nil { - intf.link.mutex.Lock() - delete(intf.link.interfaces, intf.name) - intf.link.mutex.Unlock() - return - } - intf.peer.linkOut = make(chan []byte, 1) // protocol traffic - intf.peer.out = func(msg []byte) { - defer func() { recover() }() - intf.tolink <- msg - } // called by peer.sendPacket() - intf.link.core.switchTable.idleIn <- intf.peer.port // notify switch that we're idle - intf.peer.close = func() { - close(intf.fromlink) - close(intf.tolink) - } - go intf.handler() - go intf.peer.linkLoop() -} - -func (l *link) getInterface(identity string) *linkInterface { - l.mutex.RLock() - defer l.mutex.RUnlock() - if intf, ok := l.interfaces[identity]; ok { - return intf - } - return nil -} - -func (l *link) shutdown(identity string) error { - if intf, ok := l.interfaces[identity]; ok { - intf.shutdown <- true - l.core.peers.removePeer(intf.peer.port) - l.mutex.Lock() - delete(l.interfaces, identity) - l.mutex.Unlock() - return nil - } else { - return fmt.Errorf("interface '%s' doesn't exist or already shutdown", identity) - } -} - -func (ai *linkInterface) handler() { - send := func(msg []byte) { - ai.tolink <- msg - atomic.AddUint64(&ai.peer.bytesSent, uint64(len(msg))) - util.PutBytes(msg) - } - for { - timerInterval := tcp_ping_interval - timer := time.NewTimer(timerInterval) - defer timer.Stop() - select { - case p := <-ai.peer.linkOut: - send(p) - continue - default: - } - timer.Stop() - select { - case <-timer.C: - default: - } - timer.Reset(timerInterval) - select { - case _ = <-timer.C: - send([]byte{}) - case p := <-ai.peer.linkOut: - send(p) - continue - case r := <-ai.fromlink: - ai.peer.handlePacket(r) - ai.link.core.switchTable.idleIn <- ai.peer.port - case <-ai.shutdown: - return - } - } -} -*/ diff --git a/src/yggdrasil/stream.go b/src/yggdrasil/stream.go index 966319a..db2cdf7 100644 --- a/src/yggdrasil/stream.go +++ b/src/yggdrasil/stream.go @@ -14,8 +14,6 @@ var _ = linkInterfaceMsgIO(&stream{}) type stream struct { rwc io.ReadWriteCloser inputBuffer []byte // Incoming packet stream - // TODO remove the rest, it shouldn't matter in the long run - handlePacket func([]byte) } func (s *stream) close() error { @@ -26,11 +24,9 @@ const streamMsgSize = 2048 + 65535 var streamMsg = [...]byte{0xde, 0xad, 0xb1, 0x75} // "dead bits" -func (s *stream) init(rwc io.ReadWriteCloser, in func([]byte)) { +func (s *stream) init(rwc io.ReadWriteCloser) { // TODO have this also do the metadata handshake and create the peer struct s.rwc = rwc - s.handlePacket = in - // TODO call something to do the metadata exchange } @@ -112,31 +108,6 @@ func (s *stream) _recvMetaBytes() ([]byte, error) { return metaBytes, nil } -// This reads from the channel into a []byte buffer for incoming messages. It -// copies completed messages out of the cache into a new slice, and passes them -// to the peer struct via the provided `in func([]byte)` argument. Then it -// shifts the incomplete fragments of data forward so future reads won't -// overwrite it. -func (s *stream) handleInput(bs []byte) error { - if len(bs) > 0 { - s.inputBuffer = append(s.inputBuffer, bs...) - buf := s.inputBuffer - msg, ok, err2 := stream_chopMsg(&buf) - if err2 != nil { - return fmt.Errorf("message error: %v", err2) - } - if !ok { - // We didn't get the whole message yet - return nil - } - newMsg := append(util.GetBytes(), msg...) - s.inputBuffer = append(s.inputBuffer[:0], buf...) - s.handlePacket(newMsg) - util.Yield() // Make sure we give up control to the scheduler - } - return nil -} - // This takes a pointer to a slice as an argument. It checks if there's a // complete message and, if so, slices out those parts and returns the message, // true, and nil. If there's no error, but also no complete message, it returns diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index e1f490e..74c5ffe 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -20,14 +20,11 @@ import ( "math/rand" "net" "sync" - "sync/atomic" "time" "golang.org/x/net/proxy" - "github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/crypto" - "github.com/yggdrasil-network/yggdrasil-go/src/util" ) const default_timeout = 6 * time.Second @@ -284,7 +281,7 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) { defer sock.Close() iface.setExtraOptions(sock) stream := stream{} - stream.init(sock, nil) + stream.init(sock) local, _, _ := net.SplitHostPort(sock.LocalAddr().String()) remote, _, _ := net.SplitHostPort(sock.RemoteAddr().String()) name := "tcp://" + sock.RemoteAddr().String() @@ -297,185 +294,3 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) { err = link.handler() iface.core.log.Println("DEBUG: stopped handler for", name, err) } - -// This exchanges/checks connection metadata, sets up the peer struct, sets up the writer goroutine, and then runs the reader within the current goroutine. -// It defers a bunch of cleanup stuff to tear down all of these things when the reader exists (e.g. due to a closed connection or a timeout). -func (iface *tcpInterface) handler_old(sock net.Conn, incoming bool) { - defer sock.Close() - iface.setExtraOptions(sock) - // Get our keys - myLinkPub, myLinkPriv := crypto.NewBoxKeys() // ephemeral link keys - meta := version_getBaseMetadata() - meta.box = iface.core.boxPub - meta.sig = iface.core.sigPub - meta.link = *myLinkPub - metaBytes := meta.encode() - _, err := sock.Write(metaBytes) - if err != nil { - return - } - if iface.timeout > 0 { - sock.SetReadDeadline(time.Now().Add(iface.timeout)) - } - _, err = sock.Read(metaBytes) - if err != nil { - return - } - meta = version_metadata{} // Reset to zero value - if !meta.decode(metaBytes) || !meta.check() { - // Failed to decode and check the metadata - // If it's a version mismatch issue, then print an error message - base := version_getBaseMetadata() - if meta.meta == base.meta { - if meta.ver > base.ver { - iface.core.log.Println("Failed to connect to node:", sock.RemoteAddr().String(), "version:", meta.ver) - } else if meta.ver == base.ver && meta.minorVer > base.minorVer { - iface.core.log.Println("Failed to connect to node:", sock.RemoteAddr().String(), "version:", fmt.Sprintf("%d.%d", meta.ver, meta.minorVer)) - } - } - // TODO? Block forever to prevent future connection attempts? suppress future messages about the same node? - return - } - remoteAddr, _, e1 := net.SplitHostPort(sock.RemoteAddr().String()) - localAddr, _, e2 := net.SplitHostPort(sock.LocalAddr().String()) - if e1 != nil || e2 != nil { - return - } - info := tcpInfo{ // used as a map key, so don't include ephemeral link key - box: meta.box, - sig: meta.sig, - localAddr: localAddr, - remoteAddr: remoteAddr, - } - if iface.isAlreadyConnected(info) { - return - } - // Quit the parent call if this is a connection to ourself - equiv := func(k1, k2 []byte) bool { - for idx := range k1 { - if k1[idx] != k2[idx] { - return false - } - } - return true - } - if equiv(meta.box[:], iface.core.boxPub[:]) { - return - } - if equiv(meta.sig[:], iface.core.sigPub[:]) { - return - } - // Check if we're authorized to connect to this key / IP - if incoming && !iface.core.peers.isAllowedEncryptionPublicKey(&meta.box) { - // Allow unauthorized peers if they're link-local - raddrStr, _, _ := net.SplitHostPort(sock.RemoteAddr().String()) - raddr := net.ParseIP(raddrStr) - if !raddr.IsLinkLocalUnicast() { - return - } - } - // Check if we already have a connection to this node, close and block if yes - iface.mutex.Lock() - /*if blockChan, isIn := iface.conns[info]; isIn { - iface.mutex.Unlock() - sock.Close() - <-blockChan - return - }*/ - blockChan := make(chan struct{}) - iface.conns[info] = blockChan - iface.mutex.Unlock() - defer func() { - iface.mutex.Lock() - delete(iface.conns, info) - iface.mutex.Unlock() - close(blockChan) - }() - // Note that multiple connections to the same node are allowed - // E.g. over different interfaces - p := iface.core.peers.newPeer(&meta.box, &meta.sig, crypto.GetSharedKey(myLinkPriv, &meta.link), sock.RemoteAddr().String()) - p.linkOut = make(chan []byte, 1) - out := make(chan []byte, 1) - defer close(out) - go func() { - // This goroutine waits for outgoing packets, link protocol traffic, or sends idle keep-alive traffic - send := func(msg []byte) { - msgLen := wire_encode_uint64(uint64(len(msg))) - buf := net.Buffers{streamMsg[:], msgLen, msg} - buf.WriteTo(sock) - atomic.AddUint64(&p.bytesSent, uint64(len(streamMsg)+len(msgLen)+len(msg))) - util.PutBytes(msg) - } - timerInterval := tcp_ping_interval - timer := time.NewTimer(timerInterval) - defer timer.Stop() - for { - select { - case msg := <-p.linkOut: - // Always send outgoing link traffic first, if needed - send(msg) - continue - default: - } - // Otherwise wait reset the timer and wait for something to do - timer.Stop() - select { - case <-timer.C: - default: - } - timer.Reset(timerInterval) - select { - case _ = <-timer.C: - send(nil) // TCP keep-alive traffic - case msg := <-p.linkOut: - send(msg) - case msg, ok := <-out: - if !ok { - return - } - send(msg) // Block until the socket write has finished - // Now inform the switch that we're ready for more traffic - p.core.switchTable.idleIn <- p.port - } - } - }() - p.core.switchTable.idleIn <- p.port // Start in the idle state - p.out = func(msg []byte) { - defer func() { recover() }() - out <- msg - } - p.close = func() { sock.Close() } - go p.linkLoop() - defer func() { - // Put all of our cleanup here... - p.core.peers.removePeer(p.port) - }() - us, _, _ := net.SplitHostPort(sock.LocalAddr().String()) - them, _, _ := net.SplitHostPort(sock.RemoteAddr().String()) - themNodeID := crypto.GetNodeID(&meta.box) - themAddr := address.AddrForNodeID(themNodeID) - themAddrString := net.IP(themAddr[:]).String() - themString := fmt.Sprintf("%s@%s", themAddrString, them) - iface.core.log.Printf("Connected: %s, source: %s", themString, us) - //iface.stream.init(sock, p.handlePacket) - bs := make([]byte, 2*streamMsgSize) - var n int - for { - if iface.timeout > 0 { - sock.SetReadDeadline(time.Now().Add(iface.timeout)) - } - n, err = sock.Read(bs) - if err != nil { - break - } - if n > 0 { - //iface.stream.handleInput(bs[:n]) - } - } - if err == nil { - iface.core.log.Printf("Disconnected: %s, source: %s", themString, us) - } else { - iface.core.log.Printf("Disconnected: %s, source: %s, error: %s", themString, us, err) - } - return -} From 2944be4faff0419cc4e4c7d4dbe68672d56473df Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 23 Jan 2019 17:05:16 +0000 Subject: [PATCH 082/109] Further tweaks --- src/yggdrasil/awdl.go | 18 ++++++++++++------ src/yggdrasil/link.go | 6 ------ src/yggdrasil/mobile_ios.go | 10 ++++++---- src/yggdrasil/peer.go | 1 + 4 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/yggdrasil/awdl.go b/src/yggdrasil/awdl.go index ba3131f..3e26a25 100644 --- a/src/yggdrasil/awdl.go +++ b/src/yggdrasil/awdl.go @@ -2,6 +2,7 @@ package yggdrasil import ( "errors" + "io" "sync" ) @@ -24,8 +25,13 @@ type awdlReadWriteCloser struct { } func (c awdlReadWriteCloser) Read(p []byte) (n int, err error) { - p = <-c.fromAWDL - return len(p), nil + select { + case packet := <-c.fromAWDL: + n = copy(p, packet) + return n, nil + default: + return 0, io.EOF + } } func (c awdlReadWriteCloser) Write(p []byte) (n int, err error) { @@ -48,10 +54,10 @@ func (l *awdl) init(c *Core) error { return nil } -func (l *awdl) create(fromAWDL chan []byte, toAWDL chan []byte, name, local, remote string) (*awdlInterface, error) { +func (l *awdl) create(name, local, remote string) (*awdlInterface, error) { rwc := awdlReadWriteCloser{ - fromAWDL: fromAWDL, - toAWDL: toAWDL, + fromAWDL: make(chan []byte, 1), + toAWDL: make(chan []byte, 1), } s := stream{} s.init(rwc) @@ -66,7 +72,7 @@ func (l *awdl) create(fromAWDL chan []byte, toAWDL chan []byte, name, local, rem l.mutex.Lock() l.interfaces[name] = &intf l.mutex.Unlock() - go link.handler() + go intf.link.handler() return &intf, nil } diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index e763fb1..8b833f5 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -87,12 +87,10 @@ func (intf *linkInterface) handler() error { if err != nil { return err } - intf.link.core.log.Println("Sent my metadata") metaBytes, err = intf.msgIO._recvMetaBytes() if err != nil { return err } - intf.link.core.log.Println("Received their metadata") meta = version_metadata{} if !meta.decode(metaBytes) || !meta.check() { return errors.New("failed to decode metadata") @@ -102,7 +100,6 @@ func (intf *linkInterface) handler() error { intf.link.core.log.Println("Failed to connect to node: " + intf.name + " version: " + fmt.Sprintf("%d.%d", meta.ver, meta.minorVer)) return errors.New("failed to connect: wrong version") } - intf.link.core.log.Println("Do we have a link already?") // Check if we already have a link to this node intf.info.box = meta.box intf.info.sig = meta.sig @@ -127,7 +124,6 @@ func (intf *linkInterface) handler() error { intf.link.core.log.Println("DEBUG: registered interface for", intf.name) } intf.link.mutex.Unlock() - intf.link.core.log.Println("Create peer") // Create peer shared := crypto.GetSharedKey(myLinkPriv, &meta.link) intf.peer = intf.link.core.peers.newPeer(&meta.box, &meta.sig, shared, intf.name) @@ -149,7 +145,6 @@ func (intf *linkInterface) handler() error { intf.peer.close = func() { intf.msgIO.close() } go intf.peer.linkLoop() // Start the writer - intf.link.core.log.Println("Start writer") go func() { interval := 4 * time.Second timer := time.NewTimer(interval) @@ -195,7 +190,6 @@ func (intf *linkInterface) handler() error { }() intf.link.core.switchTable.idleIn <- intf.peer.port // notify switch that we're idle // Run reader loop - intf.link.core.log.Println("Start reader") for { msg, err := intf.msgIO.readMsg() if len(msg) > 0 { diff --git a/src/yggdrasil/mobile_ios.go b/src/yggdrasil/mobile_ios.go index e864c88..5e0d4aa 100644 --- a/src/yggdrasil/mobile_ios.go +++ b/src/yggdrasil/mobile_ios.go @@ -30,9 +30,7 @@ func (nsl MobileLogger) Write(p []byte) (n int, err error) { } func (c *Core) AWDLCreateInterface(name, local, remote string) error { - fromAWDL := make(chan []byte, 32) - toAWDL := make(chan []byte, 32) - if intf, err := c.awdl.create(fromAWDL, toAWDL, name, local, remote); err != nil || intf == nil { + if intf, err := c.awdl.create(name, local, remote); err != nil || intf == nil { c.log.Println("c.awdl.create:", err) return err } @@ -45,7 +43,11 @@ func (c *Core) AWDLShutdownInterface(name string) error { func (c *Core) AWDLRecvPacket(identity string) ([]byte, error) { if intf := c.awdl.getInterface(identity); intf != nil { - return <-intf.rwc.toAWDL, nil + read, ok := <-intf.rwc.toAWDL + if !ok { + return nil, errors.New("AWDLRecvPacket: channel closed") + } + return read, nil } return nil, errors.New("AWDLRecvPacket identity not known: " + identity) } diff --git a/src/yggdrasil/peer.go b/src/yggdrasil/peer.go index 2cd1afe..ad54fbc 100644 --- a/src/yggdrasil/peer.go +++ b/src/yggdrasil/peer.go @@ -250,6 +250,7 @@ func (p *peer) handleTraffic(packet []byte, pTypeLen int) { func (p *peer) sendPacket(packet []byte) { // Is there ever a case where something more complicated is needed? // What if p.out blocks? + atomic.AddUint64(&p.bytesSent, uint64(len(packet))) p.out(packet) } From 188a9e439dbac4a4e9a99526eecc541ba6d7bd74 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 23 Jan 2019 18:16:31 +0000 Subject: [PATCH 083/109] Bug fixes for AWDL --- src/yggdrasil/awdl.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/yggdrasil/awdl.go b/src/yggdrasil/awdl.go index 3e26a25..56104ee 100644 --- a/src/yggdrasil/awdl.go +++ b/src/yggdrasil/awdl.go @@ -25,18 +25,18 @@ type awdlReadWriteCloser struct { } func (c awdlReadWriteCloser) Read(p []byte) (n int, err error) { - select { - case packet := <-c.fromAWDL: + if packet, ok := <-c.fromAWDL; ok { n = copy(p, packet) return n, nil - default: - return 0, io.EOF } + return 0, io.EOF } func (c awdlReadWriteCloser) Write(p []byte) (n int, err error) { - c.toAWDL <- p - return len(p), nil + var pc []byte + pc = append(pc, p...) + c.toAWDL <- pc + return len(pc), nil } func (c awdlReadWriteCloser) Close() error { From 705b914d008f4917a4fee8ea5bdd0d875d930367 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 23 Jan 2019 19:42:33 +0000 Subject: [PATCH 084/109] Move awdl into link --- src/yggdrasil/awdl.go | 50 ++++++++++++++++++------------------- src/yggdrasil/core.go | 3 +-- src/yggdrasil/link.go | 3 ++- src/yggdrasil/mobile_ios.go | 10 ++++---- 4 files changed, 33 insertions(+), 33 deletions(-) diff --git a/src/yggdrasil/awdl.go b/src/yggdrasil/awdl.go index 56104ee..e9e57a6 100644 --- a/src/yggdrasil/awdl.go +++ b/src/yggdrasil/awdl.go @@ -7,13 +7,13 @@ import ( ) type awdl struct { - core *Core + link *link mutex sync.RWMutex // protects interfaces below interfaces map[string]*awdlInterface } type awdlInterface struct { - link *linkInterface + linkif *linkInterface rwc awdlReadWriteCloser peer *peer stream stream @@ -45,53 +45,53 @@ func (c awdlReadWriteCloser) Close() error { return nil } -func (l *awdl) init(c *Core) error { - l.core = c - l.mutex.Lock() - l.interfaces = make(map[string]*awdlInterface) - l.mutex.Unlock() +func (a *awdl) init(l *link) error { + a.link = l + a.mutex.Lock() + a.interfaces = make(map[string]*awdlInterface) + a.mutex.Unlock() return nil } -func (l *awdl) create(name, local, remote string) (*awdlInterface, error) { +func (a *awdl) create(name, local, remote string) (*awdlInterface, error) { rwc := awdlReadWriteCloser{ fromAWDL: make(chan []byte, 1), toAWDL: make(chan []byte, 1), } s := stream{} s.init(rwc) - link, err := l.core.link.create(&s, name, "awdl", local, remote) + linkif, err := a.link.create(&s, name, "awdl", local, remote) if err != nil { return nil, err } intf := awdlInterface{ - link: link, - rwc: rwc, + linkif: linkif, + rwc: rwc, } - l.mutex.Lock() - l.interfaces[name] = &intf - l.mutex.Unlock() - go intf.link.handler() + a.mutex.Lock() + a.interfaces[name] = &intf + a.mutex.Unlock() + go intf.linkif.handler() return &intf, nil } -func (l *awdl) getInterface(identity string) *awdlInterface { - l.mutex.RLock() - defer l.mutex.RUnlock() - if intf, ok := l.interfaces[identity]; ok { +func (a *awdl) getInterface(identity string) *awdlInterface { + a.mutex.RLock() + defer a.mutex.RUnlock() + if intf, ok := a.interfaces[identity]; ok { return intf } return nil } -func (l *awdl) shutdown(identity string) error { - if intf, ok := l.interfaces[identity]; ok { - close(intf.link.closed) +func (a *awdl) shutdown(identity string) error { + if intf, ok := a.interfaces[identity]; ok { + close(intf.linkif.closed) intf.rwc.Close() - l.mutex.Lock() - delete(l.interfaces, identity) - l.mutex.Unlock() + a.mutex.Lock() + delete(a.interfaces, identity) + a.mutex.Unlock() return nil } return errors.New("Interface not found or already closed") diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index 7090561..4a1aba3 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -44,8 +44,7 @@ type Core struct { searches searches multicast multicast tcp tcpInterface - link link // TODO: not sure if this wants to be here? - awdl awdl + link link log *log.Logger } diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index 8b833f5..99044ce 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -15,6 +15,7 @@ type link struct { core *Core mutex sync.RWMutex // protects interfaces below interfaces map[linkInfo]*linkInterface + awdl awdl // AWDL interface support } type linkInfo struct { @@ -49,7 +50,7 @@ func (l *link) init(c *Core) error { l.interfaces = make(map[linkInfo]*linkInterface) l.mutex.Unlock() - if err := l.core.awdl.init(c); err != nil { + if err := l.awdl.init(l); err != nil { l.core.log.Println("Failed to start AWDL interface") return err } diff --git a/src/yggdrasil/mobile_ios.go b/src/yggdrasil/mobile_ios.go index 5e0d4aa..c2ec63b 100644 --- a/src/yggdrasil/mobile_ios.go +++ b/src/yggdrasil/mobile_ios.go @@ -30,19 +30,19 @@ func (nsl MobileLogger) Write(p []byte) (n int, err error) { } func (c *Core) AWDLCreateInterface(name, local, remote string) error { - if intf, err := c.awdl.create(name, local, remote); err != nil || intf == nil { - c.log.Println("c.awdl.create:", err) + if intf, err := c.link.awdl.create(name, local, remote); err != nil || intf == nil { + c.log.Println("c.link.awdl.create:", err) return err } return nil } func (c *Core) AWDLShutdownInterface(name string) error { - return c.awdl.shutdown(name) + return c.link.awdl.shutdown(name) } func (c *Core) AWDLRecvPacket(identity string) ([]byte, error) { - if intf := c.awdl.getInterface(identity); intf != nil { + if intf := c.link.awdl.getInterface(identity); intf != nil { read, ok := <-intf.rwc.toAWDL if !ok { return nil, errors.New("AWDLRecvPacket: channel closed") @@ -54,7 +54,7 @@ func (c *Core) AWDLRecvPacket(identity string) ([]byte, error) { func (c *Core) AWDLSendPacket(identity string, buf []byte) error { packet := append(util.GetBytes(), buf[:]...) - if intf := c.awdl.getInterface(identity); intf != nil { + if intf := c.link.awdl.getInterface(identity); intf != nil { intf.rwc.fromAWDL <- packet return nil } From 2baedc9bcc6fa4bd4863d84019ce652ef1037e6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Christian=20Gr=C3=BCnhage?= Date: Fri, 25 Jan 2019 21:28:22 +0100 Subject: [PATCH 085/109] make ansible key generator a tiny bit faster --- contrib/ansible/genkeys.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contrib/ansible/genkeys.go b/contrib/ansible/genkeys.go index 22418a0..7df2e58 100644 --- a/contrib/ansible/genkeys.go +++ b/contrib/ansible/genkeys.go @@ -117,6 +117,8 @@ func bubbleUpTo(sets []keySet, num int) []keySet { var tmp = sets[i] sets[i] = sets[i + 1] sets[i + 1] = tmp + } else { + break } } return sets From bca69df1f6da415c97d15c5b18a770b404ffa190 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sat, 26 Jan 2019 14:07:18 -0600 Subject: [PATCH 086/109] possible workaround to a deadlock --- 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 6b7f62d..cdabaf2 100644 --- a/src/yggdrasil/session.go +++ b/src/yggdrasil/session.go @@ -625,5 +625,8 @@ func (sinfo *sessionInfo) doRecv(p *wire_trafficPacket) { sinfo.updateNonce(&p.Nonce) sinfo.time = time.Now() sinfo.bytesRecvd += uint64(len(bs)) - sinfo.core.router.toRecv <- router_recvPacket{bs, sinfo} + select { + case sinfo.core.router.toRecv <- router_recvPacket{bs, sinfo}: + default: // avoid deadlocks, maybe do this somewhere else?... + } } From 0838928668de232beb00c595149b28cc31ff5a52 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 27 Jan 2019 13:31:43 +0000 Subject: [PATCH 087/109] Add support for flexible logging levels --- cmd/yggdrasil/main.go | 25 ++++++++++++++++++++----- go.mod | 1 + go.sum | 2 ++ src/yggdrasil/admin.go | 12 ++++++------ src/yggdrasil/awdl.go | 12 ++++++------ src/yggdrasil/ckr.go | 10 +++++----- src/yggdrasil/core.go | 27 ++++++++++++++------------- src/yggdrasil/mobile.go | 2 -- src/yggdrasil/multicast.go | 6 +++--- src/yggdrasil/router.go | 2 +- src/yggdrasil/switch.go | 2 +- src/yggdrasil/tcp.go | 16 ++++++++-------- src/yggdrasil/tun.go | 6 +++--- src/yggdrasil/tun_bsd.go | 22 +++++++++++----------- src/yggdrasil/tun_darwin.go | 12 ++++++------ src/yggdrasil/tun_linux.go | 6 +++--- src/yggdrasil/tun_other.go | 2 +- src/yggdrasil/tun_windows.go | 28 ++++++++++++++-------------- 18 files changed, 105 insertions(+), 88 deletions(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index f11bbc0..f67bbce 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -6,7 +6,6 @@ import ( "flag" "fmt" "io/ioutil" - "log" "os" "os/signal" "strings" @@ -14,6 +13,7 @@ import ( "golang.org/x/text/encoding/unicode" + "github.com/gologme/log" "github.com/hjson/hjson-go" "github.com/kardianos/minwinsvc" "github.com/mitchellh/mapstructure" @@ -169,6 +169,7 @@ func main() { confjson := flag.Bool("json", false, "print configuration from -genconf or -normaliseconf as JSON instead of HJSON") autoconf := flag.Bool("autoconf", false, "automatic mode (dynamic IP, peer with IPv6 neighbors)") version := 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") flag.Parse() var cfg *nodeConfig @@ -217,6 +218,20 @@ func main() { } // Create a new logger that logs output to stdout. logger := log.New(os.Stdout, "", log.Flags()) + //logger.EnableLevel("error") + //logger.EnableLevel("warn") + //logger.EnableLevel("info") + if levels := strings.Split(*logging, ","); len(levels) > 0 { + for _, level := range levels { + l := strings.TrimSpace(level) + switch l { + case "error", "warn", "info", "trace", "debug": + logger.EnableLevel(l) + default: + continue + } + } + } // Setup the Yggdrasil node itself. The node{} type includes a Core, so we // don't need to create this manually. n := node{} @@ -224,7 +239,7 @@ func main() { // Yggdrasil. This will start the router, switch, DHT node, TCP and UDP // sockets, TUN/TAP adapter and multicast discovery port. if err := n.core.Start(cfg, logger); err != nil { - logger.Println("An error occurred during startup") + logger.Errorln("An error occurred during startup") panic(err) } // The Stop function ensures that the TUN/TAP adapter is correctly shut down @@ -236,8 +251,8 @@ func main() { // This is just logged to stdout for the user. address := n.core.GetAddress() subnet := n.core.GetSubnet() - logger.Printf("Your IPv6 address is %s", address.String()) - logger.Printf("Your IPv6 subnet is %s", subnet.String()) + logger.Infof("Your IPv6 address is %s", address.String()) + logger.Infof("Your IPv6 subnet is %s", subnet.String()) // Catch interrupts from the operating system to exit gracefully. c := make(chan os.Signal, 1) r := make(chan os.Signal, 1) @@ -257,7 +272,7 @@ func main() { cfg = readConfig(useconf, useconffile, normaliseconf) n.core.UpdateConfig(cfg) } else { - logger.Println("Reloading config at runtime is only possible with -useconffile") + logger.Errorln("Reloading config at runtime is only possible with -useconffile") } case _ = <-c: goto exit diff --git a/go.mod b/go.mod index 53a5a2b..3e8db51 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ require ( github.com/hjson/hjson-go v0.0.0-20181010104306-a25ecf6bd222 github.com/kardianos/minwinsvc v0.0.0-20151122163309-cad6b2b879b0 github.com/mitchellh/mapstructure v1.1.2 + github.com/gologme/log v0.0.0-20181207131047-4e5d8ccb38e8 github.com/songgao/packets v0.0.0-20160404182456-549a10cd4091 github.com/yggdrasil-network/water v0.0.0-20180615095340-f732c88f34ae golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 diff --git a/go.sum b/go.sum index 1695daf..17b1017 100644 --- a/go.sum +++ b/go.sum @@ -18,3 +18,5 @@ golang.org/x/sys v0.0.0-20181206074257-70b957f3b65e h1:njOxP/wVblhCLIUhjHXf6X+dz golang.org/x/sys v0.0.0-20181206074257-70b957f3b65e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +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= diff --git a/src/yggdrasil/admin.go b/src/yggdrasil/admin.go index f601776..b54ac5c 100644 --- a/src/yggdrasil/admin.go +++ b/src/yggdrasil/admin.go @@ -398,7 +398,7 @@ func (a *admin) listen() { switch strings.ToLower(u.Scheme) { case "unix": if _, err := os.Stat(a.listenaddr[7:]); err == nil { - a.core.log.Println("WARNING:", a.listenaddr[7:], "already exists and may be in use by another process") + a.core.log.Warnln("WARNING:", a.listenaddr[7:], "already exists and may be in use by another process") } a.listener, err = net.Listen("unix", a.listenaddr[7:]) if err == nil { @@ -406,7 +406,7 @@ func (a *admin) listen() { case "@": // maybe abstract namespace default: if err := os.Chmod(a.listenaddr[7:], 0660); err != nil { - a.core.log.Println("WARNING:", a.listenaddr[:7], "may have unsafe permissions!") + a.core.log.Warnln("WARNING:", a.listenaddr[:7], "may have unsafe permissions!") } } } @@ -420,10 +420,10 @@ func (a *admin) listen() { a.listener, err = net.Listen("tcp", a.listenaddr) } if err != nil { - a.core.log.Printf("Admin socket failed to listen: %v", err) + a.core.log.Errorf("Admin socket failed to listen: %v", err) os.Exit(1) } - a.core.log.Printf("%s admin socket listening on %s", + a.core.log.Infof("%s admin socket listening on %s", strings.ToUpper(a.listener.Addr().Network()), a.listener.Addr().String()) defer a.listener.Close() @@ -450,9 +450,9 @@ func (a *admin) handleRequest(conn net.Conn) { "status": "error", "error": "Unrecoverable error, possibly as a result of invalid input types or malformed syntax", } - fmt.Println("Admin socket error:", r) + a.core.log.Errorln("Admin socket error:", r) if err := encoder.Encode(&send); err != nil { - fmt.Println("Admin socket JSON encode error:", err) + a.core.log.Errorln("Admin socket JSON encode error:", err) } conn.Close() } diff --git a/src/yggdrasil/awdl.go b/src/yggdrasil/awdl.go index 633d5f9..190d025 100644 --- a/src/yggdrasil/awdl.go +++ b/src/yggdrasil/awdl.go @@ -50,24 +50,24 @@ func (l *awdl) create(fromAWDL chan []byte, toAWDL chan []byte /*boxPubKey *cryp meta.sig = l.core.sigPub meta.link = *myLinkPub metaBytes := meta.encode() - l.core.log.Println("toAWDL <- metaBytes") + l.core.log.Traceln("toAWDL <- metaBytes") toAWDL <- metaBytes - l.core.log.Println("metaBytes = <-fromAWDL") + l.core.log.Traceln("metaBytes = <-fromAWDL") metaBytes = <-fromAWDL - l.core.log.Println("version_metadata{}") + l.core.log.Traceln("version_metadata{}") meta = version_metadata{} if !meta.decode(metaBytes) || !meta.check() { return nil, errors.New("Metadata decode failure") } - l.core.log.Println("version_getBaseMetadata{}") + l.core.log.Traceln("version_getBaseMetadata{}") base := version_getBaseMetadata() if meta.ver > base.ver || meta.ver == base.ver && meta.minorVer > base.minorVer { return nil, errors.New("Failed to connect to node: " + name + " version: " + fmt.Sprintf("%d.%d", meta.ver, meta.minorVer)) } - l.core.log.Println("crypto.GetSharedKey") + l.core.log.Traceln("crypto.GetSharedKey") shared := crypto.GetSharedKey(myLinkPriv, &meta.link) //shared := crypto.GetSharedKey(&l.core.boxPriv, boxPubKey) - l.core.log.Println("l.core.peers.newPeer") + l.core.log.Traceln("l.core.peers.newPeer") intf.peer = l.core.peers.newPeer(&meta.box, &meta.sig, shared, name) if intf.peer != nil { intf.peer.linkOut = make(chan []byte, 1) // protocol traffic diff --git a/src/yggdrasil/ckr.go b/src/yggdrasil/ckr.go index a5ed455..03bc571 100644 --- a/src/yggdrasil/ckr.go +++ b/src/yggdrasil/ckr.go @@ -48,7 +48,7 @@ func (c *cryptokey) init(core *Core) { }() if err := c.configure(); err != nil { - c.core.log.Println("CKR configuration failed:", err) + c.core.log.Errorln("CKR configuration failed:", err) } } @@ -192,7 +192,7 @@ func (c *cryptokey) addSourceSubnet(cidr string) error { // Add the source subnet *routingsources = append(*routingsources, *ipnet) - c.core.log.Println("Added CKR source subnet", cidr) + c.core.log.Infoln("Added CKR source subnet", cidr) return nil } @@ -264,7 +264,7 @@ func (c *cryptokey) addRoute(cidr string, dest string) error { delete(*routingcache, k) } - c.core.log.Println("Added CKR destination subnet", cidr) + c.core.log.Infoln("Added CKR destination subnet", cidr) return nil } } @@ -358,7 +358,7 @@ func (c *cryptokey) removeSourceSubnet(cidr string) error { for idx, subnet := range *routingsources { if subnet.String() == ipnet.String() { *routingsources = append((*routingsources)[:idx], (*routingsources)[idx+1:]...) - c.core.log.Println("Removed CKR source subnet", cidr) + c.core.log.Infoln("Removed CKR source subnet", cidr) return nil } } @@ -407,7 +407,7 @@ func (c *cryptokey) removeRoute(cidr string, dest string) error { for k := range *routingcache { delete(*routingcache, k) } - c.core.log.Printf("Removed CKR destination subnet %s via %s\n", cidr, dest) + c.core.log.Infoln("Removed CKR destination subnet %s via %s\n", cidr, dest) return nil } } diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index ed07581..c78bc1f 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -3,11 +3,12 @@ package yggdrasil import ( "encoding/hex" "io/ioutil" - "log" "net" "sync" "time" + "github.com/gologme/log" + "github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/config" "github.com/yggdrasil-network/yggdrasil-go/src/crypto" @@ -178,13 +179,13 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error { c.log = log if name := GetBuildName(); name != "unknown" { - c.log.Println("Build name:", name) + c.log.Infoln("Build name:", name) } if version := GetBuildVersion(); version != "unknown" { - c.log.Println("Build version:", version) + c.log.Infoln("Build version:", version) } - c.log.Println("Starting up...") + c.log.Infoln("Starting up...") c.configMutex.Lock() c.config = *nc @@ -194,12 +195,12 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error { c.init() if err := c.tcp.init(c); err != nil { - c.log.Println("Failed to start TCP interface") + c.log.Errorln("Failed to start TCP interface") return err } if err := c.awdl.init(c); err != nil { - c.log.Println("Failed to start AWDL interface") + c.log.Errorln("Failed to start AWDL interface") return err } @@ -208,39 +209,39 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error { } if err := c.switchTable.start(); err != nil { - c.log.Println("Failed to start switch") + c.log.Errorln("Failed to start switch") return err } if err := c.router.start(); err != nil { - c.log.Println("Failed to start router") + c.log.Errorln("Failed to start router") return err } if err := c.admin.start(); err != nil { - c.log.Println("Failed to start admin socket") + c.log.Errorln("Failed to start admin socket") return err } if err := c.multicast.start(); err != nil { - c.log.Println("Failed to start multicast interface") + c.log.Errorln("Failed to start multicast interface") return err } if err := c.router.tun.start(); err != nil { - c.log.Println("Failed to start TUN/TAP") + c.log.Errorln("Failed to start TUN/TAP") return err } go c.addPeerLoop() - c.log.Println("Startup complete") + c.log.Infoln("Startup complete") return nil } // Stops the Yggdrasil node. func (c *Core) Stop() { - c.log.Println("Stopping...") + c.log.Infoln("Stopping...") c.router.tun.close() c.admin.close() } diff --git a/src/yggdrasil/mobile.go b/src/yggdrasil/mobile.go index 2ffeffb..220e6ca 100644 --- a/src/yggdrasil/mobile.go +++ b/src/yggdrasil/mobile.go @@ -77,9 +77,7 @@ func (c *Core) StartJSON(configjson []byte) error { return err } nc.IfName = "dummy" - //c.log.Println(nc.MulticastInterfaces) for _, ll := range nc.MulticastInterfaces { - //c.log.Println("Processing MC", ll) ifceExpr, err := regexp.Compile(ll) if err != nil { panic(err) diff --git a/src/yggdrasil/multicast.go b/src/yggdrasil/multicast.go index 08f0954..42651de 100644 --- a/src/yggdrasil/multicast.go +++ b/src/yggdrasil/multicast.go @@ -35,15 +35,15 @@ func (m *multicast) init(core *Core) { m.groupAddr = "[ff02::114]:9001" // Check if we've been given any expressions if count := len(m.interfaces()); count != 0 { - m.core.log.Println("Found", count, "multicast interface(s)") + m.core.log.Infoln("Found", count, "multicast interface(s)") } } func (m *multicast) start() error { if len(m.interfaces()) == 0 { - m.core.log.Println("Multicast discovery is disabled") + m.core.log.Infoln("Multicast discovery is disabled") } else { - m.core.log.Println("Multicast discovery is enabled") + m.core.log.Infoln("Multicast discovery is enabled") addr, err := net.ResolveUDPAddr("udp", m.groupAddr) if err != nil { return err diff --git a/src/yggdrasil/router.go b/src/yggdrasil/router.go index 31eefbb..d505936 100644 --- a/src/yggdrasil/router.go +++ b/src/yggdrasil/router.go @@ -96,7 +96,7 @@ func (r *router) init(core *Core) { // Starts the mainLoop goroutine. func (r *router) start() error { - r.core.log.Println("Starting router") + r.core.log.Infoln("Starting router") go r.mainLoop() return nil } diff --git a/src/yggdrasil/switch.go b/src/yggdrasil/switch.go index 2a4e7e4..f2adf3f 100644 --- a/src/yggdrasil/switch.go +++ b/src/yggdrasil/switch.go @@ -563,7 +563,7 @@ func (t *switchTable) getTable() lookupTable { // Starts the switch worker func (t *switchTable) start() error { - t.core.log.Println("Starting switch") + t.core.log.Infoln("Starting switch") go t.doWorker() return nil } diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index 78e39ef..a83213d 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -139,16 +139,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.Println("Listening for TCP on:", iface.serv.Addr().String()) + iface.core.log.Infoln("Listening for TCP on:", iface.serv.Addr().String()) for { sock, err := iface.serv.Accept() if err != nil { - iface.core.log.Println("Failed to accept connection:", err) + iface.core.log.Errorln("Failed to accept connection:", err) return } select { case <-iface.serv_stop: - iface.core.log.Println("Stopping listener") + iface.core.log.Errorln("Stopping listener") return default: if err != nil { @@ -313,9 +313,9 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) { base := version_getBaseMetadata() if meta.meta == base.meta { if meta.ver > base.ver { - iface.core.log.Println("Failed to connect to node:", sock.RemoteAddr().String(), "version:", meta.ver) + iface.core.log.Errorln("Failed to connect to node:", sock.RemoteAddr().String(), "version:", meta.ver) } else if meta.ver == base.ver && meta.minorVer > base.minorVer { - iface.core.log.Println("Failed to connect to node:", sock.RemoteAddr().String(), "version:", fmt.Sprintf("%d.%d", meta.ver, meta.minorVer)) + iface.core.log.Errorln("Failed to connect to node:", sock.RemoteAddr().String(), "version:", fmt.Sprintf("%d.%d", meta.ver, meta.minorVer)) } } // TODO? Block forever to prevent future connection attempts? suppress future messages about the same node? @@ -444,12 +444,12 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) { themAddr := address.AddrForNodeID(themNodeID) themAddrString := net.IP(themAddr[:]).String() themString := fmt.Sprintf("%s@%s", themAddrString, them) - iface.core.log.Printf("Connected: %s, source: %s", themString, us) + iface.core.log.Infof("Connected: %s, source: %s", themString, us) err = iface.reader(sock, in) // In this goroutine, because of defers if err == nil { - iface.core.log.Printf("Disconnected: %s, source: %s", themString, us) + iface.core.log.Infof("Disconnected: %s, source: %s", themString, us) } else { - iface.core.log.Printf("Disconnected: %s, source: %s, error: %s", themString, us, err) + iface.core.log.Infof("Disconnected: %s, source: %s, error: %s", themString, us, err) } return } diff --git a/src/yggdrasil/tun.go b/src/yggdrasil/tun.go index c0a7139..465cbb1 100644 --- a/src/yggdrasil/tun.go +++ b/src/yggdrasil/tun.go @@ -53,7 +53,7 @@ func (tun *tunAdapter) init(core *Core, send chan<- []byte, recv <-chan []byte) tun.core.config.IfMTU != tun.core.configOld.IfMTU tun.core.configMutex.RUnlock() if updated { - tun.core.log.Println("Reconfiguring TUN/TAP is not supported yet") + tun.core.log.Warnln("Reconfiguring TUN/TAP is not supported yet") e <- nil } else { e <- nil @@ -82,8 +82,8 @@ func (tun *tunAdapter) start() error { tun.mutex.Lock() tun.isOpen = true tun.mutex.Unlock() - go func() { tun.core.log.Println("WARNING: tun.read() exited with error:", tun.read()) }() - go func() { tun.core.log.Println("WARNING: tun.write() exited with error:", tun.write()) }() + go func() { tun.core.log.Errorln("WARNING: tun.read() exited with error:", tun.read()) }() + go func() { tun.core.log.Errorln("WARNING: tun.write() exited with error:", tun.write()) }() if iftapmode { go func() { for { diff --git a/src/yggdrasil/tun_bsd.go b/src/yggdrasil/tun_bsd.go index 620c79d..81e2c46 100644 --- a/src/yggdrasil/tun_bsd.go +++ b/src/yggdrasil/tun_bsd.go @@ -114,9 +114,9 @@ func (tun *tunAdapter) setupAddress(addr string) error { } // Friendly output - tun.core.log.Printf("Interface name: %s", tun.iface.Name()) - tun.core.log.Printf("Interface IPv6: %s", addr) - tun.core.log.Printf("Interface MTU: %d", tun.mtu) + tun.core.log.Infof("Interface name: %s", tun.iface.Name()) + tun.core.log.Infof("Interface IPv6: %s", addr) + tun.core.log.Infof("Interface MTU: %d", tun.mtu) // Create the MTU request var ir in6_ifreq_mtu @@ -126,15 +126,15 @@ func (tun *tunAdapter) setupAddress(addr string) error { // Set the MTU if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(sfd), uintptr(syscall.SIOCSIFMTU), uintptr(unsafe.Pointer(&ir))); errno != 0 { err = errno - tun.core.log.Printf("Error in SIOCSIFMTU: %v", errno) + tun.core.log.Errorf("Error in SIOCSIFMTU: %v", errno) // Fall back to ifconfig to set the MTU cmd := exec.Command("ifconfig", tun.iface.Name(), "mtu", string(tun.mtu)) - tun.core.log.Printf("Using ifconfig as fallback: %v", strings.Join(cmd.Args, " ")) + tun.core.log.Warnf("Using ifconfig as fallback: %v", strings.Join(cmd.Args, " ")) output, err := cmd.CombinedOutput() if err != nil { - tun.core.log.Printf("SIOCSIFMTU fallback failed: %v.", err) - tun.core.log.Println(string(output)) + tun.core.log.Errorf("SIOCSIFMTU fallback failed: %v.", err) + tun.core.log.Traceln(string(output)) } } @@ -155,15 +155,15 @@ func (tun *tunAdapter) setupAddress(addr string) error { // Set the interface address if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(sfd), uintptr(SIOCSIFADDR_IN6), uintptr(unsafe.Pointer(&ar))); errno != 0 { err = errno - tun.core.log.Printf("Error in SIOCSIFADDR_IN6: %v", errno) + tun.core.log.Errorf("Error in SIOCSIFADDR_IN6: %v", errno) // Fall back to ifconfig to set the address cmd := exec.Command("ifconfig", tun.iface.Name(), "inet6", addr) - tun.core.log.Printf("Using ifconfig as fallback: %v", strings.Join(cmd.Args, " ")) + tun.core.log.Warnf("Using ifconfig as fallback: %v", strings.Join(cmd.Args, " ")) output, err := cmd.CombinedOutput() if err != nil { - tun.core.log.Printf("SIOCSIFADDR_IN6 fallback failed: %v.", err) - tun.core.log.Println(string(output)) + tun.core.log.Errorf("SIOCSIFADDR_IN6 fallback failed: %v.", err) + tun.core.log.Traceln(string(output)) } } diff --git a/src/yggdrasil/tun_darwin.go b/src/yggdrasil/tun_darwin.go index 828c01e..7ec1b8b 100644 --- a/src/yggdrasil/tun_darwin.go +++ b/src/yggdrasil/tun_darwin.go @@ -18,7 +18,7 @@ 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.core.log.Printf("TAP mode is not supported on this platform, defaulting to TUN") + tun.core.log.Warnln("TAP mode is not supported on this platform, defaulting to TUN") } config := water.Config{DeviceType: water.TUN} iface, err := water.New(config) @@ -98,19 +98,19 @@ func (tun *tunAdapter) setupAddress(addr string) error { copy(ir.ifr_name[:], tun.iface.Name()) ir.ifru_mtu = uint32(tun.mtu) - tun.core.log.Printf("Interface name: %s", ar.ifra_name) - tun.core.log.Printf("Interface IPv6: %s", addr) - tun.core.log.Printf("Interface MTU: %d", ir.ifru_mtu) + tun.core.log.Infof("Interface name: %s", ar.ifra_name) + tun.core.log.Infof("Interface IPv6: %s", addr) + tun.core.log.Infof("Interface MTU: %d", ir.ifru_mtu) if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(darwin_SIOCAIFADDR_IN6), uintptr(unsafe.Pointer(&ar))); errno != 0 { err = errno - tun.core.log.Printf("Error in darwin_SIOCAIFADDR_IN6: %v", errno) + tun.core.log.Errorf("Error in darwin_SIOCAIFADDR_IN6: %v", errno) return err } if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(unix.SIOCSIFMTU), uintptr(unsafe.Pointer(&ir))); errno != 0 { err = errno - tun.core.log.Printf("Error in SIOCSIFMTU: %v", errno) + tun.core.log.Errorf("Error in SIOCSIFMTU: %v", errno) return err } diff --git a/src/yggdrasil/tun_linux.go b/src/yggdrasil/tun_linux.go index 8ccdd30..30ada23 100644 --- a/src/yggdrasil/tun_linux.go +++ b/src/yggdrasil/tun_linux.go @@ -40,9 +40,9 @@ func (tun *tunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int } } // Friendly output - tun.core.log.Printf("Interface name: %s", tun.iface.Name()) - tun.core.log.Printf("Interface IPv6: %s", addr) - tun.core.log.Printf("Interface MTU: %d", tun.mtu) + tun.core.log.Infof("Interface name: %s", tun.iface.Name()) + tun.core.log.Infof("Interface IPv6: %s", addr) + tun.core.log.Infof("Interface MTU: %d", tun.mtu) return tun.setupAddress(addr) } diff --git a/src/yggdrasil/tun_other.go b/src/yggdrasil/tun_other.go index 22058c1..07ec25f 100644 --- a/src/yggdrasil/tun_other.go +++ b/src/yggdrasil/tun_other.go @@ -28,6 +28,6 @@ func (tun *tunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int // 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.core.log.Println("Platform not supported, you must set the address of", tun.iface.Name(), "to", addr) + tun.core.log.Warnln("Platform not supported, you must set the address of", tun.iface.Name(), "to", addr) return nil } diff --git a/src/yggdrasil/tun_windows.go b/src/yggdrasil/tun_windows.go index 150a976..1c89a43 100644 --- a/src/yggdrasil/tun_windows.go +++ b/src/yggdrasil/tun_windows.go @@ -15,7 +15,7 @@ import ( // delegate the hard work to "netsh". func (tun *tunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error { if !iftapmode { - tun.core.log.Printf("TUN mode is not supported on this platform, defaulting to TAP") + tun.core.log.Warnln("TUN mode is not supported on this platform, defaulting to TAP") } config := water.Config{DeviceType: water.TAP} config.PlatformSpecificParams.ComponentID = "tap0901" @@ -34,16 +34,16 @@ func (tun *tunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int tun.core.log.Printf("netsh command: %v", strings.Join(cmd.Args, " ")) output, err := cmd.CombinedOutput() if err != nil { - tun.core.log.Printf("Windows netsh failed: %v.", err) - tun.core.log.Println(string(output)) + tun.core.log.Errorf("Windows netsh failed: %v.", err) + tun.core.log.Traceln(string(output)) return err } cmd = exec.Command("netsh", "interface", "set", "interface", iface.Name(), "admin=ENABLED") tun.core.log.Printf("netsh command: %v", strings.Join(cmd.Args, " ")) output, err = cmd.CombinedOutput() if err != nil { - tun.core.log.Printf("Windows netsh failed: %v.", err) - tun.core.log.Println(string(output)) + tun.core.log.Errorf("Windows netsh failed: %v.", err) + tun.core.log.Traceln(string(output)) return err } // Get a new iface @@ -58,9 +58,9 @@ func (tun *tunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int panic(err) } // Friendly output - tun.core.log.Printf("Interface name: %s", tun.iface.Name()) - tun.core.log.Printf("Interface IPv6: %s", addr) - tun.core.log.Printf("Interface MTU: %d", tun.mtu) + tun.core.log.Infof("Interface name: %s", tun.iface.Name()) + tun.core.log.Infof("Interface IPv6: %s", addr) + tun.core.log.Infof("Interface MTU: %d", tun.mtu) return tun.setupAddress(addr) } @@ -71,11 +71,11 @@ func (tun *tunAdapter) setupMTU(mtu int) error { fmt.Sprintf("interface=%s", tun.iface.Name()), fmt.Sprintf("mtu=%d", mtu), "store=active") - tun.core.log.Printf("netsh command: %v", strings.Join(cmd.Args, " ")) + tun.core.log.Debugln("netsh command: %v", strings.Join(cmd.Args, " ")) output, err := cmd.CombinedOutput() if err != nil { - tun.core.log.Printf("Windows netsh failed: %v.", err) - tun.core.log.Println(string(output)) + tun.core.log.Errorf("Windows netsh failed: %v.", err) + tun.core.log.Traceln(string(output)) return err } return nil @@ -88,11 +88,11 @@ func (tun *tunAdapter) setupAddress(addr string) error { fmt.Sprintf("interface=%s", tun.iface.Name()), fmt.Sprintf("addr=%s", addr), "store=active") - tun.core.log.Printf("netsh command: %v", strings.Join(cmd.Args, " ")) + tun.core.log.Debugln("netsh command: %v", strings.Join(cmd.Args, " ")) output, err := cmd.CombinedOutput() if err != nil { - tun.core.log.Printf("Windows netsh failed: %v.", err) - tun.core.log.Println(string(output)) + tun.core.log.Errorf("Windows netsh failed: %v.", err) + tun.core.log.Traceln(string(output)) return err } return nil From 22d2e0e4fe305a47f3cc35b68daae98f10659b43 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 27 Jan 2019 13:33:32 +0000 Subject: [PATCH 088/109] Fix debug builds --- src/yggdrasil/debug.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/yggdrasil/debug.go b/src/yggdrasil/debug.go index 7c6c757..f7319e4 100644 --- a/src/yggdrasil/debug.go +++ b/src/yggdrasil/debug.go @@ -14,7 +14,6 @@ import _ "golang.org/x/net/ipv6" // TODO put this somewhere better import "fmt" import "net" -import "log" import "regexp" import "encoding/hex" @@ -23,6 +22,8 @@ import "net/http" import "runtime" import "os" +import "github.com/gologme/log" + import "github.com/yggdrasil-network/yggdrasil-go/src/address" import "github.com/yggdrasil-network/yggdrasil-go/src/config" import "github.com/yggdrasil-network/yggdrasil-go/src/crypto" From 2466c54a71a231795e283ad525268381ef066c55 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 27 Jan 2019 20:56:10 +0000 Subject: [PATCH 089/109] Update debug lines in link.go --- src/yggdrasil/link.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index 99044ce..1e65847 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -51,7 +51,7 @@ func (l *link) init(c *Core) error { l.mutex.Unlock() if err := l.awdl.init(l); err != nil { - l.core.log.Println("Failed to start AWDL interface") + l.core.log.Errorln("Failed to start AWDL interface") return err } @@ -98,7 +98,7 @@ func (intf *linkInterface) handler() error { } base := version_getBaseMetadata() if meta.ver > base.ver || meta.ver == base.ver && meta.minorVer > base.minorVer { - intf.link.core.log.Println("Failed to connect to node: " + intf.name + " version: " + fmt.Sprintf("%d.%d", meta.ver, meta.minorVer)) + intf.link.core.log.Errorln("Failed to connect to node: " + intf.name + " version: " + fmt.Sprintf("%d.%d", meta.ver, meta.minorVer)) return errors.New("failed to connect: wrong version") } // Check if we already have a link to this node @@ -109,7 +109,7 @@ func (intf *linkInterface) handler() error { intf.link.mutex.Unlock() // FIXME we should really return an error and let the caller block instead // That lets them do things like close connections on its own, avoid printing a connection message in the first place, etc. - intf.link.core.log.Println("DEBUG: found existing interface for", intf.name) + intf.link.core.log.Debugln("DEBUG: found existing interface for", intf.name) intf.msgIO.close() <-oldIntf.closed return nil @@ -122,7 +122,7 @@ func (intf *linkInterface) handler() error { intf.link.mutex.Unlock() close(intf.closed) }() - intf.link.core.log.Println("DEBUG: registered interface for", intf.name) + intf.link.core.log.Debugln("DEBUG: registered interface for", intf.name) } intf.link.mutex.Unlock() // Create peer From 05962b2cbd3d7f184f8903ffafc943bef001e2ff Mon Sep 17 00:00:00 2001 From: Arceliar Date: Wed, 30 Jan 2019 20:58:23 -0600 Subject: [PATCH 090/109] disable idle nodes in the switch instead of killing the connection entirely. this implementation is ugly, but i think it maybe works --- go.mod | 2 +- go.sum | 4 +-- src/yggdrasil/link.go | 64 +++++++++++++++++++++++++++++++++++++++---- 3 files changed, 62 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 3e8db51..995e54c 100644 --- a/go.mod +++ b/go.mod @@ -2,10 +2,10 @@ module github.com/yggdrasil-network/yggdrasil-go require ( github.com/docker/libcontainer v2.2.1+incompatible + github.com/gologme/log v0.0.0-20181207131047-4e5d8ccb38e8 github.com/hjson/hjson-go v0.0.0-20181010104306-a25ecf6bd222 github.com/kardianos/minwinsvc v0.0.0-20151122163309-cad6b2b879b0 github.com/mitchellh/mapstructure v1.1.2 - github.com/gologme/log v0.0.0-20181207131047-4e5d8ccb38e8 github.com/songgao/packets v0.0.0-20160404182456-549a10cd4091 github.com/yggdrasil-network/water v0.0.0-20180615095340-f732c88f34ae golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 diff --git a/go.sum b/go.sum index 17b1017..92dfe88 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/docker/libcontainer v2.2.1+incompatible h1:++SbbkCw+X8vAd4j2gOCzZ2Nn7s2xFALTf7LZKmM1/0= github.com/docker/libcontainer v2.2.1+incompatible/go.mod h1:osvj61pYsqhNCMLGX31xr7klUBhHb/ZBuXS0o1Fvwbw= +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/hjson/hjson-go v0.0.0-20181010104306-a25ecf6bd222 h1:xmvkbxXDeN1ffWq8kvrhyqVYAO2aXuRBsbpxVTR+JyU= github.com/hjson/hjson-go v0.0.0-20181010104306-a25ecf6bd222/go.mod h1:qsetwF8NlsTsOTwZTApNlTCerV+b2GjYRRcIk4JMFio= github.com/kardianos/minwinsvc v0.0.0-20151122163309-cad6b2b879b0 h1:YnZmFjg0Nvk8851WTVWlqMC1ecJH07Ctz+Ezxx4u54g= @@ -18,5 +20,3 @@ golang.org/x/sys v0.0.0-20181206074257-70b957f3b65e h1:njOxP/wVblhCLIUhjHXf6X+dz golang.org/x/sys v0.0.0-20181206074257-70b957f3b65e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -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= diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index 1e65847..320b3e6 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -146,7 +146,9 @@ func (intf *linkInterface) handler() error { intf.peer.close = func() { intf.msgIO.close() } go intf.peer.linkLoop() // Start the writer + signalReady := make(chan struct{}, 1) go func() { + defer close(signalReady) interval := 4 * time.Second timer := time.NewTimer(interval) clearTimer := func() { @@ -181,15 +183,63 @@ func (intf *linkInterface) handler() error { } intf.msgIO.writeMsg(msg) util.PutBytes(msg) - if true { - // TODO *don't* do this if we're not reading any traffic - // In such a case, the reader is responsible for resetting it the next time we read something - intf.link.core.switchTable.idleIn <- intf.peer.port + select { + case signalReady <- struct{}{}: + default: } } } }() - intf.link.core.switchTable.idleIn <- intf.peer.port // notify switch that we're idle + //intf.link.core.switchTable.idleIn <- intf.peer.port // notify switch that we're idle + // Used to enable/disable activity in the switch + signalAlive := make(chan struct{}, 1) + defer close(signalAlive) + go func() { + var isAlive bool + var isReady bool + interval := 6 * time.Second // TODO set to ReadTimeout from the config, reset if it gets changed + timer := time.NewTimer(interval) + clearTimer := func() { + if !timer.Stop() { + select { + case <-timer.C: + default: + } + } + } + defer clearTimer() + for { + clearTimer() + timer.Reset(interval) + select { + case _, ok := <-signalAlive: + if !ok { + return + } + if !isAlive { + isAlive = true + if !isReady { + // (Re-)enable in the switch + isReady = true + intf.link.core.switchTable.idleIn <- intf.peer.port + } + } + case _, ok := <-signalReady: + if !ok { + return + } + if !isAlive || !isReady { + // Disable in the switch + isReady = false + } else { + // Keep enabled in the switch + intf.link.core.switchTable.idleIn <- intf.peer.port + } + case <-timer.C: + isAlive = false + } + } + }() // Run reader loop for { msg, err := intf.msgIO.readMsg() @@ -199,6 +249,10 @@ func (intf *linkInterface) handler() error { if err != nil { return err } + select { + case signalAlive <- struct{}{}: + default: + } } //////////////////////////////////////////////////////////////////////////////// return nil From e36f88c75f8702442478b1e15544c602ccdbe72d Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 31 Jan 2019 23:18:02 +0000 Subject: [PATCH 091/109] Info logging when link connects/disconnects --- src/yggdrasil/link.go | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index 320b3e6..681cf81 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -3,10 +3,13 @@ package yggdrasil import ( "errors" "fmt" + "net" + "strings" "sync" //"sync/atomic" "time" + "github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/crypto" "github.com/yggdrasil-network/yggdrasil-go/src/util" ) @@ -143,7 +146,22 @@ func (intf *linkInterface) handler() error { out <- msg } intf.peer.linkOut = make(chan []byte, 1) - intf.peer.close = func() { intf.msgIO.close() } + intf.peer.close = func() { + intf.msgIO.close() + // Make output + themAddr := address.AddrForNodeID(crypto.GetNodeID(&intf.info.box)) + themAddrString := net.IP(themAddr[:]).String() + themString := fmt.Sprintf("%s@%s", themAddrString, intf.info.remote) + intf.link.core.log.Infof("Disconnected %s: %s, source %s", + strings.ToUpper(intf.info.linkType), themString, intf.info.local) + } + // Make output + themAddr := address.AddrForNodeID(crypto.GetNodeID(&intf.info.box)) + themAddrString := net.IP(themAddr[:]).String() + themString := fmt.Sprintf("%s@%s", themAddrString, intf.info.remote) + intf.link.core.log.Infof("Connected %s: %s, source %s", + strings.ToUpper(intf.info.linkType), themString, intf.info.local) + // Start the link loop go intf.peer.linkLoop() // Start the writer signalReady := make(chan struct{}, 1) From 432f93de894fcc21a3fb30a32fa0e053405796c7 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 31 Jan 2019 23:29:18 +0000 Subject: [PATCH 092/109] Check AllowedEncryptionPublicKeys --- src/yggdrasil/link.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index 681cf81..3b540d0 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -1,6 +1,7 @@ package yggdrasil import ( + "encoding/hex" "errors" "fmt" "net" @@ -104,6 +105,18 @@ func (intf *linkInterface) handler() error { intf.link.core.log.Errorln("Failed to connect to node: " + intf.name + " version: " + fmt.Sprintf("%d.%d", meta.ver, meta.minorVer)) return errors.New("failed to connect: wrong version") } + // Check if we're authorized to connect to this key / IP + if !intf.link.core.peers.isAllowedEncryptionPublicKey(&meta.box) { + // Allow unauthorized peers if they're link-local + raddrStr, _, _ := net.SplitHostPort(intf.info.remote) + raddr := net.ParseIP(raddrStr) + if !raddr.IsLinkLocalUnicast() { + intf.link.core.log.Debugf("%s connection to %s forbidden: AllowedEncryptionPublicKey does not contain key %s", + strings.ToUpper(intf.info.linkType), intf.info.remote, hex.EncodeToString(meta.box[:])) + intf.msgIO.close() + return nil + } + } // Check if we already have a link to this node intf.info.box = meta.box intf.info.sig = meta.sig From ec5f7d98790b246c3e5f0c5b143f5789ad895166 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 31 Jan 2019 23:47:20 +0000 Subject: [PATCH 093/109] Enforce AllowedEncryptionPublicKeys for all peers inc. link-local --- src/yggdrasil/link.go | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index 3b540d0..e52ff95 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -107,15 +107,10 @@ func (intf *linkInterface) handler() error { } // Check if we're authorized to connect to this key / IP if !intf.link.core.peers.isAllowedEncryptionPublicKey(&meta.box) { - // Allow unauthorized peers if they're link-local - raddrStr, _, _ := net.SplitHostPort(intf.info.remote) - raddr := net.ParseIP(raddrStr) - if !raddr.IsLinkLocalUnicast() { - intf.link.core.log.Debugf("%s connection to %s forbidden: AllowedEncryptionPublicKey does not contain key %s", - strings.ToUpper(intf.info.linkType), intf.info.remote, hex.EncodeToString(meta.box[:])) - intf.msgIO.close() - return nil - } + intf.link.core.log.Debugf("%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() + return nil } // Check if we already have a link to this node intf.info.box = meta.box From 43f798e82ea1cce63e23c1c2be46426fc25a1c46 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Fri, 1 Feb 2019 00:02:17 +0000 Subject: [PATCH 094/109] Check link-local in tcp.go, track direction in link.go, fix compile error for mobile.go --- src/yggdrasil/awdl.go | 4 ++-- src/yggdrasil/link.go | 35 +++++++++++++++++------------------ src/yggdrasil/mobile.go | 3 ++- src/yggdrasil/mobile_ios.go | 4 ++-- src/yggdrasil/tcp.go | 3 ++- 5 files changed, 25 insertions(+), 24 deletions(-) diff --git a/src/yggdrasil/awdl.go b/src/yggdrasil/awdl.go index e9e57a6..4236688 100644 --- a/src/yggdrasil/awdl.go +++ b/src/yggdrasil/awdl.go @@ -54,14 +54,14 @@ func (a *awdl) init(l *link) error { return nil } -func (a *awdl) create(name, local, remote string) (*awdlInterface, error) { +func (a *awdl) create(name, local, remote string, incoming bool) (*awdlInterface, error) { rwc := awdlReadWriteCloser{ fromAWDL: make(chan []byte, 1), toAWDL: make(chan []byte, 1), } s := stream{} s.init(rwc) - linkif, err := a.link.create(&s, name, "awdl", local, remote) + linkif, err := a.link.create(&s, name, "awdl", local, remote, incoming, true) if err != nil { return nil, err } diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index e52ff95..2d2155c 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -40,12 +40,14 @@ type linkInterfaceMsgIO interface { } type linkInterface struct { - name string - link *link - peer *peer - msgIO linkInterfaceMsgIO - info linkInfo - closed chan struct{} + name string + link *link + peer *peer + msgIO linkInterfaceMsgIO + info linkInfo + incoming bool + force bool + closed chan struct{} } func (l *link) init(c *Core) error { @@ -62,7 +64,7 @@ func (l *link) init(c *Core) error { return nil } -func (l *link) create(msgIO linkInterfaceMsgIO, name, linkType, local, remote string) (*linkInterface, error) { +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{ name: name, @@ -73,6 +75,8 @@ func (l *link) create(msgIO linkInterfaceMsgIO, name, linkType, local, remote st local: local, remote: remote, }, + incoming: incoming, + force: force, } //l.interfaces[intf.name] = &intf //go intf.start() @@ -106,7 +110,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.link.core.peers.isAllowedEncryptionPublicKey(&meta.box) { + if !intf.force && !intf.link.core.peers.isAllowedEncryptionPublicKey(&meta.box) { intf.link.core.log.Debugf("%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() @@ -154,19 +158,14 @@ func (intf *linkInterface) handler() error { out <- msg } intf.peer.linkOut = make(chan []byte, 1) - intf.peer.close = func() { - intf.msgIO.close() - // Make output - themAddr := address.AddrForNodeID(crypto.GetNodeID(&intf.info.box)) - themAddrString := net.IP(themAddr[:]).String() - themString := fmt.Sprintf("%s@%s", themAddrString, intf.info.remote) - intf.link.core.log.Infof("Disconnected %s: %s, source %s", - strings.ToUpper(intf.info.linkType), themString, intf.info.local) - } - // Make output themAddr := address.AddrForNodeID(crypto.GetNodeID(&intf.info.box)) themAddrString := net.IP(themAddr[:]).String() themString := fmt.Sprintf("%s@%s", themAddrString, intf.info.remote) + intf.peer.close = func() { + intf.msgIO.close() + intf.link.core.log.Infof("Disconnected %s: %s, source %s", + strings.ToUpper(intf.info.linkType), themString, intf.info.local) + } intf.link.core.log.Infof("Connected %s: %s, source %s", strings.ToUpper(intf.info.linkType), themString, intf.info.local) // Start the link loop diff --git a/src/yggdrasil/mobile.go b/src/yggdrasil/mobile.go index 52215f0..76fbe54 100644 --- a/src/yggdrasil/mobile.go +++ b/src/yggdrasil/mobile.go @@ -5,10 +5,11 @@ package yggdrasil import ( "encoding/hex" "encoding/json" - "log" "os" "time" + "github.com/gologme/log" + hjson "github.com/hjson/hjson-go" "github.com/mitchellh/mapstructure" "github.com/yggdrasil-network/yggdrasil-go/src/config" diff --git a/src/yggdrasil/mobile_ios.go b/src/yggdrasil/mobile_ios.go index c2ec63b..7b08999 100644 --- a/src/yggdrasil/mobile_ios.go +++ b/src/yggdrasil/mobile_ios.go @@ -29,8 +29,8 @@ func (nsl MobileLogger) Write(p []byte) (n int, err error) { return len(p), nil } -func (c *Core) AWDLCreateInterface(name, local, remote string) error { - if intf, err := c.link.awdl.create(name, local, remote); err != nil || intf == nil { +func (c *Core) AWDLCreateInterface(name, local, remote string, incoming bool) error { + if intf, err := c.link.awdl.create(name, local, remote, incoming); err != nil || intf == nil { c.log.Println("c.link.awdl.create:", err) return err } diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index 617aa22..979bc81 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -284,8 +284,9 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) { 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.core.link.create(&stream, name, "tcp", local, remote) + link, err := iface.core.link.create(&stream, name, "tcp", local, remote, incoming, remotelinklocal) if err != nil { iface.core.log.Println(err) panic(err) From b44a0f29f3ed3ed31323027eef864acf20240259 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sat, 2 Feb 2019 22:18:55 -0600 Subject: [PATCH 095/109] send an ack if we receive a packet and don't have any return traffic, keeping a legacy 4-second keep-alive in case there's no traffic at all to send (to be removed later, after nodes have upgraded), ideally we should either remove ReadTimeout or use it for the switch idle timeout instead --- src/util/util.go | 12 ++++++ src/yggdrasil/link.go | 85 +++++++++++++++++++++++++++---------------- 2 files changed, 65 insertions(+), 32 deletions(-) diff --git a/src/util/util.go b/src/util/util.go index 65e6d46..45be3b1 100644 --- a/src/util/util.go +++ b/src/util/util.go @@ -3,6 +3,7 @@ package util // These are misc. utility functions that didn't really fit anywhere else import "runtime" +import "time" // A wrapper around runtime.Gosched() so it doesn't need to be imported elsewhere. func Yield() { @@ -44,3 +45,14 @@ func PutBytes(bs []byte) { default: } } + +// This is a workaround to go's broken timer implementation +func TimerStop(t *time.Timer) bool { + if !t.Stop() { + select { + case <-t.C: + default: + } + } + return true +} diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index 2d2155c..a6c0ccc 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -20,6 +20,7 @@ type link struct { mutex sync.RWMutex // protects interfaces below interfaces map[linkInfo]*linkInterface awdl awdl // AWDL interface support + // TODO timeout (to remove from switch), read from config.ReadTimeout } type linkInfo struct { @@ -78,8 +79,6 @@ func (l *link) create(msgIO linkInterfaceMsgIO, name, linkType, local, remote st incoming: incoming, force: force, } - //l.interfaces[intf.name] = &intf - //go intf.start() return &intf, nil } @@ -172,41 +171,49 @@ func (intf *linkInterface) handler() error { go intf.peer.linkLoop() // Start the writer signalReady := make(chan struct{}, 1) + signalSent := make(chan struct{}, 1) + sendAck := make(chan struct{}, 1) go func() { defer close(signalReady) + defer close(signalSent) interval := 4 * time.Second - timer := time.NewTimer(interval) - clearTimer := func() { - if !timer.Stop() { - select { - case <-timer.C: - default: - } + tcpTimer := time.NewTimer(interval) // used for backwards compat with old tcp + defer util.TimerStop(tcpTimer) + send := func(bs []byte) { + intf.msgIO.writeMsg(bs) + select { + case signalSent <- struct{}{}: + default: } } - defer clearTimer() for { // First try to send any link protocol traffic select { case msg := <-intf.peer.linkOut: - intf.msgIO.writeMsg(msg) + send(msg) continue default: } // No protocol traffic to send, so reset the timer - clearTimer() - timer.Reset(interval) + util.TimerStop(tcpTimer) + tcpTimer.Reset(interval) // Now block until something is ready or the timer triggers keepalive traffic select { - case <-timer.C: - intf.msgIO.writeMsg(nil) + case <-tcpTimer.C: + intf.link.core.log.Debugf("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", + strings.ToUpper(intf.info.linkType), themString, intf.info.local) + send(nil) case msg := <-intf.peer.linkOut: intf.msgIO.writeMsg(msg) case msg, ok := <-out: if !ok { return } - intf.msgIO.writeMsg(msg) + send(msg) util.PutBytes(msg) select { case signalReady <- struct{}{}: @@ -217,27 +224,23 @@ func (intf *linkInterface) handler() error { }() //intf.link.core.switchTable.idleIn <- intf.peer.port // notify switch that we're idle // Used to enable/disable activity in the switch - signalAlive := make(chan struct{}, 1) + signalAlive := make(chan bool, 1) // True = real packet, false = keep-alive defer close(signalAlive) go func() { var isAlive bool var isReady bool - interval := 6 * time.Second // TODO set to ReadTimeout from the config, reset if it gets changed - timer := time.NewTimer(interval) - clearTimer := func() { - if !timer.Stop() { - select { - case <-timer.C: - default: - } - } - } - defer clearTimer() + var ackTimerRunning bool + timeout := 6 * time.Second // TODO set to ReadTimeout from the config, reset if it gets changed + ackTime := time.Second + timer := time.NewTimer(timeout) + defer util.TimerStop(timer) + ackTimer := time.NewTimer(ackTime) + defer util.TimerStop(ackTimer) for { - clearTimer() - timer.Reset(interval) + util.TimerStop(timer) + timer.Reset(timeout) select { - case _, ok := <-signalAlive: + case gotMsg, ok := <-signalAlive: if !ok { return } @@ -249,6 +252,24 @@ func (intf *linkInterface) handler() error { intf.link.core.switchTable.idleIn <- intf.peer.port } } + if gotMsg && !ackTimerRunning { + util.TimerStop(ackTimer) + ackTimer.Reset(ackTime) + ackTimerRunning = true + } + case _, ok := <-signalSent: + // Stop any running ack timer + if !ok { + return + } + util.TimerStop(ackTimer) + ackTimerRunning = false + case <-ackTimer.C: + // We haven't sent anything in the past ackTimeout, so signal a send of a nil packet + select { + case sendAck <- struct{}{}: + default: + } case _, ok := <-signalReady: if !ok { return @@ -275,7 +296,7 @@ func (intf *linkInterface) handler() error { return err } select { - case signalAlive <- struct{}{}: + case signalAlive <- len(msg) > 0: default: } } From ebbe5f67ad5a988e435aaf3d7df8390a7eab8da5 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sat, 2 Feb 2019 22:41:51 -0600 Subject: [PATCH 096/109] don't time out a link unless we were expecting an ack and didn't get one --- src/yggdrasil/link.go | 57 +++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index a6c0ccc..6704c91 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -171,7 +171,7 @@ func (intf *linkInterface) handler() error { go intf.peer.linkLoop() // Start the writer signalReady := make(chan struct{}, 1) - signalSent := make(chan struct{}, 1) + signalSent := make(chan bool, 1) sendAck := make(chan struct{}, 1) go func() { defer close(signalReady) @@ -182,7 +182,7 @@ func (intf *linkInterface) handler() error { send := func(bs []byte) { intf.msgIO.writeMsg(bs) select { - case signalSent <- struct{}{}: + case signalSent <- len(bs) > 0: default: } } @@ -229,16 +229,15 @@ func (intf *linkInterface) handler() error { go func() { var isAlive bool var isReady bool - var ackTimerRunning bool - timeout := 6 * time.Second // TODO set to ReadTimeout from the config, reset if it gets changed - ackTime := time.Second - timer := time.NewTimer(timeout) - defer util.TimerStop(timer) - ackTimer := time.NewTimer(ackTime) - defer util.TimerStop(ackTimer) + var sendTimerRunning bool + var recvTimerRunning bool + recvTime := 6 * time.Second // TODO set to ReadTimeout from the config, reset if it gets changed + sendTime := time.Second + sendTimer := time.NewTimer(sendTime) + defer util.TimerStop(sendTimer) + recvTimer := time.NewTimer(recvTime) + defer util.TimerStop(recvTimer) for { - util.TimerStop(timer) - timer.Reset(timeout) select { case gotMsg, ok := <-signalAlive: if !ok { @@ -252,23 +251,26 @@ func (intf *linkInterface) handler() error { intf.link.core.switchTable.idleIn <- intf.peer.port } } - if gotMsg && !ackTimerRunning { - util.TimerStop(ackTimer) - ackTimer.Reset(ackTime) - ackTimerRunning = true + if gotMsg && !sendTimerRunning { + // We got a message + // Start a timer, if it expires then send a 0-sized ack to let them know we're alive + util.TimerStop(sendTimer) + sendTimer.Reset(sendTime) + sendTimerRunning = true } - case _, ok := <-signalSent: + case sentMsg, ok := <-signalSent: // Stop any running ack timer if !ok { return } - util.TimerStop(ackTimer) - ackTimerRunning = false - case <-ackTimer.C: - // We haven't sent anything in the past ackTimeout, so signal a send of a nil packet - select { - case sendAck <- struct{}{}: - default: + util.TimerStop(sendTimer) + sendTimerRunning = false + if sentMsg && !recvTimerRunning { + // We sent a message + // Start a timer, if it expires and we haven't gotten any return traffic (including a 0-sized ack), then assume there's a problem + util.TimerStop(recvTimer) + recvTimer.Reset(recvTime) + recvTimerRunning = true } case _, ok := <-signalReady: if !ok { @@ -281,7 +283,14 @@ func (intf *linkInterface) handler() error { // Keep enabled in the switch intf.link.core.switchTable.idleIn <- intf.peer.port } - case <-timer.C: + case <-sendTimer.C: + // We haven't sent anything, so signal a send of a 0 packet to let them know we're alive + select { + case sendAck <- struct{}{}: + default: + } + 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 } } From 5ddf84f32991ae8bda3361cbbbea7c46294e97d3 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sun, 3 Feb 2019 15:22:14 -0600 Subject: [PATCH 097/109] remove peers completely after a long switch timeout, this could use some improvement later --- src/yggdrasil/switch.go | 1 + 1 file changed, 1 insertion(+) diff --git a/src/yggdrasil/switch.go b/src/yggdrasil/switch.go index f2adf3f..db39d01 100644 --- a/src/yggdrasil/switch.go +++ b/src/yggdrasil/switch.go @@ -281,6 +281,7 @@ func (t *switchTable) cleanPeers() { if now.Sub(peer.time) > switch_timeout+switch_throttle { // Longer than switch_timeout to make sure we don't remove a working peer because the root stopped responding. delete(t.data.peers, port) + go t.core.peers.removePeer(port) // TODO figure out if it's safe to do this without a goroutine, or make it safe } } if _, isIn := t.data.peers[t.parent]; !isIn { From 2f8dd5dde01c75ff76e01157fb77ddeec1e03053 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sun, 3 Feb 2019 15:50:25 -0600 Subject: [PATCH 098/109] remove race condition in setting peer.close by requiring it as an argument to newPeer --- src/yggdrasil/debug.go | 4 +--- src/yggdrasil/link.go | 9 +++------ src/yggdrasil/peer.go | 3 ++- src/yggdrasil/router.go | 2 +- 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/yggdrasil/debug.go b/src/yggdrasil/debug.go index f7319e4..94faba4 100644 --- a/src/yggdrasil/debug.go +++ b/src/yggdrasil/debug.go @@ -97,9 +97,7 @@ func (c *Core) DEBUG_getPeers() *peers { } func (ps *peers) DEBUG_newPeer(box crypto.BoxPubKey, sig crypto.SigPubKey, link crypto.BoxSharedKey) *peer { - //in <-chan []byte, - //out chan<- []byte) *peer { - return ps.newPeer(&box, &sig, &link, "(simulator)") //, in, out) + return ps.newPeer(&box, &sig, &link, "(simulator)", nil) } /* diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index 6704c91..be98dd9 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -141,7 +141,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) + intf.peer = intf.link.core.peers.newPeer(&meta.box, &meta.sig, shared, intf.name, func() { intf.msgIO.close() }) if intf.peer == nil { return errors.New("failed to create peer") } @@ -160,13 +160,10 @@ func (intf *linkInterface) handler() error { themAddr := address.AddrForNodeID(crypto.GetNodeID(&intf.info.box)) themAddrString := net.IP(themAddr[:]).String() themString := fmt.Sprintf("%s@%s", themAddrString, intf.info.remote) - intf.peer.close = func() { - intf.msgIO.close() - intf.link.core.log.Infof("Disconnected %s: %s, source %s", - strings.ToUpper(intf.info.linkType), themString, intf.info.local) - } intf.link.core.log.Infof("Connected %s: %s, source %s", strings.ToUpper(intf.info.linkType), themString, intf.info.local) + defer intf.link.core.log.Infof("Disconnected %s: %s, source %s", + strings.ToUpper(intf.info.linkType), themString, intf.info.local) // Start the link loop go intf.peer.linkLoop() // Start the writer diff --git a/src/yggdrasil/peer.go b/src/yggdrasil/peer.go index ad54fbc..237d6f6 100644 --- a/src/yggdrasil/peer.go +++ b/src/yggdrasil/peer.go @@ -113,7 +113,7 @@ 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) *peer { +func (ps *peers) newPeer(box *crypto.BoxPubKey, sig *crypto.SigPubKey, linkShared *crypto.BoxSharedKey, endpoint string, closer func()) *peer { now := time.Now() p := peer{box: *box, sig: *sig, @@ -123,6 +123,7 @@ func (ps *peers) newPeer(box *crypto.BoxPubKey, sig *crypto.SigPubKey, linkShare firstSeen: now, doSend: make(chan struct{}, 1), dinfo: make(chan *dhtInfo, 1), + close: closer, core: ps.core} ps.mutex.Lock() defer ps.mutex.Unlock() diff --git a/src/yggdrasil/router.go b/src/yggdrasil/router.go index d505936..99e6982 100644 --- a/src/yggdrasil/router.go +++ b/src/yggdrasil/router.go @@ -67,7 +67,7 @@ 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, 32) // TODO something better than this... - p := r.core.peers.newPeer(&r.core.boxPub, &r.core.sigPub, &crypto.BoxSharedKey{}, "(self)") + p := r.core.peers.newPeer(&r.core.boxPub, &r.core.sigPub, &crypto.BoxSharedKey{}, "(self)", nil) p.out = func(packet []byte) { // This is to make very sure it never blocks select { From ebdd968c246e281f73133d83b0b072464b133050 Mon Sep 17 00:00:00 2001 From: Mikhail Novosyolov Date: Tue, 5 Feb 2019 14:15:17 +0300 Subject: [PATCH 099/109] Fail build script if building of any target fails E.g, I had a build error of yggdrasil, but ./build returned exit code 0: + ./build -t -l -linkmode=external Building: yggdrasil github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil /home/user/go/src/github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil/multicast.go:39:9: undefined: net.ListenConfig Building: yggdrasilctl + exit 0 --- build | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build b/build index 7ceb4c9..f6c7246 100755 --- a/build +++ b/build @@ -1,5 +1,7 @@ #!/bin/sh +set -ef + PKGSRC=${PKGSRC:-github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil} PKGNAME=${PKGNAME:-$(sh contrib/semver/name.sh)} PKGVER=${PKGVER:-$(sh contrib/semver/version.sh --bare)} From 41f49faaa0b86b6b42420eb2bcc384dc3af0df51 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Tue, 5 Feb 2019 17:39:59 -0600 Subject: [PATCH 100/109] get code running in the netns test again, remove unnecessary allocations that were found in profiling --- misc/run-schannel-netns | 12 ++++++------ src/yggdrasil/admin.go | 6 +++++- src/yggdrasil/stream.go | 16 ++++++++-------- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/misc/run-schannel-netns b/misc/run-schannel-netns index 9723e73..74a0294 100755 --- a/misc/run-schannel-netns +++ b/misc/run-schannel-netns @@ -51,12 +51,12 @@ ip netns exec node4 ip link set lo up ip netns exec node5 ip link set lo up ip netns exec node6 ip link set lo up -ip netns exec node1 env PPROFLISTEN=localhost:6060 ./run --autoconf &> /dev/null & -ip netns exec node2 env PPROFLISTEN=localhost:6060 ./run --autoconf &> /dev/null & -ip netns exec node3 env PPROFLISTEN=localhost:6060 ./run --autoconf &> /dev/null & -ip netns exec node4 env PPROFLISTEN=localhost:6060 ./run --autoconf &> /dev/null & -ip netns exec node5 env PPROFLISTEN=localhost:6060 ./run --autoconf &> /dev/null & -ip netns exec node6 env PPROFLISTEN=localhost:6060 ./run --autoconf &> /dev/null & +echo '{AdminListen: "none"}' | ip netns exec node1 env PPROFLISTEN=localhost:6060 ./yggdrasil --useconf &> /dev/null & +echo '{AdminListen: "none"}' | ip netns exec node2 env PPROFLISTEN=localhost:6060 ./yggdrasil --useconf &> /dev/null & +echo '{AdminListen: "none"}' | ip netns exec node3 env PPROFLISTEN=localhost:6060 ./yggdrasil --useconf &> /dev/null & +echo '{AdminListen: "none"}' | ip netns exec node4 env PPROFLISTEN=localhost:6060 ./yggdrasil --useconf &> /dev/null & +echo '{AdminListen: "none"}' | ip netns exec node5 env PPROFLISTEN=localhost:6060 ./yggdrasil --useconf &> /dev/null & +echo '{AdminListen: "none"}' | ip netns exec node6 env PPROFLISTEN=localhost:6060 ./yggdrasil --useconf &> /dev/null & echo "Started, to continue you should (possibly w/ sudo):" echo "kill" $(jobs -p) diff --git a/src/yggdrasil/admin.go b/src/yggdrasil/admin.go index b54ac5c..f8347f0 100644 --- a/src/yggdrasil/admin.go +++ b/src/yggdrasil/admin.go @@ -388,7 +388,11 @@ func (a *admin) start() error { // cleans up when stopping func (a *admin) close() error { - return a.listener.Close() + if a.listener != nil { + return a.listener.Close() + } else { + return nil + } } // listen is run by start and manages API connections. diff --git a/src/yggdrasil/stream.go b/src/yggdrasil/stream.go index db2cdf7..4d73844 100644 --- a/src/yggdrasil/stream.go +++ b/src/yggdrasil/stream.go @@ -12,8 +12,10 @@ import ( var _ = linkInterfaceMsgIO(&stream{}) type stream struct { - rwc io.ReadWriteCloser - inputBuffer []byte // Incoming packet stream + rwc io.ReadWriteCloser + inputBuffer []byte // Incoming packet stream + frag [2 * streamMsgSize]byte // Temporary data read off the underlying rwc, on its way to the inputBuffer + outputBuffer [2 * streamMsgSize]byte // Temporary data about to be written to the rwc } func (s *stream) close() error { @@ -32,10 +34,9 @@ func (s *stream) init(rwc io.ReadWriteCloser) { // writeMsg writes a message with stream padding, and is *not* thread safe. func (s *stream) writeMsg(bs []byte) (int, error) { - buf := util.GetBytes() - defer util.PutBytes(buf) + buf := s.outputBuffer[:0] buf = append(buf, streamMsg[:]...) - buf = append(buf, wire_encode_uint64(uint64(len(bs)))...) + buf = wire_put_uint64(uint64(len(bs)), buf) padLen := len(buf) buf = append(buf, bs...) var bn int @@ -69,10 +70,9 @@ func (s *stream) readMsg() ([]byte, error) { return msg, nil default: // Wait for the underlying reader to return enough info for us to proceed - frag := make([]byte, 2*streamMsgSize) - n, err := s.rwc.Read(frag) + n, err := s.rwc.Read(s.frag[:]) if n > 0 { - s.inputBuffer = append(s.inputBuffer, frag[:n]...) + s.inputBuffer = append(s.inputBuffer, s.frag[:n]...) } else if err != nil { return nil, err } From ad43558fbb312e58614ca03f530ca0ace6185da9 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sat, 9 Feb 2019 15:30:17 -0600 Subject: [PATCH 101/109] fix bug in switch time --- src/yggdrasil/switch.go | 1 + 1 file changed, 1 insertion(+) diff --git a/src/yggdrasil/switch.go b/src/yggdrasil/switch.go index db39d01..9d1f870 100644 --- a/src/yggdrasil/switch.go +++ b/src/yggdrasil/switch.go @@ -415,6 +415,7 @@ func (t *switchTable) unlockedHandleMsg(msg *switchMsg, fromPort switchPort, rep // Update the matrix of peer "faster" thresholds if reprocessing { sender.faster = oldSender.faster + sender.time = oldSender.time } else { sender.faster = make(map[switchPort]uint64, len(oldSender.faster)) for port, peer := range t.data.peers { From 0ca64b0abe4735a91f8440b45a9b4e7088cd8c98 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 10 Feb 2019 12:13:49 +0000 Subject: [PATCH 102/109] Remove ReadTimeout configuration option --- cmd/yggdrasil/main.go | 5 +++-- src/config/config.go | 1 - src/yggdrasil/tcp.go | 6 ------ 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index f67bbce..aa5a749 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -77,6 +77,7 @@ func readConfig(useconf *bool, useconffile *string, normaliseconf *bool) *nodeCo // names have changed recently. changes := map[string]string{ "Multicast": "", + "ReadTimeout": "", "LinkLocal": "MulticastInterfaces", "BoxPub": "EncryptionPublicKey", "BoxPriv": "EncryptionPrivateKey", @@ -89,11 +90,11 @@ func readConfig(useconf *bool, useconffile *string, normaliseconf *bool) *nodeCo if _, ok := dat[from]; ok { if to == "" { if !*normaliseconf { - log.Println("Warning: Deprecated config option", from, "- please remove") + log.Println("Warning: Config option", from, "is deprecated") } } else { if !*normaliseconf { - log.Println("Warning: Deprecated config option", from, "- please rename to", to) + log.Println("Warning: Config option", from, "has been renamed - please change to", to) } // If the configuration file doesn't already contain a line with the // new name then set it to the old value. This makes sure that we diff --git a/src/config/config.go b/src/config/config.go index 192b94e..14b1649 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -16,7 +16,6 @@ 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."` - ReadTimeout int32 `comment:"Read timeout for connections, specified in milliseconds. If less\nthan 6000 and not negative, 6000 (the default) is used. If negative,\nreads won't time out."` 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."` 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!"` diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index 979bc81..c65e2e6 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -36,7 +36,6 @@ type tcpInterface struct { reconfigure chan chan error serv net.Listener stop chan bool - timeout time.Duration addr string mutex sync.Mutex // Protecting the below calls map[string]struct{} @@ -106,13 +105,8 @@ func (iface *tcpInterface) listen() error { iface.core.configMutex.RLock() iface.addr = iface.core.config.Listen - iface.timeout = time.Duration(iface.core.config.ReadTimeout) * time.Millisecond iface.core.configMutex.RUnlock() - if iface.timeout >= 0 && iface.timeout < default_timeout { - iface.timeout = default_timeout - } - ctx := context.Background() lc := net.ListenConfig{ Control: iface.tcpContext, From 9f7609817e146088dd6dbf192b621808a48f4ef8 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 10 Feb 2019 12:22:39 +0000 Subject: [PATCH 103/109] Remove yggdrasil-resume.service from systemd contrib and Debian package --- contrib/deb/generate.sh | 2 -- contrib/systemd/yggdrasil-resume.service | 10 ---------- contrib/systemd/yggdrasil.service | 1 - 3 files changed, 13 deletions(-) delete mode 100644 contrib/systemd/yggdrasil-resume.service diff --git a/contrib/deb/generate.sh b/contrib/deb/generate.sh index 6c8f955..6879a2d 100644 --- a/contrib/deb/generate.sh +++ b/contrib/deb/generate.sh @@ -110,12 +110,10 @@ EOF cp yggdrasil /tmp/$PKGNAME/usr/bin/ cp yggdrasilctl /tmp/$PKGNAME/usr/bin/ cp contrib/systemd/yggdrasil.service /tmp/$PKGNAME/etc/systemd/system/ -cp contrib/systemd/yggdrasil-resume.service /tmp/$PKGNAME/etc/systemd/system/ tar -czvf /tmp/$PKGNAME/data.tar.gz -C /tmp/$PKGNAME/ \ usr/bin/yggdrasil usr/bin/yggdrasilctl \ etc/systemd/system/yggdrasil.service \ - etc/systemd/system/yggdrasil-resume.service tar -czvf /tmp/$PKGNAME/control.tar.gz -C /tmp/$PKGNAME/debian . echo 2.0 > /tmp/$PKGNAME/debian-binary diff --git a/contrib/systemd/yggdrasil-resume.service b/contrib/systemd/yggdrasil-resume.service deleted file mode 100644 index c725127..0000000 --- a/contrib/systemd/yggdrasil-resume.service +++ /dev/null @@ -1,10 +0,0 @@ -[Unit] -Description=Restart yggdrasil on resume from sleep -After=sleep.target - -[Service] -Type=oneshot -ExecStart=/bin/systemctl restart yggdrasil - -[Install] -WantedBy=sleep.target diff --git a/contrib/systemd/yggdrasil.service b/contrib/systemd/yggdrasil.service index 1b6f1f0..6adf1ef 100644 --- a/contrib/systemd/yggdrasil.service +++ b/contrib/systemd/yggdrasil.service @@ -18,4 +18,3 @@ Restart=always [Install] WantedBy=multi-user.target -Also=yggdrasil-resume.service From fe09c234bc89aa79befb42aa13dcb79c129e4c44 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 10 Feb 2019 12:29:04 +0000 Subject: [PATCH 104/109] Fix Debian generate.sh --- contrib/deb/generate.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/deb/generate.sh b/contrib/deb/generate.sh index 6879a2d..4433a9d 100644 --- a/contrib/deb/generate.sh +++ b/contrib/deb/generate.sh @@ -113,7 +113,7 @@ cp contrib/systemd/yggdrasil.service /tmp/$PKGNAME/etc/systemd/system/ tar -czvf /tmp/$PKGNAME/data.tar.gz -C /tmp/$PKGNAME/ \ usr/bin/yggdrasil usr/bin/yggdrasilctl \ - etc/systemd/system/yggdrasil.service \ + etc/systemd/system/yggdrasil.service tar -czvf /tmp/$PKGNAME/control.tar.gz -C /tmp/$PKGNAME/debian . echo 2.0 > /tmp/$PKGNAME/debian-binary From 30320801d380d3e90b32adee53361106b4b3aa31 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 10 Feb 2019 15:23:49 +0000 Subject: [PATCH 105/109] Don't build 32-bit builds for macOS --- .circleci/config.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 8725e44..82a68aa 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -48,14 +48,12 @@ jobs: command: | rm -f {yggdrasil,yggdrasilctl} GOOS=darwin GOARCH=amd64 ./build && mv yggdrasil /tmp/upload/$CINAME-$CIVERSION-darwin-amd64 && mv yggdrasilctl /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-darwin-amd64; - GOOS=darwin GOARCH=386 ./build && mv yggdrasil /tmp/upload/$CINAME-$CIVERSION-darwin-i386 && mv yggdrasilctl /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-darwin-i386; - run: name: Build for macOS (.pkg format) command: | rm -rf {yggdrasil,yggdrasilctl} GOOS=darwin GOARCH=amd64 ./build && PKGARCH=amd64 sh contrib/macos/create-pkg.sh && mv *.pkg /tmp/upload/ - GOOS=darwin GOARCH=386 ./build && PKGARCH=i386 sh contrib/macos/create-pkg.sh && mv *.pkg /tmp/upload/ - run: name: Build for OpenBSD From 6f0bbbfb987c00e79a9eea460b0436c76928983f Mon Sep 17 00:00:00 2001 From: Arceliar Date: Fri, 15 Feb 2019 19:35:10 -0600 Subject: [PATCH 106/109] Debug some issues with the state machine that tracks idle connections in link.go --- src/yggdrasil/link.go | 26 ++++++++++++++++++-------- src/yggdrasil/switch.go | 1 + 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index be98dd9..ad4b1fa 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -216,6 +216,8 @@ func (intf *linkInterface) handler() error { case signalReady <- struct{}{}: default: } + intf.link.core.log.Debugf("Sending packet to %s: %s, source %s", + strings.ToUpper(intf.info.linkType), themString, intf.info.local) } } }() @@ -235,18 +237,21 @@ func (intf *linkInterface) handler() error { recvTimer := time.NewTimer(recvTime) defer util.TimerStop(recvTimer) for { + intf.link.core.log.Debugf("State of %s: %s, source %s :: isAlive %t isReady %t sendTimerRunning %t recvTimerRunning %t", + strings.ToUpper(intf.info.linkType), themString, intf.info.local, + isAlive, isReady, sendTimerRunning, recvTimerRunning) select { case gotMsg, ok := <-signalAlive: if !ok { return } - if !isAlive { - isAlive = true - if !isReady { - // (Re-)enable in the switch - isReady = true - intf.link.core.switchTable.idleIn <- intf.peer.port - } + util.TimerStop(recvTimer) + recvTimerRunning = false + isAlive = true + if !isReady { + // (Re-)enable in the switch + intf.link.core.switchTable.idleIn <- intf.peer.port + isReady = true } if gotMsg && !sendTimerRunning { // We got a message @@ -255,6 +260,10 @@ func (intf *linkInterface) handler() error { sendTimer.Reset(sendTime) sendTimerRunning = true } + if !gotMsg { + intf.link.core.log.Debugf("Received ack from %s: %s, source %s", + strings.ToUpper(intf.info.linkType), themString, intf.info.local) + } case sentMsg, ok := <-signalSent: // Stop any running ack timer if !ok { @@ -273,12 +282,13 @@ func (intf *linkInterface) handler() error { if !ok { return } - if !isAlive || !isReady { + if !isAlive { // Disable in the switch isReady = false } else { // Keep enabled in the switch intf.link.core.switchTable.idleIn <- intf.peer.port + isReady = true } case <-sendTimer.C: // We haven't sent anything, so signal a send of a 0 packet to let them know we're alive diff --git a/src/yggdrasil/switch.go b/src/yggdrasil/switch.go index 9d1f870..a2877eb 100644 --- a/src/yggdrasil/switch.go +++ b/src/yggdrasil/switch.go @@ -780,6 +780,7 @@ func (t *switchTable) doWorker() { t.queues.bufs = make(map[string]switch_buffer) // Packets per PacketStreamID (string) idle := make(map[switchPort]struct{}) // this is to deduplicate things for { + t.core.log.Debugf("Switch state: idle = %d, buffers = %d", len(idle), len(t.queues.bufs)) select { case bytes := <-t.packetIn: // Try to send it somewhere (or drop it if it's corrupt or at a dead end) From 957248b3dded3133cea5d134f1eb1a0bed68c36a Mon Sep 17 00:00:00 2001 From: Arceliar Date: Fri, 15 Feb 2019 20:23:28 -0600 Subject: [PATCH 107/109] add twolink test for namespaces with multiple links with different bandwidth --- misc/run-twolink-test | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100755 misc/run-twolink-test diff --git a/misc/run-twolink-test b/misc/run-twolink-test new file mode 100755 index 0000000..987b6de --- /dev/null +++ b/misc/run-twolink-test @@ -0,0 +1,33 @@ +#!/bin/bash + +# Connects nodes in two namespaces by two links with different bandwidth (10mbit and 100mbit) + +ip netns add node1 +ip netns add node2 + +ip link add veth11 type veth peer name veth21 +ip link set veth11 netns node1 up +ip link set veth21 netns node2 up + +ip link add veth12 type veth peer name veth22 +ip link set veth12 netns node1 up +ip link set veth22 netns node2 up + +ip netns exec node1 tc qdisc add dev veth11 root tbf rate 10mbit burst 8192 latency 1ms +ip netns exec node2 tc qdisc add dev veth21 root tbf rate 10mbit burst 8192 latency 1ms + +ip netns exec node1 tc qdisc add dev veth12 root tbf rate 100mbit burst 8192 latency 1ms +ip netns exec node2 tc qdisc add dev veth22 root tbf rate 100mbit burst 8192 latency 1ms + +echo '{AdminListen: "unix://node1.sock"}' | ip netns exec node1 env PPROFLISTEN=localhost:6060 ./yggdrasil -logging "info,warn,error,debug" -useconf &> node1.log & +echo '{AdminListen: "unix://node2.sock"}' | ip netns exec node2 env PPROFLISTEN=localhost:6060 ./yggdrasil -logging "info,warn,error,debug" -useconf &> node2.log & + +echo "Started, to continue you should (possibly w/ sudo):" +echo "kill" $(jobs -p) +wait + +ip netns delete node1 +ip netns delete node2 + +ip link delete veth11 +ip link delete veth12 From 1192ceaf68eac34c5c99dc39f2e864c7dc3d3885 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 18 Feb 2019 18:10:05 +0000 Subject: [PATCH 108/109] Update CHANGELOG.md --- CHANGELOG.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 84b5c38..6c290c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,27 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - in case of vulnerabilities. --> +## [0.3.3] - 2018-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 +- Connection contexts used for TCP connections which allow more exotic socket options to be set, e.g. + - Reusing the multicast socket to allow multiple running Yggdrasil instances without having to disable multicast + - Allowing supported Macs to peer with other nearby Macs that aren't even on the same Wi-Fi network using AWDL +- Flexible logging support, which allows for logging at different levels of verbosity + +### Changed +- Switch changes to improve parent selection +- Node configuration is now stored centrally, rather than having fragments/copies distributed at startup time +- Significant refactoring in various areas, including for link types (TCP, AWDL etc), generic streams and adapters +- macOS builds through CircleCI are now 64-bit only + +### Fixed +- Simplified `systemd` service now in `contrib` + +### Removed +- `ReadTimeout` option is now deprecated + ## [0.3.2] - 2018-12-26 ### Added - The admin socket is now multithreaded, greatly improving performance of the crawler and allowing concurrent lookups to take place From 24cf4b9d2b6aed148ad1e07ae0d8317eefb780e8 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 18 Feb 2019 22:31:34 +0000 Subject: [PATCH 109/109] Add ExecReload for SIGHUP --- contrib/systemd/yggdrasil.service | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/systemd/yggdrasil.service b/contrib/systemd/yggdrasil.service index 6adf1ef..bd52ec9 100644 --- a/contrib/systemd/yggdrasil.service +++ b/contrib/systemd/yggdrasil.service @@ -14,6 +14,7 @@ ExecStartPre=/bin/sh -ec "if ! test -s /etc/yggdrasil.conf; \ echo 'WARNING: A new /etc/yggdrasil.conf file has been generated.'; \ fi" ExecStart=/usr/bin/yggdrasil -useconffile /etc/yggdrasil.conf +ExecReload=/bin/kill -HUP $MAINPID Restart=always [Install]