From 7068160b200d1d074f50f986edd3c1954fa96c73 Mon Sep 17 00:00:00 2001 From: Arano-kai Date: Thu, 14 Nov 2019 13:29:27 +0200 Subject: [PATCH 01/12] Systemd: move config generation to a separate unit - Modular unit composition: different tasks in separate units - Use systemd tool set to run checks - Avoid using inline shell in unit --- contrib/systemd/yggdrasil-default-config.service | 13 +++++++++++++ contrib/systemd/yggdrasil.service | 7 ++----- 2 files changed, 15 insertions(+), 5 deletions(-) create mode 100644 contrib/systemd/yggdrasil-default-config.service diff --git a/contrib/systemd/yggdrasil-default-config.service b/contrib/systemd/yggdrasil-default-config.service new file mode 100644 index 0000000..e9fe45b --- /dev/null +++ b/contrib/systemd/yggdrasil-default-config.service @@ -0,0 +1,13 @@ +[Unit] +Description=yggdrasil default config generator +ConditionPathExists=|!/etc/yggdrasil.conf +ConditionFileNotEmpty=|!/etc/yggdrasil.conf +Wants=local-fs.target +After=local-fs.target + +[Service] +Type=oneshot +Group=yggdrasil +StandardOutput=file:/etc/yggdrasil.conf +ExecStart=/usr/bin/yggdrasil -genconf +ExecStartPost=/usr/bin/chmod 0640 /etc/yggdrasil.conf diff --git a/contrib/systemd/yggdrasil.service b/contrib/systemd/yggdrasil.service index b48ff78..075c0bd 100644 --- a/contrib/systemd/yggdrasil.service +++ b/contrib/systemd/yggdrasil.service @@ -1,7 +1,9 @@ [Unit] Description=yggdrasil Wants=network.target +Wants=yggdrasil-default-config.service After=network.target +After=yggdrasil-default-config.service [Service] Group=yggdrasil @@ -10,11 +12,6 @@ ProtectSystem=true SyslogIdentifier=yggdrasil CapabilityBoundSet=CAP_NET_ADMIN ExecStartPre=+-/sbin/modprobe tun -ExecStartPre=/bin/sh -ec "if ! test -s /etc/yggdrasil.conf; \ - then umask 077; \ - yggdrasil -genconf > /etc/yggdrasil.conf; \ - 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 From 4159ccb893205f532a5f83d26072ef7a46d8129b Mon Sep 17 00:00:00 2001 From: Anatolii Kurotych Date: Sat, 30 Nov 2019 16:05:44 +0200 Subject: [PATCH 02/12] Fix return value in Multicast.Stop() --- src/multicast/multicast.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/multicast/multicast.go b/src/multicast/multicast.go index 4e0b4f3..93bf94b 100644 --- a/src/multicast/multicast.go +++ b/src/multicast/multicast.go @@ -112,7 +112,7 @@ func (m *Multicast) Stop() error { err = m._stop() }) m.log.Debugln("Stopped multicast module") - return nil + return err } func (m *Multicast) _stop() error { From 468e3661684dfe11dbc4dcc5856a166b87e21009 Mon Sep 17 00:00:00 2001 From: Anatolii Kurotych Date: Sat, 30 Nov 2019 20:46:29 +0200 Subject: [PATCH 03/12] Use loglevel instead comma-separated list of logging --- cmd/yggdrasil/main.go | 45 ++++++++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 981c6ef..813e950 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -132,6 +132,32 @@ func doGenconf(isjson bool) string { return string(bs) } +func setLogLevel(loglevel string, logger *log.Logger) { + levels := [...]string{"error", "warn", "info", "debug", "trace"} + loglevel = strings.ToLower(loglevel) + + contains := func() bool { + for _, l := range levels { + if l == loglevel { + return true + } + } + return false + } + + if !contains() { // set default log level + logger.Infoln("Loglevel parse failed. Set default level(info)") + loglevel = "info" + } + + for _, l := range levels { + logger.EnableLevel(l) + if l == loglevel { + break + } + } +} + // The main function is responsible for configuring and starting Yggdrasil. func main() { // Configure the command line parameters. @@ -142,10 +168,10 @@ 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)") ver := flag.Bool("version", false, "prints the version of this build") - logging := flag.String("logging", "info,warn,error", "comma-separated list of logging levels to enable") logto := flag.String("logto", "stdout", "file path to log to, \"syslog\" or \"stdout\"") getaddr := flag.Bool("address", false, "returns the IPv6 address as derived from the supplied configuration") getsnet := flag.Bool("subnet", false, "returns the IPv6 subnet as derived from the supplied configuration") + loglevel := flag.String("loglevel", "info", "loglevel to enable") flag.Parse() var cfg *config.NodeConfig @@ -239,20 +265,9 @@ func main() { logger = log.New(os.Stdout, "", log.Flags()) logger.Warnln("Logging defaulting to stdout") } - //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 - } - } - } + + setLogLevel(*loglevel, logger) + // Setup the Yggdrasil node itself. The node{} type includes a Core, so we // don't need to create this manually. n := node{} From bf5d5b2269329906512e887701a9d27a382b7057 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 4 Dec 2019 09:29:30 +0000 Subject: [PATCH 04/12] Rename service from 'yggdrasil' to 'Yggdrasil' --- contrib/msi/build-msi.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/msi/build-msi.sh b/contrib/msi/build-msi.sh index 4e68b40..cfa2728 100644 --- a/contrib/msi/build-msi.sh +++ b/contrib/msi/build-msi.sh @@ -136,7 +136,7 @@ cat > wix.xml << EOF DisplayName="Yggdrasil Service" ErrorControl="normal" LoadOrderGroup="NetworkProvider" - Name="yggdrasil" + Name="Yggdrasil" Start="auto" Type="ownProcess" Arguments='-useconffile "%ALLUSERSPROFILE%\\Yggdrasil\\yggdrasil.conf" -logto "%ALLUSERSPROFILE%\\Yggdrasil\\yggdrasil.log"' From 1d4119950172d8d65ba24e3c7ae146a89fdf5ec9 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 10 Dec 2019 10:55:20 +0000 Subject: [PATCH 05/12] Move Wintun to separate feature --- contrib/msi/build-msi.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/contrib/msi/build-msi.sh b/contrib/msi/build-msi.sh index cfa2728..cf69d6b 100644 --- a/contrib/msi/build-msi.sh +++ b/contrib/msi/build-msi.sh @@ -177,13 +177,16 @@ cat > wix.xml << EOF SourceFile="${PKGMSMNAME}" /> - - + + + + + Date: Tue, 10 Dec 2019 11:17:15 +0000 Subject: [PATCH 06/12] Update metadata --- contrib/msi/build-msi.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/contrib/msi/build-msi.sh b/contrib/msi/build-msi.sh index cf69d6b..3f80487 100644 --- a/contrib/msi/build-msi.sh +++ b/contrib/msi/build-msi.sh @@ -100,7 +100,7 @@ cat > wix.xml << EOF Id="*" Keywords="Installer" Description="Yggdrasil Network Installer" - Comments="This is the Yggdrasil Network router for Windows." + Comments="Yggdrasil Network standalone router for Windows." Manufacturer="github.com/yggdrasil-network" InstallerVersion="200" InstallScope="perMachine" @@ -115,7 +115,8 @@ cat > wix.xml << EOF + EmbedCab="yes" + CompressionLevel="high" /> From 3e388cd7f9b6adedf768fea512a7c8e3801d2c49 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 10 Dec 2019 11:27:49 +0000 Subject: [PATCH 07/12] Try to avoid breaking Wintun during upgrades --- contrib/msi/build-msi.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/contrib/msi/build-msi.sh b/contrib/msi/build-msi.sh index 3f80487..358fb77 100644 --- a/contrib/msi/build-msi.sh +++ b/contrib/msi/build-msi.sh @@ -185,6 +185,9 @@ cat > wix.xml << EOF + + NOT UPGRADINGPRODUCTCODE + From 6f927b06133b5a2b5d01cdd8757e5509e45e5403 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 10 Dec 2019 11:33:52 +0000 Subject: [PATCH 08/12] Reverse upgrade condition --- contrib/msi/build-msi.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/msi/build-msi.sh b/contrib/msi/build-msi.sh index 358fb77..b660196 100644 --- a/contrib/msi/build-msi.sh +++ b/contrib/msi/build-msi.sh @@ -186,7 +186,7 @@ cat > wix.xml << EOF - NOT UPGRADINGPRODUCTCODE + UPGRADINGPRODUCTCODE From 4762edc2b3067884c696755d51907c5e467371c0 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 10 Dec 2019 11:38:58 +0000 Subject: [PATCH 09/12] Package display name --- contrib/msi/build-msi.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/contrib/msi/build-msi.sh b/contrib/msi/build-msi.sh index b660196..e291074 100644 --- a/contrib/msi/build-msi.sh +++ b/contrib/msi/build-msi.sh @@ -83,12 +83,18 @@ else exit 1 fi +if [ $PKGNAME != "master" ]; then + PKGDISPLAYNAME="Yggdrasil Network (${PKGNAME} branch)" +elif + PKGDISPLAYNAME="Yggdrasil Network" +fi + # Generate the wix.xml file cat > wix.xml << EOF Date: Tue, 10 Dec 2019 11:40:16 +0000 Subject: [PATCH 10/12] Fix syntax error in build-msi.sh --- contrib/msi/build-msi.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/msi/build-msi.sh b/contrib/msi/build-msi.sh index e291074..421481c 100644 --- a/contrib/msi/build-msi.sh +++ b/contrib/msi/build-msi.sh @@ -85,7 +85,7 @@ fi if [ $PKGNAME != "master" ]; then PKGDISPLAYNAME="Yggdrasil Network (${PKGNAME} branch)" -elif +else PKGDISPLAYNAME="Yggdrasil Network" fi From a2adcbd7e43e584396919ad7063ab0fde5e3ece3 Mon Sep 17 00:00:00 2001 From: William Fleurant Date: Sun, 5 Jan 2020 15:26:08 -0500 Subject: [PATCH 11/12] docker: build and copy genkeys --- contrib/docker/Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contrib/docker/Dockerfile b/contrib/docker/Dockerfile index fb4fbfa..fd2ae98 100644 --- a/contrib/docker/Dockerfile +++ b/contrib/docker/Dockerfile @@ -5,13 +5,14 @@ WORKDIR /src ENV CGO_ENABLED=0 -RUN apk add git && ./build +RUN apk add git && ./build && go build -o /src/genkeys cmd/genkeys/main.go 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/genkeys /usr/bin/genkeys COPY contrib/docker/entrypoint.sh /usr/bin/entrypoint.sh # RUN addgroup -g 1000 -S yggdrasil-network \ From 9304873047a6f3be84fef00c4be528f65e8ee2d7 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 5 Jan 2020 22:15:52 +0000 Subject: [PATCH 12/12] Convert nodeinfo to actor --- src/yggdrasil/api.go | 19 +++---- src/yggdrasil/nodeinfo.go | 114 +++++++++++++++++++++++--------------- src/yggdrasil/router.go | 1 + 3 files changed, 77 insertions(+), 57 deletions(-) diff --git a/src/yggdrasil/api.go b/src/yggdrasil/api.go index 7f82c26..693dbd0 100644 --- a/src/yggdrasil/api.go +++ b/src/yggdrasil/api.go @@ -390,17 +390,14 @@ func (c *Core) SetMaximumSessionMTU(mtu uint16) { // necessary when, e.g. crawling the network. func (c *Core) GetNodeInfo(key crypto.BoxPubKey, coords []uint64, nocache bool) (NodeInfoPayload, error) { response := make(chan *NodeInfoPayload, 1) - sendNodeInfoRequest := func() { - c.router.nodeinfo.addCallback(key, func(nodeinfo *NodeInfoPayload) { - defer func() { recover() }() - select { - case response <- nodeinfo: - default: - } - }) - c.router.nodeinfo.sendNodeInfo(key, wire_coordsUint64stoBytes(coords), false) - } - phony.Block(&c.router, sendNodeInfoRequest) + c.router.nodeinfo.addCallback(key, func(nodeinfo *NodeInfoPayload) { + defer func() { recover() }() + select { + case response <- nodeinfo: + default: + } + }) + c.router.nodeinfo.sendNodeInfo(key, wire_coordsUint64stoBytes(coords), false) timer := time.AfterFunc(6*time.Second, func() { close(response) }) defer timer.Stop() for res := range response { diff --git a/src/yggdrasil/nodeinfo.go b/src/yggdrasil/nodeinfo.go index 8a5d787..c3e9a27 100644 --- a/src/yggdrasil/nodeinfo.go +++ b/src/yggdrasil/nodeinfo.go @@ -5,21 +5,19 @@ import ( "errors" "runtime" "strings" - "sync" "time" + "github.com/Arceliar/phony" "github.com/yggdrasil-network/yggdrasil-go/src/crypto" "github.com/yggdrasil-network/yggdrasil-go/src/version" ) type nodeinfo struct { - core *Core - myNodeInfo NodeInfoPayload - myNodeInfoMutex sync.RWMutex - callbacks map[crypto.BoxPubKey]nodeinfoCallback - callbacksMutex sync.Mutex - cache map[crypto.BoxPubKey]nodeinfoCached - cacheMutex sync.RWMutex + phony.Inbox + core *Core + myNodeInfo NodeInfoPayload + callbacks map[crypto.BoxPubKey]nodeinfoCallback + cache map[crypto.BoxPubKey]nodeinfoCached } type nodeinfoCached struct { @@ -43,35 +41,43 @@ type nodeinfoReqRes struct { // Initialises the nodeinfo cache/callback maps, and starts a goroutine to keep // the cache/callback maps clean of stale entries func (m *nodeinfo) init(core *Core) { + m.Act(m, func() { + m._init(core) + }) +} + +func (m *nodeinfo) _init(core *Core) { m.core = core m.callbacks = make(map[crypto.BoxPubKey]nodeinfoCallback) m.cache = make(map[crypto.BoxPubKey]nodeinfoCached) - var f func() - f = func() { - m.callbacksMutex.Lock() - for boxPubKey, callback := range m.callbacks { - if time.Since(callback.created) > time.Minute { - delete(m.callbacks, boxPubKey) - } + m._cleanup() +} + +func (m *nodeinfo) _cleanup() { + for boxPubKey, callback := range m.callbacks { + if time.Since(callback.created) > time.Minute { + delete(m.callbacks, boxPubKey) } - m.callbacksMutex.Unlock() - m.cacheMutex.Lock() - for boxPubKey, cache := range m.cache { - if time.Since(cache.created) > time.Hour { - delete(m.cache, boxPubKey) - } - } - m.cacheMutex.Unlock() - time.AfterFunc(time.Second*30, f) } - go f() + for boxPubKey, cache := range m.cache { + if time.Since(cache.created) > time.Hour { + delete(m.cache, boxPubKey) + } + } + time.AfterFunc(time.Second*30, func() { + m.Act(m, m._cleanup) + }) } // Add a callback for a nodeinfo lookup func (m *nodeinfo) addCallback(sender crypto.BoxPubKey, call func(nodeinfo *NodeInfoPayload)) { - m.callbacksMutex.Lock() - defer m.callbacksMutex.Unlock() + m.Act(m, func() { + m._addCallback(sender, call) + }) +} + +func (m *nodeinfo) _addCallback(sender crypto.BoxPubKey, call func(nodeinfo *NodeInfoPayload)) { m.callbacks[sender] = nodeinfoCallback{ created: time.Now(), call: call, @@ -79,9 +85,7 @@ func (m *nodeinfo) addCallback(sender crypto.BoxPubKey, call func(nodeinfo *Node } // Handles the callback, if there is one -func (m *nodeinfo) callback(sender crypto.BoxPubKey, nodeinfo NodeInfoPayload) { - m.callbacksMutex.Lock() - defer m.callbacksMutex.Unlock() +func (m *nodeinfo) _callback(sender crypto.BoxPubKey, nodeinfo NodeInfoPayload) { if callback, ok := m.callbacks[sender]; ok { callback.call(&nodeinfo) delete(m.callbacks, sender) @@ -89,16 +93,26 @@ func (m *nodeinfo) callback(sender crypto.BoxPubKey, nodeinfo NodeInfoPayload) { } // Get the current node's nodeinfo -func (m *nodeinfo) getNodeInfo() NodeInfoPayload { - m.myNodeInfoMutex.RLock() - defer m.myNodeInfoMutex.RUnlock() +func (m *nodeinfo) getNodeInfo() (p NodeInfoPayload) { + phony.Block(m, func() { + p = m._getNodeInfo() + }) + return +} + +func (m *nodeinfo) _getNodeInfo() NodeInfoPayload { return m.myNodeInfo } // Set the current node's nodeinfo -func (m *nodeinfo) setNodeInfo(given interface{}, privacy bool) error { - m.myNodeInfoMutex.Lock() - defer m.myNodeInfoMutex.Unlock() +func (m *nodeinfo) setNodeInfo(given interface{}, privacy bool) (err error) { + phony.Block(m, func() { + err = m._setNodeInfo(given, privacy) + }) + return +} + +func (m *nodeinfo) _setNodeInfo(given interface{}, privacy bool) error { defaults := map[string]interface{}{ "buildname": version.BuildName(), "buildversion": version.BuildVersion(), @@ -134,9 +148,7 @@ func (m *nodeinfo) setNodeInfo(given interface{}, privacy bool) error { } // Add nodeinfo into the cache for a node -func (m *nodeinfo) addCachedNodeInfo(key crypto.BoxPubKey, payload NodeInfoPayload) { - m.cacheMutex.Lock() - defer m.cacheMutex.Unlock() +func (m *nodeinfo) _addCachedNodeInfo(key crypto.BoxPubKey, payload NodeInfoPayload) { m.cache[key] = nodeinfoCached{ created: time.Now(), payload: payload, @@ -144,9 +156,7 @@ func (m *nodeinfo) addCachedNodeInfo(key crypto.BoxPubKey, payload NodeInfoPaylo } // Get a nodeinfo entry from the cache -func (m *nodeinfo) getCachedNodeInfo(key crypto.BoxPubKey) (NodeInfoPayload, error) { - m.cacheMutex.RLock() - defer m.cacheMutex.RUnlock() +func (m *nodeinfo) _getCachedNodeInfo(key crypto.BoxPubKey) (NodeInfoPayload, error) { if nodeinfo, ok := m.cache[key]; ok { return nodeinfo.payload, nil } @@ -155,21 +165,33 @@ func (m *nodeinfo) getCachedNodeInfo(key crypto.BoxPubKey) (NodeInfoPayload, err // Handles a nodeinfo request/response - called from the router func (m *nodeinfo) handleNodeInfo(nodeinfo *nodeinfoReqRes) { + m.Act(m, func() { + m._handleNodeInfo(nodeinfo) + }) +} + +func (m *nodeinfo) _handleNodeInfo(nodeinfo *nodeinfoReqRes) { if nodeinfo.IsResponse { - m.callback(nodeinfo.SendPermPub, nodeinfo.NodeInfo) - m.addCachedNodeInfo(nodeinfo.SendPermPub, nodeinfo.NodeInfo) + m._callback(nodeinfo.SendPermPub, nodeinfo.NodeInfo) + m._addCachedNodeInfo(nodeinfo.SendPermPub, nodeinfo.NodeInfo) } else { - m.sendNodeInfo(nodeinfo.SendPermPub, nodeinfo.SendCoords, true) + m._sendNodeInfo(nodeinfo.SendPermPub, nodeinfo.SendCoords, true) } } // Send nodeinfo request or response - called from the router func (m *nodeinfo) sendNodeInfo(key crypto.BoxPubKey, coords []byte, isResponse bool) { + m.Act(m, func() { + m._sendNodeInfo(key, coords, isResponse) + }) +} + +func (m *nodeinfo) _sendNodeInfo(key crypto.BoxPubKey, coords []byte, isResponse bool) { table := m.core.switchTable.table.Load().(lookupTable) nodeinfo := nodeinfoReqRes{ SendCoords: table.self.getCoords(), IsResponse: isResponse, - NodeInfo: m.getNodeInfo(), + NodeInfo: m._getNodeInfo(), } bs := nodeinfo.encode() shared := m.core.router.sessions.getSharedKey(&m.core.boxPriv, &key) diff --git a/src/yggdrasil/router.go b/src/yggdrasil/router.go index 64c8170..bd6eefd 100644 --- a/src/yggdrasil/router.go +++ b/src/yggdrasil/router.go @@ -78,6 +78,7 @@ func (r *router) init(core *Core) { func (r *router) reconfigure() { // Reconfigure the router current := r.core.config.GetCurrent() + r.core.log.Println("Reloading NodeInfo...") if err := r.nodeinfo.setNodeInfo(current.NodeInfo, current.NodeInfoPrivacy); err != nil { r.core.log.Errorln("Error reloading NodeInfo:", err) } else {