From 74ac535d550d30577dcefa8bc6d397356e6f65e2 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Fri, 8 Feb 2019 19:46:11 -0600 Subject: [PATCH 01/20] slightly faster switch logic, should be easier to have a useful tie-breaker for peers that are equally close to the destination via the tree metric --- misc/sim/treesim.go | 4 +++- src/yggdrasil/switch.go | 45 +++++++++++++++++++++-------------------- 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/misc/sim/treesim.go b/misc/sim/treesim.go index f4cd75fa..a62f9ff4 100644 --- a/misc/sim/treesim.go +++ b/misc/sim/treesim.go @@ -6,13 +6,15 @@ import "os" import "strings" import "strconv" import "time" -import "log" import "runtime" import "runtime/pprof" import "flag" +import "github.com/gologme/log" + import . "github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil" +import . "github.com/yggdrasil-network/yggdrasil-go/src/crypto" //////////////////////////////////////////////////////////////////////////////// diff --git a/src/yggdrasil/switch.go b/src/yggdrasil/switch.go index db39d010..b777d899 100644 --- a/src/yggdrasil/switch.go +++ b/src/yggdrasil/switch.go @@ -569,23 +569,23 @@ func (t *switchTable) start() error { return nil } -// Check if a packet should go to the self node -// This means there's no node closer to the destination than us -// This is mainly used to identify packets addressed to us, or that hit a blackhole -func (t *switchTable) selfIsClosest(dest []byte) bool { +// Return a map of ports onto distance, keeping only ports closer to the destination than this node +// If the map is empty (or nil), then no peer is closer +func (t *switchTable) getCloser(dest []byte) map[switchPort]int { table := t.getTable() myDist := table.self.dist(dest) if myDist == 0 { // Skip the iteration step if it's impossible to be closer - return true + return nil } + closer := make(map[switchPort]int, len(table.elems)) for _, info := range table.elems { dist := info.locator.dist(dest) if dist < myDist { - return false + closer[info.port] = dist } } - return true + return closer } // Returns true if the peer is closer to the destination than ourself @@ -639,25 +639,26 @@ func (t *switchTable) bestPortForCoords(coords []byte) switchPort { func (t *switchTable) handleIn(packet []byte, idle map[switchPort]struct{}) bool { coords := switch_getPacketCoords(packet) ports := t.core.peers.getPorts() - if t.selfIsClosest(coords) { + closer := t.getCloser(coords) + if len(closer) == 0 { // TODO? call the router directly, and remove the whole concept of a self peer? ports[0].sendPacket(packet) return true } - table := t.getTable() - myDist := table.self.dist(coords) var best *peer - bestDist := myDist - for port := range idle { - if to := ports[port]; to != nil { - if info, isIn := table.elems[to.port]; isIn { - dist := info.locator.dist(coords) - if !(dist < bestDist) { - continue - } - best = to - bestDist = dist - } + var bestDist int + for port, dist := range closer { + to := ports[port] + _, isIdle := idle[port] + switch { + case to == nil: // skip + case !isIdle: // skip + case best == nil: // keep + fallthrough + case dist < bestDist: // keep + best = to + bestDist = dist + default: // skip } } if best != nil { @@ -696,7 +697,7 @@ func (b *switch_buffers) cleanup(t *switchTable) { // Remove queues for which we have no next hop packet := buf.packets[0] coords := switch_getPacketCoords(packet.bytes) - if t.selfIsClosest(coords) { + if len(t.getCloser(coords)) == 0 { for _, packet := range buf.packets { util.PutBytes(packet.bytes) } From 21cecf4630bf365a400ada8aa8fdcfa696029830 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sat, 9 Feb 2019 17:44:25 -0600 Subject: [PATCH 02/20] consistently prioritize which peer to forward to instead of letting it be partly random --- src/yggdrasil/switch.go | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/src/yggdrasil/switch.go b/src/yggdrasil/switch.go index b777d899..cc989d97 100644 --- a/src/yggdrasil/switch.go +++ b/src/yggdrasil/switch.go @@ -645,20 +645,39 @@ func (t *switchTable) handleIn(packet []byte, idle map[switchPort]struct{}) bool ports[0].sendPacket(packet) return true } + table := t.getTable() var best *peer var bestDist int + var bestCoordLen int for port, dist := range closer { to := ports[port] _, isIdle := idle[port] + coordLen := len(table.elems[port].locator.coords) + var update bool switch { - case to == nil: // skip - case !isIdle: // skip - case best == nil: // keep - fallthrough - case dist < bestDist: // keep + case to == nil: + //nothing + case !isIdle: + //nothing + case best == nil: + update = true + case dist < bestDist: + update = true + case dist > bestDist: + //nothing + case coordLen < bestCoordLen: + update = true + case coordLen > bestCoordLen: + //nothing + case port < best.port: + update = true + default: + //nothing + } + if update { best = to bestDist = dist - default: // skip + bestCoordLen = coordLen } } if best != nil { From 6f0bbbfb987c00e79a9eea460b0436c76928983f Mon Sep 17 00:00:00 2001 From: Arceliar Date: Fri, 15 Feb 2019 19:35:10 -0600 Subject: [PATCH 03/20] 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 be98dd92..ad4b1fac 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 9d1f870a..a2877ebe 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 04/20] 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 00000000..987b6de8 --- /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 05/20] Update CHANGELOG.md --- CHANGELOG.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 84b5c38b..6c290c89 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 06/20] 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 6adf1ef5..bd52ec9f 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] From 042adb0516e4516f0d316b786ff611bbb5da5ad8 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sat, 23 Feb 2019 00:07:00 -0600 Subject: [PATCH 07/20] make sure the only place traffic is ever dropped is in the switch. this currently disables the dedicated crypto workers --- src/yggdrasil/link.go | 10 +++++----- src/yggdrasil/router.go | 41 ++++++++++++++++++++++++++++++---------- src/yggdrasil/session.go | 4 +++- src/yggdrasil/switch.go | 12 +++++++----- 4 files changed, 46 insertions(+), 21 deletions(-) diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index ad4b1fac..3cd344d5 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -216,8 +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) + //intf.link.core.log.Debugf("Sending packet to %s: %s, source %s", + // strings.ToUpper(intf.info.linkType), themString, intf.info.local) } } }() @@ -237,9 +237,9 @@ 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) + //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 { diff --git a/src/yggdrasil/router.go b/src/yggdrasil/router.go index 99e69828..d84b935b 100644 --- a/src/yggdrasil/router.go +++ b/src/yggdrasil/router.go @@ -68,17 +68,34 @@ func (r *router) init(core *Core) { 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)", nil) - p.out = func(packet []byte) { - // This is to make very sure it never blocks - select { - case in <- packet: - return - default: - util.PutBytes(packet) - } - } + p.out = func(packet []byte) { in <- packet } r.in = in - r.out = func(packet []byte) { p.handlePacket(packet) } // The caller is responsible for go-ing if it needs to not block + out := make(chan []byte, 32) + go func() { + for packet := range out { + p.handlePacket(packet) + } + }() + out2 := make(chan []byte, 32) + go func() { + // This worker makes sure r.out never blocks + // It will buffer traffic long enough for the switch worker to take it + // If (somehow) you can send faster than the switch can receive, then this would use unbounded memory + // But crypto slows sends enough that the switch should always be able to take the packets... + var buf [][]byte + for { + buf = append(buf, <-out2) + for len(buf) > 0 { + select { + case bs := <-out2: + buf = append(buf, bs) + case out <- buf[0]: + buf = buf[1:] + } + } + } + }() + r.out = func(packet []byte) { out2 <- packet } r.toRecv = make(chan router_recvPacket, 32) recv := make(chan []byte, 32) send := make(chan []byte, 32) @@ -306,6 +323,8 @@ func (r *router) sendPacket(bs []byte) { return } + sinfo.doSend(bs) + return sinfo.send <- bs } } @@ -385,6 +404,8 @@ func (r *router) handleTraffic(packet []byte) { if !isIn { return } + sinfo.doRecv(&p) + return sinfo.recv <- &p } diff --git a/src/yggdrasil/session.go b/src/yggdrasil/session.go index cdabaf2b..bffc149b 100644 --- a/src/yggdrasil/session.go +++ b/src/yggdrasil/session.go @@ -304,7 +304,7 @@ func (ss *sessions) createSession(theirPermKey *crypto.BoxPubKey) *sessionInfo { sinfo.theirSubnet = *address.SubnetForNodeID(crypto.GetNodeID(&sinfo.theirPermPub)) sinfo.send = make(chan []byte, 32) sinfo.recv = make(chan *wire_trafficPacket, 32) - go sinfo.doWorker() + //go sinfo.doWorker() ss.sinfos[sinfo.myHandle] = &sinfo ss.byMySes[sinfo.mySesPub] = &sinfo.myHandle ss.byTheirPerm[sinfo.theirPermPub] = &sinfo.myHandle @@ -625,6 +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.recvPacket(bs, sinfo) + return select { case sinfo.core.router.toRecv <- router_recvPacket{bs, sinfo}: default: // avoid deadlocks, maybe do this somewhere else?... diff --git a/src/yggdrasil/switch.go b/src/yggdrasil/switch.go index 9b8c8398..d45b8855 100644 --- a/src/yggdrasil/switch.go +++ b/src/yggdrasil/switch.go @@ -668,10 +668,12 @@ func (t *switchTable) handleIn(packet []byte, idle map[switchPort]struct{}) bool //nothing case coordLen < bestCoordLen: update = true - case coordLen > bestCoordLen: - //nothing - case port < best.port: - update = true + /* + case coordLen > bestCoordLen: + //nothing + case port < best.port: + update = true + */ default: //nothing } @@ -800,7 +802,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)) + //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 bb3edd5e556fdb09f83eb8c2d5708f2de7f0dd56 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sun, 24 Feb 2019 12:59:30 -0600 Subject: [PATCH 08/20] add the relevant error to the default logging when a connection is closed --- src/yggdrasil/link.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index 3cd344d5..27c3aa23 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -162,8 +162,6 @@ func (intf *linkInterface) handler() error { 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) - 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 @@ -304,12 +302,13 @@ func (intf *linkInterface) handler() error { }() // Run reader loop for { - msg, err := intf.msgIO.readMsg() + var msg []byte + msg, err = intf.msgIO.readMsg() if len(msg) > 0 { intf.peer.handlePacket(msg) } if err != nil { - return err + break } select { case signalAlive <- len(msg) > 0: @@ -317,5 +316,8 @@ func (intf *linkInterface) handler() error { } } //////////////////////////////////////////////////////////////////////////////// - return nil + // Remember to set `err` to something useful before returning + intf.link.core.log.Infof("Disconnected %s: %s, source %s, reason: %s", + strings.ToUpper(intf.info.linkType), themString, intf.info.local, err) + return err } From 654407dc6d1dc5724420d7e08f2064d0e5b32a68 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sun, 24 Feb 2019 13:24:55 -0600 Subject: [PATCH 09/20] close long-dead connections in link.go instead of in switch.go, this is important in case a connection opens but never bothers to send even one switch message --- src/yggdrasil/link.go | 12 +++++++++++- src/yggdrasil/switch.go | 23 ----------------------- 2 files changed, 11 insertions(+), 24 deletions(-) diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index 27c3aa23..df9625d0 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -228,12 +228,15 @@ func (intf *linkInterface) handler() error { var isReady bool var sendTimerRunning bool var recvTimerRunning bool - recvTime := 6 * time.Second // TODO set to ReadTimeout from the config, reset if it gets changed + recvTime := 6 * time.Second // TODO set to ReadTimeout from the config, reset if it gets changed + closeTime := 2 * switch_timeout // TODO or maybe this makes more sense for ReadTimeout?... sendTime := time.Second sendTimer := time.NewTimer(sendTime) defer util.TimerStop(sendTimer) recvTimer := time.NewTimer(recvTime) defer util.TimerStop(recvTimer) + closeTimer := time.NewTimer(closeTime) + defer util.TimerStop(closeTimer) 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, @@ -243,6 +246,7 @@ func (intf *linkInterface) handler() error { if !ok { return } + util.TimerStop(closeTimer) util.TimerStop(recvTimer) recvTimerRunning = false isAlive = true @@ -274,6 +278,8 @@ func (intf *linkInterface) handler() error { // 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) + util.TimerStop(closeTimer) + closeTimer.Reset(closeTime) recvTimerRunning = true } case _, ok := <-signalReady: @@ -297,6 +303,10 @@ func (intf *linkInterface) handler() error { case <-recvTimer.C: // We haven't received anything, so assume there's a problem and don't return this node to the switch until they start responding isAlive = false + case <-closeTimer.C: + // We haven't received anything in a really long time, so things have died at the switch level and then some... + // Just close the connection at this point... + intf.msgIO.close() } } }() diff --git a/src/yggdrasil/switch.go b/src/yggdrasil/switch.go index d45b8855..1b611af2 100644 --- a/src/yggdrasil/switch.go +++ b/src/yggdrasil/switch.go @@ -215,7 +215,6 @@ func (t *switchTable) doMaintenance() { defer t.mutex.Unlock() // Release lock when we're done t.cleanRoot() t.cleanDropped() - t.cleanPeers() } // Updates the root periodically if it is ourself, or promotes ourself to root if we're better than the current root or if the current root has timed out. @@ -272,28 +271,6 @@ func (t *switchTable) forgetPeer(port switchPort) { } } -// Clean all unresponsive peers from the table, needed in case a peer stops updating. -// Needed in case a non-parent peer keeps the connection open but stops sending updates. -// Also reclaims space from deleted peers by copying the map. -func (t *switchTable) cleanPeers() { - now := time.Now() - for port, peer := range t.data.peers { - 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 { - // The root timestamp would probably time out before this happens, but better safe than sorry. - // We removed the current parent, so find a new one. - t.parent = 0 - for _, peer := range t.data.peers { - t.unlockedHandleMsg(&peer.msg, peer.port, true) - } - } -} - // Dropped is a list of roots that are better than the current root, but stopped sending new timestamps. // If we switch to a new root, and that root is better than an old root that previously timed out, then we can clean up the old dropped root infos. // This function is called periodically to do that cleanup. From def4fb358787a73f9e2f44180b5363e560ceb29a Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sun, 24 Feb 2019 14:48:16 -0600 Subject: [PATCH 10/20] fix timeout and improve logging on connection close --- src/yggdrasil/link.go | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index df9625d0..443b5943 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -4,6 +4,7 @@ import ( "encoding/hex" "errors" "fmt" + "io" "net" "strings" "sync" @@ -223,6 +224,7 @@ func (intf *linkInterface) handler() error { // Used to enable/disable activity in the switch signalAlive := make(chan bool, 1) // True = real packet, false = keep-alive defer close(signalAlive) + ret := make(chan error, 1) // How we signal the return value when multiple goroutines are involved go func() { var isAlive bool var isReady bool @@ -247,6 +249,7 @@ func (intf *linkInterface) handler() error { return } util.TimerStop(closeTimer) + closeTimer.Reset(closeTime) util.TimerStop(recvTimer) recvTimerRunning = false isAlive = true @@ -278,8 +281,6 @@ func (intf *linkInterface) handler() error { // 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) - util.TimerStop(closeTimer) - closeTimer.Reset(closeTime) recvTimerRunning = true } case _, ok := <-signalReady: @@ -306,18 +307,27 @@ func (intf *linkInterface) handler() error { case <-closeTimer.C: // We haven't received anything in a really long time, so things have died at the switch level and then some... // Just close the connection at this point... + select { + case ret <- errors.New("timeout"): + default: + } intf.msgIO.close() } } }() // Run reader loop for { - var msg []byte - msg, err = intf.msgIO.readMsg() + msg, err := intf.msgIO.readMsg() if len(msg) > 0 { intf.peer.handlePacket(msg) } if err != nil { + if err != io.EOF { + select { + case ret <- err: + default: + } + } break } select { @@ -327,7 +337,14 @@ func (intf *linkInterface) handler() error { } //////////////////////////////////////////////////////////////////////////////// // Remember to set `err` to something useful before returning - intf.link.core.log.Infof("Disconnected %s: %s, source %s, reason: %s", - strings.ToUpper(intf.info.linkType), themString, intf.info.local, err) + select { + case err = <-ret: + intf.link.core.log.Infof("Disconnected %s: %s, source %s; error: %s", + strings.ToUpper(intf.info.linkType), themString, intf.info.local, err) + default: + err = nil + intf.link.core.log.Infof("Disconnected %s: %s, source %s", + strings.ToUpper(intf.info.linkType), themString, intf.info.local) + } return err } From 25692420501e7252c2615382b8115d7119008eb2 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Tue, 26 Feb 2019 21:07:56 -0600 Subject: [PATCH 11/20] fixes to linkInterface.handler() --- src/util/util.go | 20 ++++++++++++++++++++ src/yggdrasil/link.go | 11 ++++++++--- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/util/util.go b/src/util/util.go index 45be3b19..df15ff2c 100644 --- a/src/util/util.go +++ b/src/util/util.go @@ -56,3 +56,23 @@ func TimerStop(t *time.Timer) bool { } return true } + +// Run a blocking function with a timeout. +// Returns true if the function returns. +// Returns false if the timer fires. +// The blocked function remains blocked--the caller is responsible for somehow killing it. +func FuncTimeout(f func(), timeout time.Duration) bool { + success := make(chan struct{}) + go func() { + defer close(success) + f() + }() + timer := time.NewTimer(timeout) + defer TimerStop(timer) + select { + case <-success: + return true + case <-timer.C: + return false + } +} diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index 443b5943..06020cd7 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -92,11 +92,16 @@ func (intf *linkInterface) handler() error { meta.link = *myLinkPub metaBytes := meta.encode() // TODO timeouts on send/recv (goroutine for send/recv, channel select w/ timer) - err := intf.msgIO._sendMetaBytes(metaBytes) + var err error + if !util.FuncTimeout(func() { err = intf.msgIO._sendMetaBytes(metaBytes) }, 30*time.Second) { + return errors.New("timeout on metadata send") + } if err != nil { return err } - metaBytes, err = intf.msgIO._recvMetaBytes() + if !util.FuncTimeout(func() { metaBytes, err = intf.msgIO._recvMetaBytes() }, 30*time.Second) { + return errors.New("timeout on metadata recv") + } if err != nil { return err } @@ -110,7 +115,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.force && !intf.link.core.peers.isAllowedEncryptionPublicKey(&meta.box) { + if !intf.incoming && !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() From 371b5ca6a2115d88fa5fe5f21f8d9624c65a766e Mon Sep 17 00:00:00 2001 From: Arceliar Date: Thu, 28 Feb 2019 18:49:34 -0600 Subject: [PATCH 12/20] Change log message about AllowedEncryptionPublicKeys from Debug to Warn --- src/yggdrasil/link.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index 06020cd7..6fc7687b 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -116,7 +116,7 @@ func (intf *linkInterface) handler() error { } // Check if we're authorized to connect to this key / IP if !intf.incoming && !intf.force && !intf.link.core.peers.isAllowedEncryptionPublicKey(&meta.box) { - intf.link.core.log.Debugf("%s connection to %s forbidden: AllowedEncryptionPublicKeys does not contain key %s", + intf.link.core.log.Warnf("%s connection to %s forbidden: AllowedEncryptionPublicKeys does not contain key %s", strings.ToUpper(intf.info.linkType), intf.info.remote, hex.EncodeToString(meta.box[:])) intf.msgIO.close() return nil From 06df791efc20ceec653c81dd624ff789eecd5fb6 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Thu, 28 Feb 2019 19:08:56 -0600 Subject: [PATCH 13/20] buffer packets moving from the switch to the router, allow them front drop if there's too many --- src/yggdrasil/switch.go | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/yggdrasil/switch.go b/src/yggdrasil/switch.go index 1b611af2..bf6b9194 100644 --- a/src/yggdrasil/switch.go +++ b/src/yggdrasil/switch.go @@ -176,6 +176,7 @@ type switchTable struct { admin chan func() // Pass a lambda for the admin socket to query stuff queues switch_buffers // Queues - not atomic so ONLY use through admin chan queueTotalMaxSize uint64 // Maximum combined size of queues + toRouter chan []byte // Packets to be sent to the router } // Minimum allowed total size of switch queues. @@ -199,6 +200,7 @@ func (t *switchTable) init(core *Core) { t.idleIn = make(chan switchPort, 1024) t.admin = make(chan func()) t.queueTotalMaxSize = SwitchQueueTotalMinSize + t.toRouter = make(chan []byte, 1) } // Safely gets a copy of this node's locator. @@ -616,17 +618,17 @@ func (t *switchTable) bestPortForCoords(coords []byte) switchPort { // Returns true if the packet has been handled somehow, false if it should be queued func (t *switchTable) handleIn(packet []byte, idle map[switchPort]struct{}) bool { coords := switch_getPacketCoords(packet) - ports := t.core.peers.getPorts() closer := t.getCloser(coords) if len(closer) == 0 { // TODO? call the router directly, and remove the whole concept of a self peer? - ports[0].sendPacket(packet) + t.toRouter <- packet return true } table := t.getTable() var best *peer var bestDist int var bestCoordLen int + ports := t.core.peers.getPorts() for port, dist := range closer { to := ports[port] _, isIdle := idle[port] @@ -775,6 +777,33 @@ func (t *switchTable) handleIdle(port switchPort) bool { // The switch worker does routing lookups and sends packets to where they need to be func (t *switchTable) doWorker() { + sendingToRouter := make(chan []byte, 1) + go func() { + // Keep sending packets to the router + self := t.core.peers.getPorts()[0] + for bs := range sendingToRouter { + self.sendPacket(bs) + } + }() + go func() { + // Keep taking packets from the idle worker and sending them to the above whenever it's idle, keeping anything extra in a (fifo, head-drop) buffer + var buf [][]byte + for { + buf = append(buf, <-t.toRouter) + for len(buf) > 0 { + select { + case bs := <-t.toRouter: + buf = append(buf, bs) + for len(buf) > 32 { + util.PutBytes(buf[0]) + buf = buf[1:] + } + case sendingToRouter <- buf[0]: + buf = buf[1:] + } + } + } + }() t.queues.switchTable = t t.queues.bufs = make(map[string]switch_buffer) // Packets per PacketStreamID (string) idle := make(map[switchPort]struct{}) // this is to deduplicate things From 304f22dc1d23807d7f612f3d32433121b4876c5c Mon Sep 17 00:00:00 2001 From: Arceliar Date: Thu, 28 Feb 2019 20:05:21 -0600 Subject: [PATCH 14/20] re-enable session workers in a way that doesn't block and drops packets before decrypting if necessary --- src/yggdrasil/router.go | 7 +------ src/yggdrasil/session.go | 32 +++++++++++++++++++++++--------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/yggdrasil/router.go b/src/yggdrasil/router.go index d84b935b..6acd4735 100644 --- a/src/yggdrasil/router.go +++ b/src/yggdrasil/router.go @@ -66,7 +66,7 @@ func (r *router) init(core *Core) { 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... + in := make(chan []byte, 1) // TODO something better than this... p := r.core.peers.newPeer(&r.core.boxPub, &r.core.sigPub, &crypto.BoxSharedKey{}, "(self)", nil) p.out = func(packet []byte) { in <- packet } r.in = in @@ -322,9 +322,6 @@ func (r *router) sendPacket(bs []byte) { // Don't continue - drop the packet return } - - sinfo.doSend(bs) - return sinfo.send <- bs } } @@ -404,8 +401,6 @@ func (r *router) handleTraffic(packet []byte) { if !isIn { return } - sinfo.doRecv(&p) - return sinfo.recv <- &p } diff --git a/src/yggdrasil/session.go b/src/yggdrasil/session.go index bffc149b..012af579 100644 --- a/src/yggdrasil/session.go +++ b/src/yggdrasil/session.go @@ -304,7 +304,7 @@ func (ss *sessions) createSession(theirPermKey *crypto.BoxPubKey) *sessionInfo { sinfo.theirSubnet = *address.SubnetForNodeID(crypto.GetNodeID(&sinfo.theirPermPub)) sinfo.send = make(chan []byte, 32) sinfo.recv = make(chan *wire_trafficPacket, 32) - //go sinfo.doWorker() + go sinfo.doWorker() ss.sinfos[sinfo.myHandle] = &sinfo ss.byMySes[sinfo.mySesPub] = &sinfo.myHandle ss.byTheirPerm[sinfo.theirPermPub] = &sinfo.myHandle @@ -525,17 +525,36 @@ func (ss *sessions) resetInits() { // It handles calling the relatively expensive crypto operations. // It's also responsible for checking nonces and dropping out-of-date/duplicate packets, or else calling the function to update nonces if the packet is OK. func (sinfo *sessionInfo) doWorker() { + send := make(chan []byte, 32) + defer close(send) + go func() { + for bs := range send { + sinfo.doSend(bs) + } + }() + recv := make(chan *wire_trafficPacket, 32) + defer close(recv) + go func() { + for p := range recv { + sinfo.doRecv(p) + } + }() for { select { case p, ok := <-sinfo.recv: if ok { - sinfo.doRecv(p) + select { + case recv <- p: + default: + // We need something to not block, and it's best to drop it before we decrypt + util.PutBytes(p.Payload) + } } else { return } case bs, ok := <-sinfo.send: if ok { - sinfo.doSend(bs) + send <- bs } else { return } @@ -625,10 +644,5 @@ func (sinfo *sessionInfo) doRecv(p *wire_trafficPacket) { sinfo.updateNonce(&p.Nonce) sinfo.time = time.Now() sinfo.bytesRecvd += uint64(len(bs)) - sinfo.core.router.recvPacket(bs, sinfo) - return - select { - case sinfo.core.router.toRecv <- router_recvPacket{bs, sinfo}: - default: // avoid deadlocks, maybe do this somewhere else?... - } + sinfo.core.router.toRecv <- router_recvPacket{bs, sinfo} } From ef7782289762770e8992aa880572418360ad2f81 Mon Sep 17 00:00:00 2001 From: William Wennerstr?m Date: Fri, 1 Mar 2019 17:26:07 +0100 Subject: [PATCH 15/20] contrib/busybox-init: add init.d script for busybox init --- contrib/busybox-init/S42yggdrasil | 71 +++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100755 contrib/busybox-init/S42yggdrasil diff --git a/contrib/busybox-init/S42yggdrasil b/contrib/busybox-init/S42yggdrasil new file mode 100755 index 00000000..d09c3a79 --- /dev/null +++ b/contrib/busybox-init/S42yggdrasil @@ -0,0 +1,71 @@ +#!/bin/sh + +CONFFILE="/etc/yggdrasil.conf" + +genconf() { + /usr/bin/yggdrasil -genconf > "$1" + return $? +} + +probetun() { + modprobe tun + return $? +} + +start() { + if [ ! -f "$CONFFILE" ]; then + printf 'Generating configuration file: ' + if genconf "$CONFFILE"; then + echo "OK" + else + echo "FAIL" + return 1 + fi + fi + + if [ ! -e /dev/net/tun ]; then + printf 'Inserting TUN module: ' + if probetun; then + echo "OK" + else + echo "FAIL" + return 1 + fi + fi + + printf 'Starting yggdrasil: ' + if start-stop-daemon -S -b -x /usr/bin/yggdrasil \ + -- --useconf < "$CONFFILE"; then + echo "OK" + else + echo "FAIL" + fi +} + +stop() { + printf "Stopping yggdrasil: " + if start-stop-daemon -K -q -x /usr/bin/yggdrasil; then + echo "OK" + else + echo "FAIL" + fi +} + +restart() { + stop + start +} + +reload() { + restart +} + +case "$1" in + start|stop|restart|reload) + "$1";; + *) + echo "Usage: $0 {start|stop|restart}" + exit 1 +esac + +exit 0 From a6ae159329938f8252f2495fcc79abafcc1eaf86 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Fri, 1 Mar 2019 18:26:52 +0000 Subject: [PATCH 16/20] Give some more feedback that a configuration reload actually happens --- src/yggdrasil/core.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index 2e23dd19..04433282 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -125,11 +125,15 @@ func (c *Core) addPeerLoop() { // 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.log.Infoln("Reloading configuration...") + c.configMutex.Lock() c.configOld = c.config c.config = *config c.configMutex.Unlock() + errors := 0 + components := []chan chan error{ c.admin.reconfigure, c.searches.reconfigure, @@ -148,9 +152,16 @@ func (c *Core) UpdateConfig(config *config.NodeConfig) { response := make(chan error) component <- response if err := <-response; err != nil { - c.log.Println(err) + c.log.Errorln(err) + errors++ } } + + if errors > 0 { + c.log.Warnln(errors, "modules reported errors during configuration reload") + } else { + c.log.Infoln("Configuration reloaded successfully") + } } // GetBuildName gets the current build name. This is usually injected if built From e99903bf725de434b76ea3c790afdd2b43e894cb Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Fri, 1 Mar 2019 19:26:50 +0000 Subject: [PATCH 17/20] Wake up AWDL on Darwin if awdl0 is an enabled multicast interface --- src/yggdrasil/multicast.go | 3 ++- src/yggdrasil/multicast_darwin.go | 25 ++++++++++++++++++++++++- src/yggdrasil/multicast_other.go | 6 +++++- src/yggdrasil/multicast_unix.go | 6 +++++- src/yggdrasil/multicast_windows.go | 6 +++++- 5 files changed, 41 insertions(+), 5 deletions(-) diff --git a/src/yggdrasil/multicast.go b/src/yggdrasil/multicast.go index 42651deb..b20ee94d 100644 --- a/src/yggdrasil/multicast.go +++ b/src/yggdrasil/multicast.go @@ -50,7 +50,7 @@ func (m *multicast) start() error { } listenString := fmt.Sprintf("[::]:%v", addr.Port) lc := net.ListenConfig{ - Control: multicastReuse, + Control: m.multicastReuse, } conn, err := lc.ListenPacket(context.Background(), "udp6", listenString) if err != nil { @@ -61,6 +61,7 @@ func (m *multicast) start() error { // Windows can't set this flag, so we need to handle it in other ways } + m.multicastWake() go m.listen() go m.announce() } diff --git a/src/yggdrasil/multicast_darwin.go b/src/yggdrasil/multicast_darwin.go index 71eecce4..9c5d0b2d 100644 --- a/src/yggdrasil/multicast_darwin.go +++ b/src/yggdrasil/multicast_darwin.go @@ -2,10 +2,33 @@ package yggdrasil +/* +#cgo CFLAGS: -x objective-c +#cgo LDFLAGS: -framework Foundation +#import +void WakeUpAWDL() { + id delegateObject; // Assume this exists. + NSNetServiceBrowser *serviceBrowser; + + serviceBrowser = [[NSNetServiceBrowser alloc] init]; + serviceBrowser.includesPeerToPeer = YES; + [serviceBrowser searchForServicesOfType:@"_yggdrasil._tcp" inDomain:@""]; +} +*/ +import "C" import "syscall" import "golang.org/x/sys/unix" -func multicastReuse(network string, address string, c syscall.RawConn) error { +func (m *multicast) multicastWake() { + for _, intf := range m.interfaces() { + if intf.Name == "awdl0" { + m.core.log.Infoln("Multicast discovery is waking up AWDL") + C.WakeUpAWDL() + } + } +} + +func (m *multicast) multicastReuse(network string, address string, c syscall.RawConn) error { var control error var reuseport error var recvanyif error diff --git a/src/yggdrasil/multicast_other.go b/src/yggdrasil/multicast_other.go index 8a4ce56c..043dd3ae 100644 --- a/src/yggdrasil/multicast_other.go +++ b/src/yggdrasil/multicast_other.go @@ -4,6 +4,10 @@ package yggdrasil import "syscall" -func multicastReuse(network string, address string, c syscall.RawConn) error { +func (m *multicast) multicastWake() { + +} + +func (m *multicast) multicastReuse(network string, address string, c syscall.RawConn) error { return nil } diff --git a/src/yggdrasil/multicast_unix.go b/src/yggdrasil/multicast_unix.go index 54bbc645..4237481c 100644 --- a/src/yggdrasil/multicast_unix.go +++ b/src/yggdrasil/multicast_unix.go @@ -5,7 +5,11 @@ package yggdrasil import "syscall" import "golang.org/x/sys/unix" -func multicastReuse(network string, address string, c syscall.RawConn) error { +func (m *multicast) multicastWake() { + +} + +func (m *multicast) multicastReuse(network string, address string, c syscall.RawConn) error { var control error var reuseport error diff --git a/src/yggdrasil/multicast_windows.go b/src/yggdrasil/multicast_windows.go index 13f20315..14c93129 100644 --- a/src/yggdrasil/multicast_windows.go +++ b/src/yggdrasil/multicast_windows.go @@ -5,7 +5,11 @@ package yggdrasil import "syscall" import "golang.org/x/sys/windows" -func multicastReuse(network string, address string, c syscall.RawConn) error { +func (m *multicast) multicastWake() { + +} + +func (m *multicast) multicastReuse(network string, address string, c syscall.RawConn) error { var control error var reuseaddr error From 12e088ab9e282663d48334b30a27b4975e9bee6d Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Fri, 1 Mar 2019 19:34:53 +0000 Subject: [PATCH 18/20] Remove unnecessary Cgo line --- src/yggdrasil/multicast_darwin.go | 1 - 1 file changed, 1 deletion(-) diff --git a/src/yggdrasil/multicast_darwin.go b/src/yggdrasil/multicast_darwin.go index 9c5d0b2d..66c18241 100644 --- a/src/yggdrasil/multicast_darwin.go +++ b/src/yggdrasil/multicast_darwin.go @@ -7,7 +7,6 @@ package yggdrasil #cgo LDFLAGS: -framework Foundation #import void WakeUpAWDL() { - id delegateObject; // Assume this exists. NSNetServiceBrowser *serviceBrowser; serviceBrowser = [[NSNetServiceBrowser alloc] init]; From b401b92a75a8da0fba86d2d2e4b34fb05f097e97 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 2 Mar 2019 00:38:18 +0000 Subject: [PATCH 19/20] Try updating circleci workflow --- .circleci/config.yml | 119 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 107 insertions(+), 12 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 82a68aad..871d2faf 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,7 +3,7 @@ # Check https://circleci.com/docs/2.0/language-go/ for more details version: 2 jobs: - build: + build-linux: docker: - image: circleci/golang:1.11 @@ -43,17 +43,97 @@ jobs: sudo alien --to-rpm yggdrasil*.deb --scripts --keep-version && mv *.rpm /tmp/upload/; mv *.deb /tmp/upload/ + - run: + name: Build for EdgeRouter + command: | + rm -f {yggdrasil,yggdrasilctl} + git clone https://github.com/neilalexander/vyatta-yggdrasil /tmp/vyatta-yggdrasil; + cd /tmp/vyatta-yggdrasil; + BUILDDIR_YGG=$CIRCLE_WORKING_DIRECTORY ./build-edgerouter-x $CIRCLE_BRANCH; + BUILDDIR_YGG=$CIRCLE_WORKING_DIRECTORY ./build-edgerouter-lite $CIRCLE_BRANCH; + mv *.deb /tmp/upload; + + - persist_to_workspace: + root: /tmp + paths: + - upload + + build-macos: + macos: + xcode: "10.0.0" + + working_directory: ~/go/src/github.com/yggdrasil-network/yggdrasil-go + + steps: + - checkout + + - run: + name: Create artifact upload directory and set variables + command: | + mkdir /tmp/upload + echo 'export CINAME=$(sh contrib/semver/name.sh)' >> $BASH_ENV + echo 'export CIVERSION=$(sh contrib/semver/version.sh --bare)' >> $BASH_ENV + echo 'export PATH=$PATH:/usr/local/go/bin:~/go/bin' >> $BASH_ENV + git config --global user.email "$(git log --format='%ae' HEAD -1)"; + git config --global user.name "$(git log --format='%an' HEAD -1)"; + echo -e "Host *\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config + + - run: + name: Install Go 1.11 + command: | + cd /tmp + curl -LO https://dl.google.com/go/go1.11.5.darwin-amd64.pkg + sudo installer -pkg /tmp/go1.11.5.darwin-amd64.pkg -target / + + - run: + name: Install Gomobile + command: | + GO111MODULE=off go get golang.org/x/mobile/cmd/gomobile + gomobile init + - run: name: Build for macOS 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; + GO111MODULE=on GOOS=darwin GOARCH=amd64 ./build + mv yggdrasil /tmp/upload/$CINAME-$CIVERSION-darwin-amd64 + mv yggdrasilctl /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-darwin-amd64; - 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/ + PKGARCH=amd64 sh contrib/macos/create-pkg.sh + mv *.pkg /tmp/upload/ + + #- run: + # name: Build framework for iOS (.framework format) + # command: | + # sudo GO111MODULE=off go get -v github.com/yggdrasil-network/yggdrasil-go/cmd/... + # sudo GO111MODULE=off go get -v github.com/yggdrasil-network/yggdrasil-go/src/... + # GO111MODULE=off ./build -i + # mv *.framework /tmp/upload + + - persist_to_workspace: + root: /tmp + paths: + - upload + + build-other: + docker: + - image: circleci/golang:1.11 + + steps: + - checkout + + - run: + name: Create artifact upload directory and set variables + command: | + mkdir /tmp/upload + echo 'export CINAME=$(sh contrib/semver/name.sh)' >> $BASH_ENV + echo 'export CIVERSION=$(sh contrib/semver/version.sh --bare)' >> $BASH_ENV + git config --global user.email "$(git log --format='%ae' HEAD -1)"; + git config --global user.name "$(git log --format='%an' HEAD -1)"; - run: name: Build for OpenBSD @@ -83,16 +163,31 @@ jobs: GOOS=windows GOARCH=amd64 ./build && mv yggdrasil.exe /tmp/upload/$CINAME-$CIVERSION-windows-amd64.exe && mv yggdrasilctl.exe /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-windows-amd64.exe; GOOS=windows GOARCH=386 ./build && mv yggdrasil.exe /tmp/upload/$CINAME-$CIVERSION-windows-i386.exe && mv yggdrasilctl.exe /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-windows-i386.exe; - - run: - name: Build for EdgeRouter - command: | - rm -f {yggdrasil,yggdrasilctl} - git clone https://github.com/neilalexander/vyatta-yggdrasil /tmp/vyatta-yggdrasil; - cd /tmp/vyatta-yggdrasil; - BUILDDIR_YGG=$CIRCLE_WORKING_DIRECTORY ./build-edgerouter-x $CIRCLE_BRANCH; - BUILDDIR_YGG=$CIRCLE_WORKING_DIRECTORY ./build-edgerouter-lite $CIRCLE_BRANCH; - mv *.deb /tmp/upload; + - persist_to_workspace: + root: /tmp + paths: + - upload + + upload: + machine: true + + steps: + - attach_workspace: + at: /tmp - store_artifacts: path: /tmp/upload destination: / + +workflows: + version: 2 + build-all: + jobs: + - build-linux + - build-macos + - build-other + - upload: + requires: + - build-linux + - build-macos + - build-other From 857a33c91bb9151817eef3d706e1f543a997bf5f Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 2 Mar 2019 15:26:55 +0000 Subject: [PATCH 20/20] Revert "Simplifying Dockerfile" --- contrib/docker/Dockerfile | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/contrib/docker/Dockerfile b/contrib/docker/Dockerfile index a91f775d..fb4fbfaa 100644 --- a/contrib/docker/Dockerfile +++ b/contrib/docker/Dockerfile @@ -1,19 +1,18 @@ -FROM golang:alpine as builder +FROM docker.io/golang:alpine as builder +COPY . /src WORKDIR /src ENV CGO_ENABLED=0 -RUN apk add git && \ - git clone https://github.com/yggdrasil-network/yggdrasil-go.git . && \ - ./build +RUN apk add git && ./build -FROM alpine +FROM docker.io/alpine LABEL maintainer="Christer Waren/CWINFO " COPY --from=builder /src/yggdrasil /usr/bin/yggdrasil COPY --from=builder /src/yggdrasilctl /usr/bin/yggdrasilctl -COPY --from=builder /src/contrib/docker/entrypoint.sh /usr/bin/entrypoint.sh +COPY contrib/docker/entrypoint.sh /usr/bin/entrypoint.sh # RUN addgroup -g 1000 -S yggdrasil-network \ # && adduser -u 1000 -S -g 1000 --home /etc/yggdrasil-network yggdrasil-network