From 0c4c3858857fc0848d5b8c46aa93415973eaa708 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 3 Apr 2022 12:48:06 +0100 Subject: [PATCH 001/293] Fix regression in Path MTU discovery MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the past we used to send back anything up to 900 bytes of the packet in the ICMPv6 Packet Too Big response, whereas now we seemingly only send back 40 bytes. It turns out that sending back only the 40 bytes of IPv6 headers isn't enough for most operating systems to positively ID the flow to reduce the MTU. This PR updates it so that we can send up to 512 bytes instead (900 is probably excessive) — that should leave plenty of room for any number of IPv6 extension headers and the next protocol headers and some of the payload. This seems to fix the problem in my testing. --- src/ipv6rwc/ipv6rwc.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ipv6rwc/ipv6rwc.go b/src/ipv6rwc/ipv6rwc.go index fc2a688f..a87dc924 100644 --- a/src/ipv6rwc/ipv6rwc.go +++ b/src/ipv6rwc/ipv6rwc.go @@ -237,11 +237,11 @@ func (k *keyStore) readPC(p []byte) (int, error) { k.mutex.Unlock() if len(bs) > mtu { // Using bs would make it leak off the stack, so copy to buf - buf := make([]byte, 40) - copy(buf, bs) + buf := make([]byte, 512) + cn := copy(buf, bs) ptb := &icmp.PacketTooBig{ MTU: mtu, - Data: buf[:40], + Data: buf[:cn], } if packet, err := CreateICMPv6(buf[8:24], buf[24:40], ipv6.ICMPTypePacketTooBig, 0, ptb); err == nil { _, _ = k.writePC(packet) From 93c94e38f99f5d97edfbef83d1938e083919a4b0 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 17 Apr 2022 17:24:34 +0100 Subject: [PATCH 002/293] GitHub Actions --- .github/workflows/ci.yml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..03e1e76f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,31 @@ +name: Yggdrasil + +on: + push: + branches: [ master develop ] + pull_request: + branches: [ master develop ] + release: + +jobs: + build-test: + name: Go ${{ matrix.goversion }} + + strategy: + matrix: + goversion: ["1.16", "1.17", "1.18"] + + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: ${{ matrix.goversion }} + + - name: Build Yggdrasil + run: go build -v ./... + + - name: Unit tests + run: go test -v ./... From c7ffbc05a5d80da9eb47cee2089899d38ebe5658 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 17 Apr 2022 17:53:55 +0100 Subject: [PATCH 003/293] Update GitHub Actions --- .github/workflows/ci.yml | 83 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 78 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 03e1e76f..42ff2015 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,19 +2,33 @@ name: Yggdrasil on: push: - branches: [ master develop ] pull_request: - branches: [ master develop ] release: + workflow_dispatch: jobs: - build-test: - name: Go ${{ matrix.goversion }} - + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - uses: actions/setup-go@v3 + with: + go-version: 1.18 + - uses: actions/checkout@v3 + - name: golangci-lint + uses: golangci/golangci-lint-action@v3 + with: + args: --issues-exit-code=1 + + build-linux: strategy: + fail-fast: false matrix: goversion: ["1.16", "1.17", "1.18"] + name: Build & Test (Linux, Go ${{ matrix.goversion }}) + needs: [lint] + runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -29,3 +43,62 @@ jobs: - name: Unit tests run: go test -v ./... + + build-windows: + strategy: + fail-fast: false + matrix: + goversion: ["1.16", "1.17", "1.18"] + + name: Build & Test (Windows, Go ${{ matrix.goversion }}) + needs: [lint] + + runs-on: windows-latest + steps: + - uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: ${{ matrix.goversion }} + + - name: Build Yggdrasil + run: go build -v ./... + + - name: Unit tests + run: go test -v ./... + + build-macos: + strategy: + fail-fast: false + matrix: + goversion: ["1.16", "1.17", "1.18"] + + name: Build & Test (macOS, Go ${{ matrix.goversion }}) + needs: [lint] + + runs-on: macos-latest + steps: + - uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: ${{ matrix.goversion }} + + - name: Build Yggdrasil + run: go build -v ./... + + - name: Unit tests + run: go test -v ./... + + tests-ok: + name: All tests passed + needs: [build-linux, build-windows, build-macos] + runs-on: ubuntu-latest + if: ${{ !cancelled() }} + steps: + - name: Check all tests passed + uses: re-actors/alls-green@release/v1 + with: + jobs: ${{ toJSON(needs) }} From 90f9be38c56343d3f54e3b1483c7ea1c62a33e79 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 17 Apr 2022 17:56:54 +0100 Subject: [PATCH 004/293] Fix lint errors --- src/admin/admin.go | 3 +-- src/ipv6rwc/ipv6rwc.go | 2 +- src/multicast/multicast.go | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/admin/admin.go b/src/admin/admin.go index ce831395..56164533 100644 --- a/src/admin/admin.go +++ b/src/admin/admin.go @@ -83,8 +83,7 @@ func (a *AdminSocket) Init(c *core.Core, nc *config.NodeConfig, log *log.Logger, } return res, nil }) - a.core.SetAdmin(a) - return nil + return a.core.SetAdmin(a) } func (a *AdminSocket) SetupAdminHandlers(na *AdminSocket) { diff --git a/src/ipv6rwc/ipv6rwc.go b/src/ipv6rwc/ipv6rwc.go index a87dc924..bbaa8707 100644 --- a/src/ipv6rwc/ipv6rwc.go +++ b/src/ipv6rwc/ipv6rwc.go @@ -153,7 +153,7 @@ func (k *keyStore) update(key ed25519.PublicKey) *keyInfo { k.resetTimeout(info) k.mutex.Unlock() for _, packet := range packets { - k.core.WriteTo(packet, iwt.Addr(info.key[:])) + _, _ = k.core.WriteTo(packet, iwt.Addr(info.key[:])) } return info } diff --git a/src/multicast/multicast.go b/src/multicast/multicast.go index 882911df..9093e4cf 100644 --- a/src/multicast/multicast.go +++ b/src/multicast/multicast.go @@ -300,7 +300,7 @@ func (m *Multicast) _announce() { if li, err := m.core.Listen(u, iface.Name); err == nil { m.log.Debugln("Started multicasting on", iface.Name) // Store the listener so that we can stop it later if needed - linfo = &listenerInfo{listener: li, time: time.Now()} + linfo = &listenerInfo{listener: li, time: time.Now(), port: info.port} m.listeners[iface.Name] = linfo } else { m.log.Warnln("Not multicasting on", iface.Name, "due to error:", err) From 41d890bb64eafe6c29e94c9656cc347daf324d29 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 17 Apr 2022 18:02:25 +0100 Subject: [PATCH 005/293] Run `goimports` --- contrib/mobile/mobile_android.go | 1 + contrib/mobile/mobile_ios.go | 1 + contrib/mobile/mobile_other.go | 1 + src/core/debug.go | 14 ++++++++------ src/core/proto.go | 4 ++-- src/multicast/multicast_unix.go | 7 +++++-- src/multicast/multicast_windows.go | 7 +++++-- 7 files changed, 23 insertions(+), 12 deletions(-) diff --git a/contrib/mobile/mobile_android.go b/contrib/mobile/mobile_android.go index f3206aca..39bebec2 100644 --- a/contrib/mobile/mobile_android.go +++ b/contrib/mobile/mobile_android.go @@ -1,3 +1,4 @@ +//go:build android // +build android package mobile diff --git a/contrib/mobile/mobile_ios.go b/contrib/mobile/mobile_ios.go index 253bbc0b..fedee2db 100644 --- a/contrib/mobile/mobile_ios.go +++ b/contrib/mobile/mobile_ios.go @@ -1,3 +1,4 @@ +//go:build ios // +build ios package mobile diff --git a/contrib/mobile/mobile_other.go b/contrib/mobile/mobile_other.go index 7b7ed74a..aceeb7e2 100644 --- a/contrib/mobile/mobile_other.go +++ b/contrib/mobile/mobile_other.go @@ -1,3 +1,4 @@ +//go:build !android && !ios // +build !android,!ios package mobile diff --git a/src/core/debug.go b/src/core/debug.go index eb406798..ee1f1ed8 100644 --- a/src/core/debug.go +++ b/src/core/debug.go @@ -3,14 +3,16 @@ package core -import "fmt" +import ( + "fmt" -import _ "net/http/pprof" -import "net/http" -import "runtime" -import "os" + "net/http" + _ "net/http/pprof" + "os" + "runtime" -import "github.com/gologme/log" + "github.com/gologme/log" +) // Start the profiler in debug builds, if the required environment variable is set. func init() { diff --git a/src/core/proto.go b/src/core/proto.go index 3045972e..5af59cc8 100644 --- a/src/core/proto.go +++ b/src/core/proto.go @@ -47,9 +47,9 @@ func (p *protoHandler) init(core *Core) { p.core = core p.nodeinfo.init(p) - p.selfRequests = make(map[keyArray]*reqInfo) + p.selfRequests = make(map[keyArray]*reqInfo) p.peersRequests = make(map[keyArray]*reqInfo) - p.dhtRequests = make(map[keyArray]*reqInfo) + p.dhtRequests = make(map[keyArray]*reqInfo) } // Common functions diff --git a/src/multicast/multicast_unix.go b/src/multicast/multicast_unix.go index 9c822fcf..c59d876b 100644 --- a/src/multicast/multicast_unix.go +++ b/src/multicast/multicast_unix.go @@ -3,8 +3,11 @@ package multicast -import "syscall" -import "golang.org/x/sys/unix" +import ( + "syscall" + + "golang.org/x/sys/unix" +) func (m *Multicast) _multicastStarted() { diff --git a/src/multicast/multicast_windows.go b/src/multicast/multicast_windows.go index 515412a4..fa194ac6 100644 --- a/src/multicast/multicast_windows.go +++ b/src/multicast/multicast_windows.go @@ -3,8 +3,11 @@ package multicast -import "syscall" -import "golang.org/x/sys/windows" +import ( + "syscall" + + "golang.org/x/sys/windows" +) func (m *Multicast) _multicastStarted() { From 073799d3ded35f6d57ea216329e5b06857fea2cb Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 17 Apr 2022 18:22:26 +0100 Subject: [PATCH 006/293] Require Go 1.17 --- .github/workflows/ci.yml | 74 ++++++++++++++++++++-------------------- README.md | 5 +-- go.mod | 21 ++++++++---- 3 files changed, 55 insertions(+), 45 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 42ff2015..c9268f15 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,73 +24,73 @@ jobs: strategy: fail-fast: false matrix: - goversion: ["1.16", "1.17", "1.18"] - + goversion: ["1.17", "1.18"] + name: Build & Test (Linux, Go ${{ matrix.goversion }}) needs: [lint] - + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v3 - - name: Set up Go - uses: actions/setup-go@v3 - with: - go-version: ${{ matrix.goversion }} + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: ${{ matrix.goversion }} - - name: Build Yggdrasil - run: go build -v ./... + - name: Build Yggdrasil + run: go build -v ./... - - name: Unit tests - run: go test -v ./... + - name: Unit tests + run: go test -v ./... build-windows: strategy: fail-fast: false matrix: - goversion: ["1.16", "1.17", "1.18"] - + goversion: ["1.17", "1.18"] + name: Build & Test (Windows, Go ${{ matrix.goversion }}) needs: [lint] - + runs-on: windows-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v3 - - name: Set up Go - uses: actions/setup-go@v3 - with: - go-version: ${{ matrix.goversion }} + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: ${{ matrix.goversion }} - - name: Build Yggdrasil - run: go build -v ./... + - name: Build Yggdrasil + run: go build -v ./... + + - name: Unit tests + run: go test -v ./... - - name: Unit tests - run: go test -v ./... - build-macos: strategy: fail-fast: false matrix: - goversion: ["1.16", "1.17", "1.18"] - + goversion: ["1.17", "1.18"] + name: Build & Test (macOS, Go ${{ matrix.goversion }}) needs: [lint] - + runs-on: macos-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v3 - - name: Set up Go - uses: actions/setup-go@v3 - with: - go-version: ${{ matrix.goversion }} + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: ${{ matrix.goversion }} - - name: Build Yggdrasil - run: go build -v ./... + - name: Build Yggdrasil + run: go build -v ./... - - name: Unit tests - run: go test -v ./... + - name: Unit tests + run: go test -v ./... tests-ok: name: All tests passed diff --git a/README.md b/README.md index f1f71d11..2bfb5b1d 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ connectivity - it also works over IPv4. Yggdrasil works on a number of platforms, including Linux, macOS, Ubiquiti EdgeRouter, VyOS, Windows, FreeBSD, OpenBSD and OpenWrt. -Please see our [Installation](https://yggdrasil-network.github.io/installation.html) +Please see our [Installation](https://yggdrasil-network.github.io/installation.html) page for more information. You may also find other platform-specific wrappers, scripts or tools in the `contrib` folder. @@ -25,7 +25,7 @@ or tools in the `contrib` folder. If you want to build from source, as opposed to installing one of the pre-built packages: -1. Install [Go](https://golang.org) (requires Go 1.16 or later) +1. Install [Go](https://golang.org) (requires Go 1.17 or later) 2. Clone this repository 2. Run `./build` @@ -57,6 +57,7 @@ other configuration such as listen addresses or multicast addresses, etc. ### Run Yggdrasil To run with the generated static configuration: + ``` ./yggdrasil -useconffile /path/to/yggdrasil.conf ``` diff --git a/go.mod b/go.mod index 9c957553..236877c0 100644 --- a/go.mod +++ b/go.mod @@ -1,22 +1,17 @@ module github.com/yggdrasil-network/yggdrasil-go -go 1.16 +go 1.17 require ( github.com/Arceliar/ironwood v0.0.0-20211125050254-8951369625d0 github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 - github.com/VividCortex/ewma v1.2.0 // indirect github.com/cheggaaa/pb/v3 v3.0.8 - github.com/fatih/color v1.12.0 // indirect github.com/gologme/log v1.2.0 github.com/hashicorp/go-syslog v1.0.0 github.com/hjson/hjson-go v3.1.0+incompatible github.com/kardianos/minwinsvc v1.0.0 - github.com/mattn/go-isatty v0.0.13 // indirect - github.com/mattn/go-runewidth v0.0.13 // indirect github.com/mitchellh/mapstructure v1.4.1 github.com/vishvananda/netlink v1.1.0 - github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f // indirect golang.org/x/mobile v0.0.0-20220112015953-858099ff7816 golang.org/x/net v0.0.0-20211101193420-4a448f8816b3 golang.org/x/sys v0.0.0-20211102192858-4dd72447c267 @@ -24,3 +19,17 @@ require ( golang.zx2c4.com/wireguard v0.0.0-20211017052713-f87e87af0d9a golang.zx2c4.com/wireguard/windows v0.4.12 ) + +require ( + github.com/VividCortex/ewma v1.2.0 // indirect + github.com/fatih/color v1.12.0 // indirect + github.com/mattn/go-colorable v0.1.8 // indirect + github.com/mattn/go-isatty v0.0.13 // indirect + github.com/mattn/go-runewidth v0.0.13 // indirect + github.com/rivo/uniseg v0.2.0 // indirect + github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f // indirect + golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect + golang.org/x/mod v0.4.2 // indirect + golang.org/x/tools v0.1.8-0.20211022200916-316ba0b74098 // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect +) From bc78530fcbc144a77c979cef4e971610dc301a83 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 17 Apr 2022 23:38:16 +0100 Subject: [PATCH 007/293] Build packages in GitHub Actions --- .github/workflows/ci.yml | 132 +++++++++++++++++++++++++++++++++++++++ contrib/msi/build-msi.sh | 38 ++++------- contrib/semver/name.sh | 11 ++-- 3 files changed, 148 insertions(+), 33 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c9268f15..a884d762 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -102,3 +102,135 @@ jobs: uses: re-actors/alls-green@release/v1 with: jobs: ${{ toJSON(needs) }} + + build-packages-debian: + strategy: + fail-fast: false + matrix: + pkgarch: ["amd64", "i386", "mips", "mipsel", "armhf", "armel", "arm64"] + + name: Create Package (Debian, ${{ matrix.pkgarch }}) + needs: [tests-ok] + + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: 1.18 + + - name: Build package + env: + PKGARCH: ${{ matrix.pkgarch }} + run: sh contrib/deb/generate.sh + + - name: Upload artifacts + uses: actions/upload-artifact@v3 + with: + name: Debian package (${{ matrix.pkgarch }}) + path: "*.deb" + if-no-files-found: error + + build-packages-macos: + strategy: + fail-fast: false + matrix: + pkgarch: ["amd64", "arm64"] + + name: Create Package (macOS, ${{ matrix.pkgarch }}) + needs: [tests-ok] + + runs-on: macos-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: 1.18 + + - name: Build package + env: + PKGARCH: ${{ matrix.pkgarch }} + run: sh contrib/macos/create-pkg.sh + + - name: Upload artifacts + uses: actions/upload-artifact@v3 + with: + name: macOS package (${{ matrix.pkgarch }}) + path: "*.pkg" + if-no-files-found: error + + build-packages-windows: + strategy: + fail-fast: false + matrix: + pkgarch: ["x64", "x86", "arm", "arm64"] + + name: Create Package (Windows, ${{ matrix.pkgarch }}) + needs: [tests-ok] + + runs-on: windows-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: 1.18 + + - name: Build package + run: sh contrib/msi/build-msi.sh ${{ matrix.pkgarch }} + + - name: Upload artifacts + uses: actions/upload-artifact@v3 + with: + name: Windows package (${{ matrix.pkgarch }}) + path: "*.msi" + if-no-files-found: error + + build-packages-router: + strategy: + fail-fast: false + matrix: + pkgarch: ["edgerouter-x", "edgerouter-lite", "vyos-amd64", "vyos-i386"] + + name: Create Package (Router, ${{ matrix.pkgarch }}) + needs: [tests-ok] + + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + path: yggdrasil + + - uses: actions/checkout@v3 + with: + repository: neilalexander/vyatta-yggdrasil + path: vyatta-yggdrasil + + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: 1.18 + + - name: Build package + env: + BUILDDIR_YGG: /home/runner/work/yggdrasil-go/yggdrasil-go/yggdrasil + run: cd /home/runner/work/yggdrasil-go/yggdrasil-go/vyatta-yggdrasil && ./build-${{ matrix.pkgarch }} + + - name: Upload artifacts + uses: actions/upload-artifact@v3 + with: + name: Router package (${{ matrix.pkgarch }}) + path: "/home/runner/work/yggdrasil-go/yggdrasil-go/vyatta-yggdrasil/*.deb" + if-no-files-found: error diff --git a/contrib/msi/build-msi.sh b/contrib/msi/build-msi.sh index 888075c8..38b9b810 100644 --- a/contrib/msi/build-msi.sh +++ b/contrib/msi/build-msi.sh @@ -1,9 +1,9 @@ #!/bin/sh # This script generates an MSI file for Yggdrasil for a given architecture. It -# needs to run on Windows within MSYS2 and Go 1.13 or later must be installed on -# the system and within the PATH. This is ran currently by Appveyor (see -# appveyor.yml in the repository root) for both x86 and x64. +# needs to run on Windows within MSYS2 and Go 1.17 or later must be installed on +# the system and within the PATH. This is ran currently by GitHub Actions (see +# the workflows in the repository). # # Author: Neil Alexander @@ -11,37 +11,21 @@ PKGARCH=$1 if [ "${PKGARCH}" == "" ]; then - echo "tell me the architecture: x86, x64 or arm" + echo "tell me the architecture: x86, x64, arm or arm64" exit 1 fi -# Get the rest of the repository history. This is needed within Appveyor because -# otherwise we don't get all of the branch histories and therefore the semver -# scripts don't work properly. -if [ "${APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH}" != "" ]; -then - git fetch --all -# git checkout ${APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH} -elif [ "${APPVEYOR_REPO_BRANCH}" != "" ]; -then - git fetch --all - git checkout ${APPVEYOR_REPO_BRANCH} -fi - -# Install prerequisites within MSYS2 -pacman -S --needed --noconfirm unzip git curl - # Download the wix tools! if [ ! -d wixbin ]; then - curl -LO https://github.com/wixtoolset/wix3/releases/download/wix3112rtm/wix311-binaries.zip - if [ `md5sum wix311-binaries.zip | cut -f 1 -d " "` != "47a506f8ab6666ee3cc502fb07d0ee2a" ]; + curl -LO https://wixtoolset.org/downloads/v3.14.0.3910/wix314-binaries.zip + if [ `md5sum wix314-binaries.zip | cut -f 1 -d " "` != "34f655cf108086838dd5a76d4318063b" ]; then echo "wix package didn't match expected checksum" exit 1 fi mkdir -p wixbin - unzip -o wix311-binaries.zip -d wixbin || ( + unzip -o wix314-binaries.zip -d wixbin || ( echo "failed to unzip WiX" exit 1 ) @@ -51,7 +35,7 @@ fi [ "${PKGARCH}" == "x64" ] && GOOS=windows GOARCH=amd64 CGO_ENABLED=0 ./build [ "${PKGARCH}" == "x86" ] && GOOS=windows GOARCH=386 CGO_ENABLED=0 ./build [ "${PKGARCH}" == "arm" ] && GOOS=windows GOARCH=arm CGO_ENABLED=0 ./build -#[ "${PKGARCH}" == "arm64" ] && GOOS=windows GOARCH=arm64 CGO_ENABLED=0 ./build +[ "${PKGARCH}" == "arm64" ] && GOOS=windows GOARCH=arm64 CGO_ENABLED=0 ./build # Create the postinstall script cat > updateconfig.bat << EOF @@ -69,7 +53,7 @@ EOF PKGNAME=$(sh contrib/semver/name.sh) PKGVERSION=$(sh contrib/msi/msversion.sh --bare) PKGVERSIONMS=$(echo $PKGVERSION | tr - .) -[ "${PKGARCH}" == "x64" ] && \ +([ "${PKGARCH}" == "x64" ] || [ "${PKGARCH}" == "arm64" ]) && \ PKGGUID="77757838-1a23-40a5-a720-c3b43e0260cc" PKGINSTFOLDER="ProgramFiles64Folder" || \ PKGGUID="54a3294e-a441-4322-aefb-3bb40dd022bb" PKGINSTFOLDER="ProgramFilesFolder" @@ -85,8 +69,8 @@ elif [ $PKGARCH = "x86" ]; then PKGWINTUNDLL=wintun/bin/x86/wintun.dll elif [ $PKGARCH = "arm" ]; then PKGWINTUNDLL=wintun/bin/arm/wintun.dll -#elif [ $PKGARCH = "arm64" ]; then -# PKGWINTUNDLL=wintun/bin/arm64/wintun.dll +elif [ $PKGARCH = "arm64" ]; then + PKGWINTUNDLL=wintun/bin/arm64/wintun.dll else echo "wasn't sure which architecture to get wintun for" exit 1 diff --git a/contrib/semver/name.sh b/contrib/semver/name.sh index 53f56d5f..6294f896 100644 --- a/contrib/semver/name.sh +++ b/contrib/semver/name.sh @@ -1,13 +1,12 @@ #!/bin/sh # Get the current branch name -BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null) +BRANCH="$GITHUB_REF_NAME" +if [ -z "$BRANCH" ]; then + BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null) +fi -if [ -n "$APPVEYOR_PULL_REQUEST_NUMBER" ]; then - printf "yggdrasil-pr%s" "$APPVEYOR_PULL_REQUEST_NUMBER" - exit 0 -# Complain if the git history is not available -elif [ $? != 0 ] || [ -z "$BRANCH" ]; then +if [ $? != 0 ] || [ -z "$BRANCH" ]; then printf "yggdrasil" exit 0 fi From d2308f8d3a42a123daed4ac1e147b0076f685684 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 18 Apr 2022 10:25:05 +0100 Subject: [PATCH 008/293] Remove Appveyor and CircleCI configs --- .circleci/config.yml | 234 ------------------------------------------- appveyor.yml | 23 ----- 2 files changed, 257 deletions(-) delete mode 100644 .circleci/config.yml delete mode 100644 appveyor.yml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 065f2b0b..00000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,234 +0,0 @@ -# Golang CircleCI 2.0 configuration file -# -# Check https://circleci.com/docs/2.0/language-go/ for more details -version: 2.1 -jobs: - lint: - docker: - - image: circleci/golang:1.17 - - steps: - - checkout - - - run: - name: Run golangci-lint - command: | - go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.31.0 - golangci-lint run - - - run: - name: Run Go tests - command: | - go test ./... - - build-linux: - docker: - - image: circleci/golang:1.17 - - 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 CIVERSIONRPM=$(sh contrib/semver/version.sh --bare | tr "-" ".")' >> $BASH_ENV - echo 'export CIBRANCH=$(echo $CIRCLE_BRANCH | tr -d "/")' >> $BASH_ENV - case "$CINAME" in \ - "yggdrasil") (echo 'export CICONFLICTS=yggdrasil-develop' >> $BASH_ENV) ;; \ - "yggdrasil-develop") (echo 'export CICONFLICTS=yggdrasil' >> $BASH_ENV) ;; \ - *) (echo 'export CICONFLICTS="yggdrasil yggdrasil-develop"' >> $BASH_ENV) ;; \ - esac - git config --global user.email "$(git log --format='%ae' HEAD -1)"; - git config --global user.name "$(git log --format='%an' HEAD -1)"; - - - run: - name: Install RPM utilities - command: | - sudo apt-get update - sudo apt-get install -y rpm file - mkdir -p ~/rpmbuild/BUILD ~/rpmbuild/RPMS ~/rpmbuild/SOURCES ~/rpmbuild/SPECS ~/rpmbuild/SRPMS - - - run: - name: Test debug builds - command: | - ./build -d - test -f yggdrasil && test -f yggdrasilctl - - - run: - name: Build for Linux (including Debian packages) - command: | - rm -f {yggdrasil,yggdrasilctl} - PKGARCH=amd64 sh contrib/deb/generate.sh && mv yggdrasil /tmp/upload/$CINAME-$CIVERSION-linux-amd64 && mv yggdrasilctl /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-linux-amd64; - PKGARCH=i386 sh contrib/deb/generate.sh && mv yggdrasil /tmp/upload/$CINAME-$CIVERSION-linux-i386 && mv yggdrasilctl /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-linux-i386; - PKGARCH=mipsel sh contrib/deb/generate.sh && mv yggdrasil /tmp/upload/$CINAME-$CIVERSION-linux-mipsel && mv yggdrasilctl /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-linux-mipsel; - PKGARCH=mips sh contrib/deb/generate.sh && mv yggdrasil /tmp/upload/$CINAME-$CIVERSION-linux-mips && mv yggdrasilctl /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-linux-mips; - PKGARCH=armhf sh contrib/deb/generate.sh && mv yggdrasil /tmp/upload/$CINAME-$CIVERSION-linux-armhf && mv yggdrasilctl /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-linux-armhf; - PKGARCH=armel sh contrib/deb/generate.sh && mv yggdrasil /tmp/upload/$CINAME-$CIVERSION-linux-armel && mv yggdrasilctl /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-linux-armel; - PKGARCH=arm64 sh contrib/deb/generate.sh && mv yggdrasil /tmp/upload/$CINAME-$CIVERSION-linux-arm64 && mv yggdrasilctl /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-linux-arm64; - mv *.deb /tmp/upload/ - - - run: - name: Build for Linux (RPM packages) - command: | - git clone https://github.com/yggdrasil-network/yggdrasil-package-rpm ~/rpmbuild/SPECS - cd ../ && tar -czvf ~/rpmbuild/SOURCES/v$CIVERSIONRPM --transform "s/project/yggdrasil-go-$CIBRANCH-$CIVERSIONRPM/" project - sed -i "s/yggdrasil-go/yggdrasil-go-$CIBRANCH/" ~/rpmbuild/SPECS/yggdrasil.spec - sed -i "s/^PKGNAME=yggdrasil/PKGNAME=yggdrasil-$CIBRANCH/" ~/rpmbuild/SPECS/yggdrasil.spec - sed -i "s/^Name\:.*/Name\: $CINAME/" ~/rpmbuild/SPECS/yggdrasil.spec - sed -i "s/^Version\:.*/Version\: $CIVERSIONRPM/" ~/rpmbuild/SPECS/yggdrasil.spec - sed -i "s/^Conflicts\:.*/Conflicts\: $CICONFLICTS/" ~/rpmbuild/SPECS/yggdrasil.spec - cat ~/rpmbuild/SPECS/yggdrasil.spec - GOARCH=amd64 rpmbuild -v --nodeps --target=x86_64 -ba ~/rpmbuild/SPECS/yggdrasil.spec - #GOARCH=386 rpmbuild -v --nodeps --target=i386 -bb ~/rpmbuild/SPECS/yggdrasil.spec - find ~/rpmbuild/RPMS/ -name '*.rpm' -exec mv {} /tmp/upload \; - find ~/rpmbuild/SRPMS/ -name '*.rpm' -exec mv {} /tmp/upload \; - - - run: - name: Build for EdgeRouter and VyOS - 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; - BUILDDIR_YGG=$CIRCLE_WORKING_DIRECTORY ./build-vyos-i386 $CIRCLE_BRANCH - BUILDDIR_YGG=$CIRCLE_WORKING_DIRECTORY ./build-vyos-amd64 $CIRCLE_BRANCH - mv *.deb /tmp/upload; - - - persist_to_workspace: - root: /tmp - paths: - - upload - - build-macos: - macos: - xcode: "13.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.17 - command: | - cd /tmp - curl -LO https://dl.google.com/go/go1.17.darwin-amd64.pkg - sudo installer -pkg /tmp/go1.17.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: | - GO111MODULE=on GOOS=darwin GOARCH=amd64 ./build - cp yggdrasil /tmp/upload/$CINAME-$CIVERSION-darwin-amd64 - cp yggdrasilctl /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-darwin-amd64; - GO111MODULE=on GOOS=darwin GOARCH=arm64 ./build - cp yggdrasil /tmp/upload/$CINAME-$CIVERSION-darwin-arm64 - cp yggdrasilctl /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-darwin-arm64; - - - run: - name: Build for macOS (.pkg format) - command: | - PKGARCH=amd64 sh contrib/macos/create-pkg.sh - PKGARCH=arm64 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.17 - - 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 - command: | - rm -f {yggdrasil,yggdrasilctl} - GOOS=openbsd GOARCH=amd64 ./build && mv yggdrasil /tmp/upload/$CINAME-$CIVERSION-openbsd-amd64 && mv yggdrasilctl /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-openbsd-amd64; - GOOS=openbsd GOARCH=386 ./build && mv yggdrasil /tmp/upload/$CINAME-$CIVERSION-openbsd-i386 && mv yggdrasilctl /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-openbsd-i386; - - - run: - name: Build for FreeBSD - command: | - rm -f {yggdrasil,yggdrasilctl} - GOOS=freebsd GOARCH=amd64 ./build && mv yggdrasil /tmp/upload/$CINAME-$CIVERSION-freebsd-amd64 && mv yggdrasilctl /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-freebsd-amd64; - GOOS=freebsd GOARCH=386 ./build && mv yggdrasil /tmp/upload/$CINAME-$CIVERSION-freebsd-i386 && mv yggdrasilctl /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-freebsd-i386; - - - run: - name: Build for Windows - command: | - rm -f {yggdrasil,yggdrasilctl} - 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; - - - persist_to_workspace: - root: /tmp - paths: - - upload - - upload: - machine: true - - steps: - - attach_workspace: - at: /tmp - - - store_artifacts: - path: /tmp/upload - destination: / - -workflows: - version: 2.1 - build: - jobs: - - lint - - build-linux - - build-macos - - build-other - - upload: - requires: - - build-linux - - build-macos - - build-other diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 27ff7fbd..00000000 --- a/appveyor.yml +++ /dev/null @@ -1,23 +0,0 @@ -version: '{build}' -pull_requests: - do_not_increment_build_number: true -os: Visual Studio 2019 -shallow_clone: false - -environment: - MSYS2_PATH_TYPE: inherit - CHERE_INVOKING: enabled_from_arguments - -build_script: -- cmd: cd %APPVEYOR_BUILD_FOLDER% -- curl -o C:\projects\golang.zip https://dl.google.com/go/go1.17.5.windows-amd64.zip -- 7z x C:\projects\golang.zip -oC:\projects\ -- cmd: set PATH=C:\projects\go\bin;%PATH% -- cmd: set GOROOT=C:\projects\go -- c:\msys64\usr\bin\bash -lc "./contrib/msi/build-msi.sh x64" -- c:\msys64\usr\bin\bash -lc "./contrib/msi/build-msi.sh x86" - -test: off - -artifacts: -- path: '*.msi' From e9caf989b84522be85fac176a61a901a724f3e77 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 18 Apr 2022 10:27:43 +0100 Subject: [PATCH 009/293] Enable CodeQL --- .github/workflows/codeql.yml | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 00000000..0e94b041 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,36 @@ +name: "CodeQL" + +on: + push: + branches: [ develop, future, master ] + pull_request: + branches: [ develop, master ] + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'go' ] + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 From 55f7874b357cbe0f591d1b14be4b6102ca6c1406 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 18 Apr 2022 10:30:40 +0100 Subject: [PATCH 010/293] Limit concurrency of CI runs --- .github/workflows/ci.yml | 4 ++++ .github/workflows/codeql.yml | 30 +++++++++++++++++------------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a884d762..0ce66554 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,6 +6,10 @@ on: release: workflow_dispatch: +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: lint: name: Lint diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 0e94b041..14cbec40 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -2,9 +2,13 @@ name: "CodeQL" on: push: - branches: [ develop, future, master ] + branches: [develop, future, master] pull_request: - branches: [ develop, master ] + branches: [develop, master] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: analyze: @@ -18,19 +22,19 @@ jobs: strategy: fail-fast: false matrix: - language: [ 'go' ] + language: ["go"] steps: - - name: Checkout repository - uses: actions/checkout@v3 + - name: Checkout repository + uses: actions/checkout@v3 - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - with: - languages: ${{ matrix.language }} + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} - - name: Autobuild - uses: github/codeql-action/autobuild@v2 + - name: Autobuild + uses: github/codeql-action/autobuild@v2 - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 From c3de1542b075b4f65dee147f701addb00a07a4ab Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 18 Apr 2022 10:33:33 +0100 Subject: [PATCH 011/293] Move CodeQL into main CI workflow --- .github/workflows/ci.yml | 30 ++++++++++++++++++++++++++- .github/workflows/codeql.yml | 40 ------------------------------------ 2 files changed, 29 insertions(+), 41 deletions(-) delete mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0ce66554..6bb7c7f2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,6 +24,34 @@ jobs: with: args: --issues-exit-code=1 + codeql: + name: Analyse + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: ["go"] + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + build-linux: strategy: fail-fast: false @@ -98,7 +126,7 @@ jobs: tests-ok: name: All tests passed - needs: [build-linux, build-windows, build-macos] + needs: [lint, codeql, build-linux, build-windows, build-macos] runs-on: ubuntu-latest if: ${{ !cancelled() }} steps: diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml deleted file mode 100644 index 14cbec40..00000000 --- a/.github/workflows/codeql.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: "CodeQL" - -on: - push: - branches: [develop, future, master] - pull_request: - branches: [develop, master] - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - - strategy: - fail-fast: false - matrix: - language: ["go"] - - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - with: - languages: ${{ matrix.language }} - - - name: Autobuild - uses: github/codeql-action/autobuild@v2 - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 From b77b018c4d996ae101c98794287888f229311335 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 18 Apr 2022 10:35:05 +0100 Subject: [PATCH 012/293] Modify workflow strategy --- .github/workflows/ci.yml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6bb7c7f2..b41e4336 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,11 +32,6 @@ jobs: contents: read security-events: write - strategy: - fail-fast: false - matrix: - language: ["go"] - steps: - name: Checkout repository uses: actions/checkout@v3 @@ -44,7 +39,7 @@ jobs: - name: Initialize CodeQL uses: github/codeql-action/init@v2 with: - languages: ${{ matrix.language }} + languages: go - name: Autobuild uses: github/codeql-action/autobuild@v2 From 5e89ab706faa06ca45dbcbb8cd54fde44df995c2 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 18 Apr 2022 15:20:45 +0100 Subject: [PATCH 013/293] Update README.md --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 2bfb5b1d..d0afae47 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # Yggdrasil -[![CircleCI](https://circleci.com/gh/yggdrasil-network/yggdrasil-go.svg?style=shield&circle-token=:circle-token -)](https://circleci.com/gh/yggdrasil-network/yggdrasil-go) +[![Build status](https://github.com/yggdrasil-network/yggdrasil-go/actions/workflows/ci.yml/badge.svg)](https://github.com/yggdrasil-network/yggdrasil-go/actions/workflows/ci.yml) ## Introduction From 42d4298e197e9f4455b9ed34e9b49847cc10af63 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 18 Apr 2022 15:23:52 +0100 Subject: [PATCH 014/293] Update ironwood to latest commit on `archive-ygg0.4` branch --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 236877c0..c93b8a0b 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/yggdrasil-network/yggdrasil-go go 1.17 require ( - github.com/Arceliar/ironwood v0.0.0-20211125050254-8951369625d0 + github.com/Arceliar/ironwood v0.0.0-20220409035209-b7f71f05435a github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 github.com/cheggaaa/pb/v3 v3.0.8 github.com/gologme/log v1.2.0 diff --git a/go.sum b/go.sum index 063b5998..c87112f7 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Arceliar/ironwood v0.0.0-20211125050254-8951369625d0 h1:QUqcb7BOcBU2p7Nax7pESOb8hrZYtI0Ts6j4v4mvcQo= -github.com/Arceliar/ironwood v0.0.0-20211125050254-8951369625d0/go.mod h1:RP72rucOFm5udrnEzTmIWLRVGQiV/fSUAQXJ0RST/nk= +github.com/Arceliar/ironwood v0.0.0-20220409035209-b7f71f05435a h1:yfbnOyqPcx2gi5cFIJ2rlPz5M6rFPHT/c8FgZmFjCdc= +github.com/Arceliar/ironwood v0.0.0-20220409035209-b7f71f05435a/go.mod h1:RP72rucOFm5udrnEzTmIWLRVGQiV/fSUAQXJ0RST/nk= github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 h1:WndgpSW13S32VLQ3ugUxx2EnnWmgba1kCqPkd4Gk1yQ= github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979/go.mod h1:6Lkn+/zJilRMsKmbmG1RPoamiArC6HS73xbwRyp3UyI= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= From e13657d2cad33cec5a58a6ebb9d31c407b5047a0 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 18 Apr 2022 15:27:47 +0100 Subject: [PATCH 015/293] Version 0.4.4 changelog --- CHANGELOG.md | 193 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 158 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c9c7e471..498d8338 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ # Changelog + All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) @@ -25,89 +26,114 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - in case of vulnerabilities. --> +## [0.4.4] - 2022-04-18 + +### Fixed + +- ICMPv6 "Packet Too Big" payload size has been increased, which should fix Path MTU Discovery (PMTUD) when two nodes have different `IfMTU` values configured + +### Changed + +- Go 1.17 is now required + ## [0.4.3] - 2022-02-06 + ### Added + - `bytes_sent`, `bytes_recvd` and `uptime` have been added to `getPeers` - Clearer logging when connections are rejected due to incompatible peer versions ### Fixed + - Latency-based parent selection tiebreak is now reliable on platforms even with low timer resolution - Tree distance calculation offsets have been corrected ## [0.4.2] - 2021-11-03 + ### Fixed + - Reverted a dependency update which resulted in problems building with Go 1.16 and running on Windows ## [0.4.1] - 2021-11-03 + ### Added + - TLS peerings now support Server Name Indication (SNI) - - The SNI is sent automatically if the peering URI contains a DNS name - - A custom SNI can be specified by adding the `?sni=domain.com` parameter to the peering URI + - The SNI is sent automatically if the peering URI contains a DNS name + - A custom SNI can be specified by adding the `?sni=domain.com` parameter to the peering URI - A new `ipv6rwc` API package now implements the IPv6-specific logic separate from the `tun` package ### Fixed + - A crash when calculating the partial public key for very high IPv6 addresses has been fixed - A crash due to a concurrent map write has been fixed - A crash due to missing TUN configuration has been fixed - A race condition in the keystore code has been fixed ## [0.4.0] - 2021-07-04 + ### Added + - New routing scheme, which is backwards incompatible with previous versions of Yggdrasil - - The wire protocol version number, exchanged as part of the peer setup handshake, has been increased to 0.4 - - Nodes running this new version **will not** be able to peer with earlier versions of Yggdrasil - - Please note that **the network may be temporarily unstable** while infrastructure is being upgraded to the new release + - The wire protocol version number, exchanged as part of the peer setup handshake, has been increased to 0.4 + - Nodes running this new version **will not** be able to peer with earlier versions of Yggdrasil + - Please note that **the network may be temporarily unstable** while infrastructure is being upgraded to the new release - TLS connections now use public key pinning - - If no public key was already pinned, then the public key received as part of the TLS handshake is pinned to the connection - - The public key received as part of the handshake is checked against the pinned keys, and if no match is found, the connection is rejected + - If no public key was already pinned, then the public key received as part of the TLS handshake is pinned to the connection + - The public key received as part of the handshake is checked against the pinned keys, and if no match is found, the connection is rejected ### Changed + - IP addresses are now derived from ed25519 public (signing) keys - - Previously, addresses were derived from a hash of X25519 (Diffie-Hellman) keys - - Importantly, this means that **all internal IPv6 addresses will change with this release** — this will affect anyone running public services or relying on Yggdrasil for remote access + - Previously, addresses were derived from a hash of X25519 (Diffie-Hellman) keys + - Importantly, this means that **all internal IPv6 addresses will change with this release** — this will affect anyone running public services or relying on Yggdrasil for remote access - It is now recommended to peer over TLS - - Link-local peers from multicast peer discovery will now connect over TLS, with the key from the multicast beacon pinned to the connection - - `socks://` peers now expect the destination endpoint to be a `tls://` listener, instead of a `tcp://` listener + - Link-local peers from multicast peer discovery will now connect over TLS, with the key from the multicast beacon pinned to the connection + - `socks://` peers now expect the destination endpoint to be a `tls://` listener, instead of a `tcp://` listener - Multicast peer discovery is now more configurable - - There are separate configuration options to control if beacons are sent, what port to listen on for incoming connections (if sending beacons), and whether or not to listen for beacons from other nodes (and open connections when receiving a beacon) - - Each configuration entry in the list specifies a regular expression to match against interface names - - If an interface matches multiple regex in the list, it will use the settings for the first entry in the list that it matches with + - There are separate configuration options to control if beacons are sent, what port to listen on for incoming connections (if sending beacons), and whether or not to listen for beacons from other nodes (and open connections when receiving a beacon) + - Each configuration entry in the list specifies a regular expression to match against interface names + - If an interface matches multiple regex in the list, it will use the settings for the first entry in the list that it matches with - The session and routing code has been entirely redesigned and rewritten - - This is still an early work-in-progress, so the code hasn't been as well tested or optimized as the old code base — please bear with us for these next few releases as we work through any bugs or issues - - Generally speaking, we expect to see reduced bandwidth use and improved reliability with the new design, especially in cases where nodes move around or change peerings frequently - - Cryptographic sessions no longer use a single shared (ephemeral) secret for the entire life of the session. Keys are now rotated regularly for ongoing sessions (currently rotated at least once per round trip exchange of traffic, subject to change in future releases) - - Source routing has been added. Under normal circumstances, this is what is used to forward session traffic (e.g. the user's IPv6 traffic) - - DHT-based routing has been added. This is used when the sender does not know a source route to the destination. Forwarding through the DHT is less efficient, but the only information that it requires the sender to know is the destination node's (static) key. This is primarily used during the key exchange at session setup, or as a temporary fallback when a source route fails due to changes in the network - - The new DHT design is no longer RPC-based, does not support crawling and does not inherently allow nodes to look up the owner of an arbitrary key. Responding to lookups is now implemented at the application level and a response is only sent if the destination key matches the node's `/128` IP or `/64` prefix - - The greedy routing scheme, used to forward all traffic in previous releases, is now only used for protocol traffic (i.e. DHT setup and source route discovery) - - The routing logic now lives in a [standalone library](https://github.com/Arceliar/ironwood). You are encouraged **not** to use it, as it's still considered pre-alpha, but it's available for those who want to experiment with the new routing algorithm in other contexts - - Session MTUs may be slightly lower now, in order to accommodate large packet headers if required + - This is still an early work-in-progress, so the code hasn't been as well tested or optimized as the old code base — please bear with us for these next few releases as we work through any bugs or issues + - Generally speaking, we expect to see reduced bandwidth use and improved reliability with the new design, especially in cases where nodes move around or change peerings frequently + - Cryptographic sessions no longer use a single shared (ephemeral) secret for the entire life of the session. Keys are now rotated regularly for ongoing sessions (currently rotated at least once per round trip exchange of traffic, subject to change in future releases) + - Source routing has been added. Under normal circumstances, this is what is used to forward session traffic (e.g. the user's IPv6 traffic) + - DHT-based routing has been added. This is used when the sender does not know a source route to the destination. Forwarding through the DHT is less efficient, but the only information that it requires the sender to know is the destination node's (static) key. This is primarily used during the key exchange at session setup, or as a temporary fallback when a source route fails due to changes in the network + - The new DHT design is no longer RPC-based, does not support crawling and does not inherently allow nodes to look up the owner of an arbitrary key. Responding to lookups is now implemented at the application level and a response is only sent if the destination key matches the node's `/128` IP or `/64` prefix + - The greedy routing scheme, used to forward all traffic in previous releases, is now only used for protocol traffic (i.e. DHT setup and source route discovery) + - The routing logic now lives in a [standalone library](https://github.com/Arceliar/ironwood). You are encouraged **not** to use it, as it's still considered pre-alpha, but it's available for those who want to experiment with the new routing algorithm in other contexts + - Session MTUs may be slightly lower now, in order to accommodate large packet headers if required - Many of the admin functions available over `yggdrasilctl` have been changed or removed as part of rewrites to the code - - Several remote `debug` functions have been added temporarily, to allow for crawling and census gathering during the transition to the new version, but we intend to remove this at some point in the (possibly distant) future - - The list of available functions will likely be expanded in future releases + - Several remote `debug` functions have been added temporarily, to allow for crawling and census gathering during the transition to the new version, but we intend to remove this at some point in the (possibly distant) future + - The list of available functions will likely be expanded in future releases - The configuration file format has been updated in response to the changed/removed features ### Removed + - Tunnel routing (a.k.a. crypto-key routing or "CKR") has been removed - - It was far too easy to accidentally break routing altogether by capturing the route to peers with the TUN adapter - - We recommend tunnelling an existing standard over Yggdrasil instead (e.g. `ip6gre`, `ip6gretap` or other similar encapsulations, using Yggdrasil IPv6 addresses as the tunnel endpoints) - - All `TunnelRouting` configuration options will no longer take effect + - It was far too easy to accidentally break routing altogether by capturing the route to peers with the TUN adapter + - We recommend tunnelling an existing standard over Yggdrasil instead (e.g. `ip6gre`, `ip6gretap` or other similar encapsulations, using Yggdrasil IPv6 addresses as the tunnel endpoints) + - All `TunnelRouting` configuration options will no longer take effect - Session firewall has been removed - - This was never a true firewall — it didn't behave like a stateful IP firewall, often allowed return traffic unexpectedly and was simply a way to prevent a node from being flooded with unwanted sessions, so the name could be misleading and usually lead to a false sense of security - - Due to design changes, the new code needs to address the possible memory exhaustion attacks in other ways and a single configurable list no longer makes sense - - Users who want a firewall or other packet filter mechansim should configure something supported by their OS instead (e.g. `ip6tables`) - - All `SessionFirewall` configuration options will no longer take effect + - This was never a true firewall — it didn't behave like a stateful IP firewall, often allowed return traffic unexpectedly and was simply a way to prevent a node from being flooded with unwanted sessions, so the name could be misleading and usually lead to a false sense of security + - Due to design changes, the new code needs to address the possible memory exhaustion attacks in other ways and a single configurable list no longer makes sense + - Users who want a firewall or other packet filter mechansim should configure something supported by their OS instead (e.g. `ip6tables`) + - All `SessionFirewall` configuration options will no longer take effect - `SIGHUP` handling to reload the configuration at runtime has been removed - - It was not obvious which parts of the configuration could be reloaded at runtime, and which required the application to be killed and restarted to take effect - - Reloading the config without restarting was also a delicate and bug-prone process, and was distracting from more important developments - - `SIGHUP` will be handled normally (i.e. by exiting) + - It was not obvious which parts of the configuration could be reloaded at runtime, and which required the application to be killed and restarted to take effect + - Reloading the config without restarting was also a delicate and bug-prone process, and was distracting from more important developments + - `SIGHUP` will be handled normally (i.e. by exiting) - `cmd/yggrasilsim` has been removed, and is unlikely to return to this repository ## [0.3.16] - 2021-03-18 + ### Added + - New simulation code under `cmd/yggdrasilsim` (work-in-progress) ### Changed + - Multi-threading in the switch - Swich lookups happen independently for each (incoming) peer connection, instead of being funneled to a single dedicated switch worker - Packets are queued for each (outgoing) peer connection, instead of being handled by a single dedicated switch worker @@ -123,13 +149,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Upgrade build to Go 1.16 ### Fixed + - Fixed a bug where the connection listener could exit prematurely due to resoruce exhaustion (if e.g. too many connections were opened) - Fixed DefaultIfName for OpenBSD (`/dev/tun0` -> `tun0`) - Fixed an issue where a peer could sometimes never be added to the switch - Fixed a goroutine leak that could occur if a peer with an open connection continued to spam additional connection attempts ## [0.3.15] - 2020-09-27 + ### Added + - Support for pinning remote public keys in peering strings has been added, e.g. - By signing public key: `tcp://host:port?ed25519=key` - By encryption public key: `tcp://host:port?curve25519=key` @@ -139,25 +168,32 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Added support for SOCKS proxy authentication, e.g. `socks://user@password:host/...` ### Fixed + - Some bugs in the multicast code that could cause unnecessary CPU usage have been fixed - A possible multicast deadlock on macOS when enumerating interfaces has been fixed - A deadlock in the connection code has been fixed - Updated HJSON dependency that caused some build problems ### Changed + - `DisconnectPeer` and `RemovePeer` have been separated and implemented properly now - Less nodes are stored in the DHT now, reducing ambient network traffic and possible instability - Default config file for FreeBSD is now at `/usr/local/etc/yggdrasil.conf` instead of `/etc/yggdrasil.conf` ## [0.3.14] - 2020-03-28 + ### Fixed + - Fixes a memory leak that may occur if packets are incorrectly never removed from a switch queue ### Changed + - Make DHT searches a bit more reliable by tracking the 16 most recently visited nodes ## [0.3.13] - 2020-02-21 + ### Added + - Support for the Wireguard TUN driver, which now replaces Water and provides far better support and performance on Windows - Windows `.msi` installer files are now supported (bundling the Wireguard TUN driver) - NodeInfo code is now actorised, should be more reliable @@ -165,6 +201,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - The Yggdrasil API now supports dialing a remote node using the public key instead of the Node ID ### Changed + - The `-loglevel` command line parameter is now cumulative and automatically includes all levels below the one specified - DHT search code has been significantly simplified and processes rumoured nodes in parallel, speeding up search time - DHT search results are now sorted @@ -172,26 +209,32 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - The Yggdrasil API now returns public keys instead of node IDs when querying for local and remote addresses ### Fixed + - The multicast code no longer panics when shutting down the node - A potential OOB error when calculating IPv4 flow labels (when tunnel routing is enabled) has been fixed - A bug resulting in incorrect idle notifications in the switch should now be fixed - MTUs are now using a common datatype throughout the codebase ### Removed + - TAP mode has been removed entirely, since it is no longer supported with the Wireguard TUN package. Please note that if you are using TAP mode, you may need to revise your config! - NetBSD support has been removed until the Wireguard TUN package supports NetBSD ## [0.3.12] - 2019-11-24 + ### Added + - New API functions `SetMaximumSessionMTU` and `GetMaximumSessionMTU` - New command line parameters `-address` and `-subnet` for getting the address/subnet from the config file, for use with `-useconffile` or `-useconf` - A warning is now produced in the Yggdrasil output at startup when the MTU in the config is invalid or has been adjusted for some reason ### Changed + - On Linux, outgoing `InterfacePeers` connections now use `SO_BINDTODEVICE` to prefer an outgoing interface - The `genkeys` utility is now in `cmd` rather than `misc` ### Fixed + - A data race condition has been fixed when updating session coordinates - A crash when shutting down when no multicast interfaces are configured has been fixed - A deadlock when calling `AddPeer` multiple times has been fixed @@ -200,10 +243,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - The MTU calculation now correctly accounts for ethernet headers when running in TAP mode ## [0.3.11] - 2019-10-25 + ### Added + - Support for TLS listeners and peers has been added, allowing the use of `tls://host:port` in `Peers`, `InterfacePeers` and `Listen` configuration settings - this allows hiding Yggdrasil peerings inside regular TLS connections ### Changed + - Go 1.13 or later is now required for building Yggdrasil - Some exported API functions have been updated to work with standard Go interfaces: - `net.Conn` instead of `yggdrasil.Conn` @@ -213,27 +259,35 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Multicast module reloading behaviour has been improved ### Fixed + - An incorrectly held mutex in the crypto-key routing code has been fixed - Multicast module no longer opens a listener socket if no multicast interfaces are configured ## [0.3.10] - 2019-10-10 + ### Added + - The core library now includes several unit tests for peering and `yggdrasil.Conn` connections ### Changed + - On recent Linux kernels, Yggdrasil will now set the `tcp_congestion_control` algorithm used for its own TCP sockets to [BBR](https://github.com/google/bbr), which reduces latency under load - The systemd service configuration in `contrib` (and, by extension, some of our packages) now attempts to load the `tun` module, in case TUN/TAP support is available but not loaded, and it restricts Yggdrasil to the `CAP_NET_ADMIN` capability for managing the TUN/TAP adapter, rather than letting it do whatever the (typically `root`) user can do ### Fixed + - The `yggdrasil.Conn.RemoteAddr()` function no longer blocks, fixing a deadlock when CKR is used while under heavy load ## [0.3.9] - 2019-09-27 + ### Added + - Yggdrasil will now complain more verbosely when a peer URI is incorrectly formatted - Soft-shutdown methods have been added, allowing a node to shut down gracefully when terminated - New multicast interval logic which sends multicast beacons more often when Yggdrasil is first started to increase the chance of finding nearby nodes quickly after startup ### Changed + - The switch now buffers packets more eagerly in an attempt to give the best link a chance to send, which appears to reduce packet reordering when crossing aggregate sets of peerings - Substantial amounts of the codebase have been refactored to use the actor model, which should substantially reduce the chance of deadlocks - Nonce tracking in sessions has been modified so that memory usage is reduced whilst still only allowing duplicate packets within a small window @@ -242,6 +296,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - The maximum queue size is now managed exclusively by the switch rather than by the core ### Fixed + - The broken `hjson-go` dependency which affected builds of the previous version has now been resolved in the module manifest - Some minor memory leaks in the switch have been fixed, which improves memory usage on mobile builds - A memory leak in the add-peer loop has been fixed @@ -253,10 +308,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - A panic which could occur when the TUN/TAP interface reads an undersized/corrupted packet has been fixed ### Removed + - A number of legacy debug functions have now been removed and a number of exported API functions are now better documented ## [0.3.8] - 2019-08-21 + ### Changed + - Yggdrasil can now send multiple packets from the switch at once, which results in improved throughput with smaller packets or lower MTUs - Performance has been slightly improved by not allocating cancellations where not necessary - Crypto-key routing options have been renamed for clarity @@ -269,20 +327,25 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - New nonce tracking should help to reduce the number of packets dropped as a result of multiple/aggregate paths or congestion control in the switch ### Fixed + - A deadlock was fixed in the session code which could result in Yggdrasil failing to pass traffic after some time ### Security + - Address verification was not strict enough, which could result in a malicious session sending traffic with unexpected or spoofed source or destination addresses which Yggdrasil could fail to reject - Versions `0.3.6` and `0.3.7` are vulnerable - users of these versions should upgrade as soon as possible - Versions `0.3.5` and earlier are not affected ## [0.3.7] - 2019-08-14 + ### Changed + - The switch should now forward packets along a single path more consistently in cases where congestion is low and multiple equal-length paths exist, which should improve stability and result in fewer out-of-order packets - Sessions should now be more tolerant of out-of-order packets, by replacing a bitmask with a variable sized heap+map structure to track recently received nonces, which should reduce the number of packets dropped due to reordering when multiple paths are used or multiple independent flows are transmitted through the same session - The admin socket can no longer return a dotfile representation of the known parts of the network, this could be rebuilt by clients using information from `getSwitchPeers`,`getDHT` and `getSessions` ### Fixed + - A number of significant performance regressions introduced in version 0.3.6 have been fixed, resulting in better performance - Flow labels are now used to prioritise traffic flows again correctly - In low-traffic scenarios where there are multiple peerings between a pair of nodes, Yggdrasil now prefers the most active peering instead of the least active, helping to reduce packet reordering @@ -296,13 +359,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - A number of minor allocation and pointer fixes ## [0.3.6] - 2019-08-03 + ### Added + - Yggdrasil now has a public API with interfaces such as `yggdrasil.ConnDialer`, `yggdrasil.ConnListener` and `yggdrasil.Conn` for using Yggdrasil as a transport directly within applications - Session gatekeeper functions, part of the API, which can be used to control whether to allow or reject incoming or outgoing sessions dynamically (compared to the previous fixed whitelist/blacklist approach) - Support for logging to files or syslog (where supported) - Platform defaults now include the ability to set sane defaults for multicast interfaces ### Changed + - Following a massive refactoring exercise, Yggdrasil's codebase has now been broken out into modules - Core node functionality in the `yggdrasil` package with a public API - This allows Yggdrasil to be integrated directly into other applications and used as a transport @@ -315,6 +381,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Upstream dependency references have been updated, which includes a number of fixes in the Water library ### Fixed + - Multicast discovery is no longer disabled if the nominated interfaces aren't available on the system yet, e.g. during boot - Multicast interfaces are now re-evaluated more frequently so that Yggdrasil doesn't need to be restarted to use interfaces that have become available since startup - Admin socket error cases are now handled better @@ -329,12 +396,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Lots of small bug tweaks and clean-ups throughout the codebase ## [0.3.5] - 2019-03-13 + ### Fixed + - The `AllowedEncryptionPublicKeys` option has now been fixed to handle incoming connections properly and no longer blocks outgoing connections (this was broken in v0.3.4) - Multicast TCP listeners will now be stopped correctly when the link-local address on the interface changes or disappears altogether ## [0.3.4] - 2019-03-12 + ### Added + - Support for multiple listeners (although currently only TCP listeners are supported) - New multicast behaviour where each multicast interface is given its own link-local listener and does not depend on the `Listen` configuration - Blocking detection in the switch to avoid parenting a blocked peer @@ -345,6 +416,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Added `LinkLocalTCPPort` option for controlling the port number that link-local TCP listeners will listen on by default when setting up `MulticastInterfaces` (a node restart is currently required for changes to `LinkLocalTCPPort` to take effect - it cannot be updated by reloading config during runtime) ### Changed + - The `Listen` configuration statement is now an array instead of a string - The `Listen` configuration statement should now conform to the same formatting as peers with the protocol prefix, e.g. `tcp://[::]:0` - Session workers are now non-blocking @@ -353,6 +425,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Peer forwarding is now prioritised instead of randomised ### Fixed + - Admin socket `getTunTap` call now returns properly instead of claiming no interface is enabled in all cases - Handling of `getRoutes` etc in `yggdrasilctl` is now working - Local interface names are no longer leaked in multicast packets @@ -360,7 +433,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Yggdrasil now correctly responds to multicast interfaces going up and down during runtime ## [0.3.3] - 2019-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. @@ -369,26 +444,33 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - 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 - The ability to hide NodeInfo defaults through either setting the `NodeInfoPrivacy` option or through setting individual `NodeInfo` attributes to `null` ### Changed + - The `armhf` build now targets ARMv6 instead of ARMv7, adding support for Raspberry Pi Zero and other older models, amongst others ### Fixed + - DHT entries are now populated using a copy in memory to fix various potential DHT bugs - DHT traffic should now throttle back exponentially to reduce idle traffic - Adjust how nodes are inserted into the DHT which should help to reduce some incorrect DHT traffic @@ -396,7 +478,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - In TUN mode, ICMPv6 packets are now ignored whereas they were incorrectly processed before ## [0.3.1] - 2018-12-17 + ### Added + - Build name and version is now imprinted onto the binaries if available/specified during build - Ability to disable admin socket with `AdminListen: none` - `AF_UNIX` domain sockets for the admin socket @@ -406,18 +490,22 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Adds flags `-c`, `-l` and `-t` to `build` script for specifying `GCFLAGS`, `LDFLAGS` or whether to keep symbol/DWARF tables ### Changed + - Default `AdminListen` in newly generated config is now `unix:///var/run/yggdrasil.sock` - Formatting of `getRoutes` in the admin socket has been improved - Debian package now adds `yggdrasil` group to assist with `AF_UNIX` admin socket permissions - Crypto, address and other utility code refactored into separate Go packages ### Fixed + - Switch peer convergence is now much faster again (previously it was taking up to a minute once the peering was established) - `yggdrasilctl` is now less prone to crashing when parameters are specified incorrectly - Panic fixed when `Peers` or `InterfacePeers` was commented out ## [0.3.0] - 2018-12-12 + ### Added + - Crypto-key routing support for tunnelling both IPv4 and IPv6 over Yggdrasil - Add advanced `SwitchOptions` in configuration file for tuning the switch - Add `dhtPing` to the admin socket to aid in crawling the network @@ -430,6 +518,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - `yggdrasilctl` now returns more useful logging in the event of a fatal error ### Changed + - Switched to Chord DHT (instead of Kademlia, although still compatible at the protocol level) - The `AdminListen` option and `yggdrasilctl` now default to `unix:///var/run/yggdrasil.sock` on BSDs, macOS and Linux - Cleaned up some of the parameter naming in the admin socket @@ -439,12 +528,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - `yggdrasilctl` now has more useful help text (with `-help` or when no arguments passed) ### Fixed + - Memory leaks in the DHT fixed - Crash fixed where the ICMPv6 NDP goroutine would incorrectly start in TUN mode - Removing peers from the switch table if they stop sending switch messages but keep the TCP connection alive ## [0.2.7] - 2018-10-13 + ### Added + - Session firewall, which makes it possible to control who can open sessions with your node - Add `getSwitchQueues` to admin socket - Add `InterfacePeers` for configuring static peerings via specific network interfaces @@ -452,81 +544,106 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - FreeBSD service script in `contrib` ## Changed + - CircleCI builds are now built with Go 1.11 instead of Go 1.9 ## Fixed + - Race condition in the switch table, reported by trn - Debug builds are now tested by CircleCI as well as platform release builds - Port number fixed on admin graph from unknown nodes ## [0.2.6] - 2018-07-31 + ### Added + - Configurable TCP timeouts to assist in peering over Tor/I2P - Prefer IPv6 flow label when extending coordinates to sort backpressure queues - `arm64` builds through CircleCI ### Changed + - Sort dot graph links by integer value ## [0.2.5] - 2018-07-19 + ### Changed + - Make `yggdrasilctl` less case sensitive - More verbose TCP disconnect messages ### Fixed + - Fixed debug builds - Cap maximum MTU on Linux in TAP mode - Process successfully-read TCP traffic before checking for / handling errors (fixes EOF behavior) ## [0.2.4] - 2018-07-08 + ### Added + - Support for UNIX domain sockets for the admin socket using `unix:///path/to/file.sock` - Centralised platform-specific defaults ### Changed + - Backpressure tuning, including reducing resource consumption ### Fixed + - macOS local ping bug, which previously prevented you from pinging your own `utun` adapter's IPv6 address ## [0.2.3] - 2018-06-29 + ### Added + - Begin keeping changelog (incomplete and possibly inaccurate information before this point). - Build RPMs in CircleCI using alien. This provides package support for Fedora, Red Hat Enterprise Linux, CentOS and other RPM-based distributions. ### Changed + - Local backpressure improvements. - Change `box_pub_key` to `key` in admin API for simplicity. - Session cleanup. ## [0.2.2] - 2018-06-21 + ### Added + - Add `yggdrasilconf` utility for testing with the `vyatta-yggdrasil` package. - Add a randomized retry delay after TCP disconnects, to prevent synchronization livelocks. ### Changed + - Update build script to strip by default, which significantly reduces the size of the binary. - Add debug `-d` and UPX `-u` flags to the `build` script. - Start pprof in debug builds based on an environment variable (e.g. `PPROFLISTEN=localhost:6060`), instead of a flag. ### Fixed + - Fix typo in big-endian BOM so that both little-endian and big-endian UTF-16 files are detected correctly. ## [0.2.1] - 2018-06-15 + ### Changed + - The address range was moved from `fd00::/8` to `200::/7`. This range was chosen as it is marked as deprecated. The change prevents overlap with other ULA privately assigned ranges. ### Fixed + - UTF-16 detection conversion for configuration files, which can particularly be a problem on Windows 10 if a configuration file is generated from within PowerShell. - Fixes to the Debian package control file. - Fixes to the launchd service for macOS. - Fixes to the DHT and switch. ## [0.2.0] - 2018-06-13 + ### Added + - Exchange version information during connection setup, to prevent connections with incompatible versions. ### Changed + - Wire format changes (backwards incompatible). - Less maintenance traffic per peer. - Exponential back-off for DHT maintenance traffic (less maintenance traffic for known good peers). @@ -534,18 +651,24 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Use local queue sizes for a sort of local-only backpressure routing, instead of the removed bandwidth estimates, when deciding where to send a packet. ### Removed + - UDP peering, this may be added again if/when a better implementation appears. - Per peer bandwidth estimation, as this has been replaced with an early local backpressure implementation. ## [0.1.0] - 2018-02-01 + ### Added + - Adopt semantic versioning. ### Changed + - Wire format changes (backwards incompatible). - Many other undocumented changes leading up to this release and before the next one. ## [0.0.1] - 2017-12-28 + ### Added + - First commit. - Initial public release. From 4ddebb338d6e115d6c09fffadb338869c4745b12 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 18 Apr 2022 15:29:43 +0100 Subject: [PATCH 016/293] Update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 498d8338..6c6aa506 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,7 +34,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Changed -- Go 1.17 is now required +- Go 1.17 is now required to build Yggdrasil ## [0.4.3] - 2022-02-06 From c19319df5e4e773d8668d5a5b4999a639c8113c0 Mon Sep 17 00:00:00 2001 From: Rubikoid Date: Thu, 17 Mar 2022 02:43:28 +0300 Subject: [PATCH 017/293] Fix coords print --- cmd/yggdrasilctl/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/yggdrasilctl/main.go b/cmd/yggdrasilctl/main.go index 788b4f19..180bea09 100644 --- a/cmd/yggdrasilctl/main.go +++ b/cmd/yggdrasilctl/main.go @@ -300,7 +300,7 @@ func handleGetSelf(res map[string]interface{}, verbose bool) { if boxSigKey, ok := v.(map[string]interface{})["key"].(string); ok { fmt.Println("Public key:", boxSigKey) } - if coords, ok := v.(map[string]interface{})["coords"].(string); ok { + if coords, ok := v.(map[string]interface{})["coords"].([]interface{}); ok { fmt.Println("Coords:", coords) } if verbose { From 88a0a3e8fbdb174938f7222acc1dad41eca76df0 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 7 Jul 2022 17:03:29 +0100 Subject: [PATCH 018/293] Fix data races in `handleProto` (observed by @majestrate) --- src/core/proto.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/core/proto.go b/src/core/proto.go index 5af59cc8..3c68c0e3 100644 --- a/src/core/proto.go +++ b/src/core/proto.go @@ -65,10 +65,16 @@ func (p *protoHandler) handleProto(from phony.Actor, key keyArray, bs []byte) { case typeProtoNodeInfoResponse: p.nodeinfo.handleRes(p, key, bs[1:]) case typeProtoDebug: - p._handleDebug(key, bs[1:]) + p.handleDebug(from, key, bs[1:]) } } +func (p *protoHandler) handleDebug(from phony.Actor, key keyArray, bs []byte) { + p.Act(from, func() { + p._handleDebug(key, bs) + }) +} + func (p *protoHandler) _handleDebug(key keyArray, bs []byte) { if len(bs) == 0 { return From 234addc81f3495e110db31d1f9fb9822adcae701 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 7 Jul 2022 18:17:27 +0100 Subject: [PATCH 019/293] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c6aa506..0341e6d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Fixed - ICMPv6 "Packet Too Big" payload size has been increased, which should fix Path MTU Discovery (PMTUD) when two nodes have different `IfMTU` values configured +- A crash has been fixed when handling debug packet responses +- `yggdrasilctl getSelf` should now report coordinates correctly again ### Changed From df7ca3a5b888ac487026e469bcb9af037d8c9aa6 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 7 Jul 2022 18:17:39 +0100 Subject: [PATCH 020/293] Update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0341e6d9..34001548 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,7 +26,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - in case of vulnerabilities. --> -## [0.4.4] - 2022-04-18 +## [0.4.4] - 2022-07-07 ### Fixed From 8c454a146cb70aa07ee2c87af964f5c1394da299 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 7 Jul 2022 18:19:15 +0100 Subject: [PATCH 021/293] Silence incorrect linter warning --- src/address/address.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/address/address.go b/src/address/address.go index c7f2eb46..d56be80d 100644 --- a/src/address/address.go +++ b/src/address/address.go @@ -108,7 +108,7 @@ func SubnetForKey(publicKey ed25519.PublicKey) *Subnet { } var snet Subnet copy(snet[:], addr[:]) - prefix := GetPrefix() + prefix := GetPrefix() // nolint:staticcheck snet[len(prefix)-1] |= 0x01 return &snet } @@ -117,7 +117,7 @@ func SubnetForKey(publicKey ed25519.PublicKey) *Subnet { // This is used for key lookup. func (a *Address) GetKey() ed25519.PublicKey { var key [ed25519.PublicKeySize]byte - prefix := GetPrefix() + prefix := GetPrefix() // nolint:staticcheck ones := int(a[len(prefix)]) for idx := 0; idx < ones; idx++ { key[idx/8] |= 0x80 >> byte(idx%8) From 5616b9fc847cabb23b96b470e13a90f00b5bad15 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 24 Jul 2022 10:23:25 +0100 Subject: [PATCH 022/293] Don't lose my work --- cmd/yggdrasil/main.go | 37 +++++++-- src/core/api.go | 6 +- src/core/core.go | 174 +++++++++++++++++------------------------- src/core/link.go | 9 +-- src/core/options.go | 30 ++++++++ src/core/tcp.go | 8 +- 6 files changed, 140 insertions(+), 124 deletions(-) create mode 100644 src/core/options.go diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 58b8230d..7f751833 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -36,7 +36,7 @@ import ( ) type node struct { - core core.Core + core *core.Core config *config.NodeConfig tuntap *tuntap.TunAdapter multicast *multicast.Multicast @@ -327,11 +327,32 @@ func run(args yggArgs, ctx context.Context, done chan struct{}) { // Setup the Yggdrasil node itself. The node{} type includes a Core, so we // don't need to create this manually. + sk, err := hex.DecodeString(cfg.PrivateKey) + if err != nil { + panic(err) + } + options := []core.SetupOption{ + core.IfName(cfg.IfName), + core.IfMTU(cfg.IfMTU), + } + for _, peer := range cfg.Peers { + options = append(options, core.Peer{URI: peer}) + } + for intf, peers := range cfg.InterfacePeers { + for _, peer := range peers { + options = append(options, core.Peer{URI: peer, SourceInterface: intf}) + } + } + for _, allowed := range cfg.AllowedPublicKeys { + k, err := hex.DecodeString(allowed) + if err != nil { + panic(err) + } + options = append(options, core.AllowedPublicKey(k[:])) + } n := node{config: cfg} - // Now start Yggdrasil - this starts the DHT, router, switch and other core - // components needed for Yggdrasil to operate - if err = n.core.Start(cfg, logger); err != nil { - logger.Errorln("An error occurred during startup") + n.core, err = core.New(sk[:], options...) + if err != nil { panic(err) } // Register the session firewall gatekeeper function @@ -340,21 +361,21 @@ func run(args yggArgs, ctx context.Context, done chan struct{}) { n.multicast = &multicast.Multicast{} n.tuntap = &tuntap.TunAdapter{} // Start the admin socket - if err := n.admin.Init(&n.core, cfg, logger, nil); err != nil { + if err := n.admin.Init(n.core, cfg, logger, nil); err != nil { logger.Errorln("An error occurred initialising admin socket:", err) } else if err := n.admin.Start(); err != nil { logger.Errorln("An error occurred starting admin socket:", err) } n.admin.SetupAdminHandlers(n.admin) // Start the multicast interface - if err := n.multicast.Init(&n.core, cfg, logger, nil); err != nil { + if err := n.multicast.Init(n.core, cfg, logger, nil); err != nil { logger.Errorln("An error occurred initialising multicast:", err) } else if err := n.multicast.Start(); err != nil { logger.Errorln("An error occurred starting multicast:", err) } n.multicast.SetupAdminHandlers(n.admin) // Start the TUN/TAP interface - rwc := ipv6rwc.NewReadWriteCloser(&n.core) + rwc := ipv6rwc.NewReadWriteCloser(n.core) if err := n.tuntap.Init(rwc, cfg, logger, nil); err != nil { logger.Errorln("An error occurred initialising TUN/TAP:", err) } else if err := n.tuntap.Start(); err != nil { diff --git a/src/core/api.go b/src/core/api.go index fabd7439..30e7e0f6 100644 --- a/src/core/api.go +++ b/src/core/api.go @@ -2,8 +2,6 @@ package core import ( "crypto/ed25519" - "sync/atomic" - "time" //"encoding/hex" "encoding/json" @@ -27,6 +25,7 @@ type Self struct { Coords []uint64 } +/* type Peer struct { Key ed25519.PublicKey Root ed25519.PublicKey @@ -37,6 +36,7 @@ type Peer struct { TXBytes uint64 Uptime time.Duration } +*/ type DHTEntry struct { Key ed25519.PublicKey @@ -62,6 +62,7 @@ func (c *Core) GetSelf() Self { return self } +/* func (c *Core) GetPeers() []Peer { var peers []Peer names := make(map[net.Conn]string) @@ -90,6 +91,7 @@ func (c *Core) GetPeers() []Peer { } return peers } +*/ func (c *Core) GetDHT() []DHTEntry { var dhts []DHTEntry diff --git a/src/core/core.go b/src/core/core.go index 0332980b..df3888af 100644 --- a/src/core/core.go +++ b/src/core/core.go @@ -3,12 +3,11 @@ package core import ( "context" "crypto/ed25519" - "encoding/hex" - "errors" "fmt" "io/ioutil" "net" "net/url" + "os" "time" iwe "github.com/Arceliar/ironwood/encrypted" @@ -16,9 +15,9 @@ import ( "github.com/Arceliar/phony" "github.com/gologme/log" - "github.com/yggdrasil-network/yggdrasil-go/src/config" - //"github.com/yggdrasil-network/yggdrasil-go/src/crypto" + "github.com/yggdrasil-network/yggdrasil-go/src/crypto" "github.com/yggdrasil-network/yggdrasil-go/src/version" + //"github.com/yggdrasil-network/yggdrasil-go/src/crypto" ) // The Core object represents the Yggdrasil node. You should create a Core @@ -29,62 +28,95 @@ type Core struct { // guarantee that it will be covered by the mutex phony.Inbox *iwe.PacketConn - config *config.NodeConfig // Config + ctx context.Context + cancel context.CancelFunc secret ed25519.PrivateKey public ed25519.PublicKey links links proto protoHandler log *log.Logger addPeerTimer *time.Timer - ctx context.Context - ctxCancel context.CancelFunc + config struct { + _peers map[Peer]struct{} // configurable after startup + _listeners map[ListenAddress]struct{} // configurable after startup + nodeinfo NodeInfo // immutable after startup + nodeinfoPrivacy NodeInfoPrivacy // immutable after startup + ifname IfName // immutable after startup + ifmtu IfMTU // immutable after startup + _allowedPublicKeys map[[32]byte]struct{} // configurable after startup + } } -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 - // This is pretty much required to completely avoid race conditions - c.config.RLock() - defer c.config.RUnlock() +func New(secret ed25519.PrivateKey, opts ...SetupOption) (*Core, error) { + if len(secret) != ed25519.PrivateKeySize { + return nil, fmt.Errorf("private key is incorrect length") + } + c := &Core{ + secret: secret, + public: secret.Public().(ed25519.PublicKey), + log: log.New(os.Stdout, "", 0), // TODO: not this + } + c.ctx, c.cancel = context.WithCancel(context.Background()) + var err error + if c.PacketConn, err = iwe.NewPacketConn(c.secret); err != nil { + return nil, fmt.Errorf("error creating encryption: %w", err) + } + for _, opt := range opts { + c._applyOption(opt) + } if c.log == nil { c.log = log.New(ioutil.Discard, "", 0) } - - sigPriv, err := hex.DecodeString(c.config.PrivateKey) - if err != nil { - return err - } - if len(sigPriv) < ed25519.PrivateKeySize { - return errors.New("PrivateKey is incorrect length") - } - - c.secret = ed25519.PrivateKey(sigPriv) - c.public = c.secret.Public().(ed25519.PublicKey) - // TODO check public against current.PublicKey, error if they don't match - - c.PacketConn, err = iwe.NewPacketConn(c.secret) - c.ctx, c.ctxCancel = context.WithCancel(context.Background()) c.proto.init(c) - if err := c.proto.nodeinfo.setNodeInfo(c.config.NodeInfo, c.config.NodeInfoPrivacy); err != nil { - return fmt.Errorf("setNodeInfo: %w", err) + if err := c.links.init(c); err != nil { + return nil, fmt.Errorf("error initialising links: %w", err) + } + if err := c.proto.nodeinfo.setNodeInfo(c.config.nodeinfo, bool(c.config.nodeinfoPrivacy)); err != nil { + return nil, fmt.Errorf("error setting node info: %w", err) + } + c.addPeerTimer = time.AfterFunc(time.Minute, func() { + c.Act(nil, c._addPeerLoop) + }) + if name := version.BuildName(); name != "unknown" { + c.log.Infoln("Build name:", name) + } + if version := version.BuildVersion(); version != "unknown" { + c.log.Infoln("Build version:", version) + } + return c, nil +} + +func (c *Core) _applyOption(opt SetupOption) { + switch v := opt.(type) { + case Peer: + c.config._peers[v] = struct{}{} + case ListenAddress: + c.config._listeners[v] = struct{}{} + case NodeInfo: + c.config.nodeinfo = v + case NodeInfoPrivacy: + c.config.nodeinfoPrivacy = v + case IfName: + c.config.ifname = v + case IfMTU: + c.config.ifmtu = v + case AllowedPublicKey: + pk := crypto.SigPubKey{} + copy(pk[:], v) + c.config._allowedPublicKeys[pk] = struct{}{} } - return err } // 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() { - c.config.RLock() - defer c.config.RUnlock() - if c.addPeerTimer == nil { return } // Add peers from the Peers section - for _, peer := range c.config.Peers { + for peer := range c.config._peers { go func(peer string, intf string) { u, err := url.Parse(peer) if err != nil { @@ -93,22 +125,7 @@ func (c *Core) _addPeerLoop() { if err := c.CallPeer(u, intf); err != nil { c.log.Errorln("Failed to add peer:", err) } - }(peer, "") // TODO: this should be acted and not in a goroutine? - } - - // Add peers from the InterfacePeers section - for intf, intfpeers := range c.config.InterfacePeers { - for _, peer := range intfpeers { - go func(peer string, intf string) { - u, err := url.Parse(peer) - if err != nil { - c.log.Errorln("Failed to parse peer url:", peer, err) - } - if err := c.CallPeer(u, intf); err != nil { - c.log.Errorln("Failed to add peer:", err) - } - }(peer, intf) // TODO: this should be acted and not in a goroutine? - } + }(peer.URI, peer.SourceInterface) // TODO: this should be acted and not in a goroutine? } c.addPeerTimer = time.AfterFunc(time.Minute, func() { @@ -116,49 +133,6 @@ func (c *Core) _addPeerLoop() { }) } -// Start starts up Yggdrasil using the provided config.NodeConfig, and outputs -// debug logging through the provided log.Logger. The started stack will include -// TCP and UDP sockets, a multicast discovery socket, an admin socket, router, -// switch and DHT node. A config.NodeState is returned which contains both the -// current and previous configurations (from reconfigures). -func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) (err error) { - phony.Block(c, func() { - err = c._start(nc, log) - }) - return -} - -// This function is unsafe and should only be ran by the core actor. -func (c *Core) _start(nc *config.NodeConfig, log *log.Logger) error { - c.log = log - c.config = nc - - if name := version.BuildName(); name != "unknown" { - c.log.Infoln("Build name:", name) - } - if version := version.BuildVersion(); version != "unknown" { - c.log.Infoln("Build version:", version) - } - - c.log.Infoln("Starting up...") - if err := c._init(); err != nil { - c.log.Errorln("Failed to initialize core") - return err - } - - if err := c.links.init(c); err != nil { - c.log.Errorln("Failed to start link interfaces") - return err - } - - c.addPeerTimer = time.AfterFunc(0, func() { - c.Act(nil, c._addPeerLoop) - }) - - c.log.Infoln("Startup complete") - return nil -} - // Stop shuts down the Yggdrasil node. func (c *Core) Stop() { phony.Block(c, func() { @@ -168,17 +142,9 @@ func (c *Core) Stop() { }) } -func (c *Core) Close() error { - var err error - phony.Block(c, func() { - err = c._close() - }) - return err -} - // This function is unsafe and should only be ran by the core actor. func (c *Core) _close() error { - c.ctxCancel() + c.cancel() err := c.PacketConn.Close() if c.addPeerTimer != nil { c.addPeerTimer.Stop() diff --git a/src/core/link.go b/src/core/link.go index f96c9be9..0b7e50a9 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -1,6 +1,7 @@ package core import ( + "bytes" "crypto/ed25519" "encoding/hex" "errors" @@ -215,12 +216,10 @@ func (intf *link) handler() (chan struct{}, error) { } } // Check if we're authorized to connect to this key / IP - intf.links.core.config.RLock() - allowed := intf.links.core.config.AllowedPublicKeys - intf.links.core.config.RUnlock() + allowed := intf.links.core.config._allowedPublicKeys isallowed := len(allowed) == 0 - for _, k := range allowed { - if k == hex.EncodeToString(meta.key) { // TODO: this is yuck + for k := range allowed { + if bytes.Equal(k[:], meta.key) { isallowed = true break } diff --git a/src/core/options.go b/src/core/options.go new file mode 100644 index 00000000..0210a4ef --- /dev/null +++ b/src/core/options.go @@ -0,0 +1,30 @@ +package core + +import ( + "crypto/ed25519" +) + +type SetupOption interface { + isSetupOption() +} + +type ListenAddress string +type AdminListenAddress string +type Peer struct { + URI string + SourceInterface string +} +type NodeInfo map[string]interface{} +type NodeInfoPrivacy bool +type IfName string +type IfMTU uint16 +type AllowedPublicKey ed25519.PublicKey + +func (a ListenAddress) isSetupOption() {} +func (a AdminListenAddress) isSetupOption() {} +func (a Peer) isSetupOption() {} +func (a NodeInfo) isSetupOption() {} +func (a NodeInfoPrivacy) isSetupOption() {} +func (a IfName) isSetupOption() {} +func (a IfMTU) isSetupOption() {} +func (a AllowedPublicKey) isSetupOption() {} diff --git a/src/core/tcp.go b/src/core/tcp.go index 7b1773b8..8e1435ef 100644 --- a/src/core/tcp.go +++ b/src/core/tcp.go @@ -96,7 +96,7 @@ func (t *tcp) getAddr() *net.TCPAddr { } // Initializes the struct. -func (t *tcp) init(l *links) error { +func (t *tcp) init(l *links, listeners []ListenAddress) error { t.links = l t.tls.init(t) t.mutex.Lock() @@ -105,10 +105,8 @@ func (t *tcp) init(l *links) error { t.listeners = make(map[string]*TcpListener) t.mutex.Unlock() - t.links.core.config.RLock() - defer t.links.core.config.RUnlock() - for _, listenaddr := range t.links.core.config.Listen { - u, err := url.Parse(listenaddr) + for _, listenaddr := range listeners { + u, err := url.Parse(string(listenaddr)) if err != nil { t.links.core.log.Errorln("Failed to parse listener: listener", listenaddr, "is not correctly formatted, ignoring") } From 4c889703b135f459232f0fa1a9f01851a6e334ab Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 6 Aug 2022 15:05:12 +0100 Subject: [PATCH 023/293] Continue refactoring --- contrib/mobile/mobile.go | 45 ++++++++++++++++++++++++++++++---------- go.mod | 2 +- go.sum | 2 ++ src/core/api.go | 44 +++++++++++++++++++-------------------- src/core/core.go | 6 ++++-- src/core/core_test.go | 29 ++++++++++---------------- src/core/link.go | 14 ++++++++----- 7 files changed, 82 insertions(+), 60 deletions(-) diff --git a/contrib/mobile/mobile.go b/contrib/mobile/mobile.go index ba7cfdf6..fc850368 100644 --- a/contrib/mobile/mobile.go +++ b/contrib/mobile/mobile.go @@ -25,7 +25,7 @@ import ( // 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. type Yggdrasil struct { - core core.Core + core *core.Core iprwc *ipv6rwc.ReadWriteCloser config *config.NodeConfig multicast multicast.Multicast @@ -48,19 +48,42 @@ func (m *Yggdrasil) StartJSON(configjson []byte) error { if err := json.Unmarshal(configjson, &m.config); err != nil { return err } - m.config.IfName = "none" - if err := m.core.Start(m.config, logger); err != nil { - logger.Errorln("An error occured starting Yggdrasil:", err) - return err + // Setup the Yggdrasil node itself. + sk, err := hex.DecodeString(m.config.PrivateKey) + if err != nil { + panic(err) + } + options := []core.SetupOption{ + core.IfName("none"), + core.IfMTU(m.config.IfMTU), + } + for _, peer := range m.config.Peers { + options = append(options, core.Peer{URI: peer}) + } + for intf, peers := range m.config.InterfacePeers { + for _, peer := range peers { + options = append(options, core.Peer{URI: peer, SourceInterface: intf}) + } + } + for _, allowed := range m.config.AllowedPublicKeys { + k, err := hex.DecodeString(allowed) + if err != nil { + panic(err) + } + options = append(options, core.AllowedPublicKey(k[:])) + } + m.core, err = core.New(sk[:], options...) + if err != nil { + panic(err) } mtu := m.config.IfMTU - m.iprwc = ipv6rwc.NewReadWriteCloser(&m.core) + m.iprwc = ipv6rwc.NewReadWriteCloser(m.core) if m.iprwc.MaxMTU() < mtu { mtu = m.iprwc.MaxMTU() } m.iprwc.SetMTU(mtu) if len(m.config.MulticastInterfaces) > 0 { - if err := m.multicast.Init(&m.core, m.config, logger, nil); err != nil { + if err := m.multicast.Init(m.core, m.config, logger, nil); err != nil { logger.Errorln("An error occurred initialising multicast:", err) return err } @@ -139,18 +162,18 @@ func (m *Yggdrasil) GetCoordsString() string { func (m *Yggdrasil) GetPeersJSON() (result string) { peers := []struct { - core.Peer + core.PeerInfo IP string }{} for _, v := range m.core.GetPeers() { a := address.AddrForKey(v.Key) ip := net.IP(a[:]).String() peers = append(peers, struct { - core.Peer + core.PeerInfo IP string }{ - Peer: v, - IP: ip, + PeerInfo: v, + IP: ip, }) } if res, err := json.Marshal(peers); err == nil { diff --git a/go.mod b/go.mod index c93b8a0b..11170a1b 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/kardianos/minwinsvc v1.0.0 github.com/mitchellh/mapstructure v1.4.1 github.com/vishvananda/netlink v1.1.0 - golang.org/x/mobile v0.0.0-20220112015953-858099ff7816 + golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105 golang.org/x/net v0.0.0-20211101193420-4a448f8816b3 golang.org/x/sys v0.0.0-20211102192858-4dd72447c267 golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b diff --git a/go.sum b/go.sum index c87112f7..0fc5cadd 100644 --- a/go.sum +++ b/go.sum @@ -52,6 +52,8 @@ golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+o golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20220112015953-858099ff7816 h1:jhDgkcu3yQ4tasBZ+1YwDmK7eFmuVf1w1k+NGGGxfmE= golang.org/x/mobile v0.0.0-20220112015953-858099ff7816/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ= +golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105 h1:3vUV5x5+3LfQbgk7paCM6INOaJG9xXQbn79xoNkwfIk= +golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= diff --git a/src/core/api.go b/src/core/api.go index 30e7e0f6..3ab26ee5 100644 --- a/src/core/api.go +++ b/src/core/api.go @@ -2,6 +2,8 @@ package core import ( "crypto/ed25519" + "sync/atomic" + "time" //"encoding/hex" "encoding/json" @@ -19,14 +21,13 @@ import ( //"github.com/Arceliar/phony" ) -type Self struct { +type SelfInfo struct { Key ed25519.PublicKey Root ed25519.PublicKey Coords []uint64 } -/* -type Peer struct { +type PeerInfo struct { Key ed25519.PublicKey Root ed25519.PublicKey Coords []uint64 @@ -36,25 +37,24 @@ type Peer struct { TXBytes uint64 Uptime time.Duration } -*/ -type DHTEntry struct { +type DHTEntryInfo struct { Key ed25519.PublicKey Port uint64 Rest uint64 } -type PathEntry struct { +type PathEntryInfo struct { Key ed25519.PublicKey Path []uint64 } -type Session struct { +type SessionInfo struct { Key ed25519.PublicKey } -func (c *Core) GetSelf() Self { - var self Self +func (c *Core) GetSelf() SelfInfo { + var self SelfInfo s := c.PacketConn.PacketConn.Debug.GetSelf() self.Key = s.Key self.Root = s.Root @@ -62,9 +62,8 @@ func (c *Core) GetSelf() Self { return self } -/* -func (c *Core) GetPeers() []Peer { - var peers []Peer +func (c *Core) GetPeers() []PeerInfo { + var peers []PeerInfo names := make(map[net.Conn]string) c.links.mutex.Lock() for _, info := range c.links.links { @@ -73,7 +72,7 @@ func (c *Core) GetPeers() []Peer { c.links.mutex.Unlock() ps := c.PacketConn.PacketConn.Debug.GetPeers() for _, p := range ps { - var info Peer + var info PeerInfo info.Key = p.Key info.Root = p.Root info.Coords = p.Coords @@ -91,13 +90,12 @@ func (c *Core) GetPeers() []Peer { } return peers } -*/ -func (c *Core) GetDHT() []DHTEntry { - var dhts []DHTEntry +func (c *Core) GetDHT() []DHTEntryInfo { + var dhts []DHTEntryInfo ds := c.PacketConn.PacketConn.Debug.GetDHT() for _, d := range ds { - var info DHTEntry + var info DHTEntryInfo info.Key = d.Key info.Port = d.Port info.Rest = d.Rest @@ -106,11 +104,11 @@ func (c *Core) GetDHT() []DHTEntry { return dhts } -func (c *Core) GetPaths() []PathEntry { - var paths []PathEntry +func (c *Core) GetPaths() []PathEntryInfo { + var paths []PathEntryInfo ps := c.PacketConn.PacketConn.Debug.GetPaths() for _, p := range ps { - var info PathEntry + var info PathEntryInfo info.Key = p.Key info.Path = p.Path paths = append(paths, info) @@ -118,11 +116,11 @@ func (c *Core) GetPaths() []PathEntry { return paths } -func (c *Core) GetSessions() []Session { - var sessions []Session +func (c *Core) GetSessions() []SessionInfo { + var sessions []SessionInfo ss := c.PacketConn.Debug.GetSessions() for _, s := range ss { - var info Session + var info SessionInfo info.Key = s.Key sessions = append(sessions, info) } diff --git a/src/core/core.go b/src/core/core.go index df3888af..f41b6531 100644 --- a/src/core/core.go +++ b/src/core/core.go @@ -15,7 +15,6 @@ import ( "github.com/Arceliar/phony" "github.com/gologme/log" - "github.com/yggdrasil-network/yggdrasil-go/src/crypto" "github.com/yggdrasil-network/yggdrasil-go/src/version" //"github.com/yggdrasil-network/yggdrasil-go/src/crypto" ) @@ -61,6 +60,9 @@ func New(secret ed25519.PrivateKey, opts ...SetupOption) (*Core, error) { if c.PacketConn, err = iwe.NewPacketConn(c.secret); err != nil { return nil, fmt.Errorf("error creating encryption: %w", err) } + c.config._peers = map[Peer]struct{}{} + c.config._listeners = map[ListenAddress]struct{}{} + c.config._allowedPublicKeys = map[[32]byte]struct{}{} for _, opt := range opts { c._applyOption(opt) } @@ -101,7 +103,7 @@ func (c *Core) _applyOption(opt SetupOption) { case IfMTU: c.config.ifmtu = v case AllowedPublicKey: - pk := crypto.SigPubKey{} + pk := [32]byte{} copy(pk[:], v) c.config._allowedPublicKeys[pk] = struct{}{} } diff --git a/src/core/core_test.go b/src/core/core_test.go index fcfe2e31..823e5e95 100644 --- a/src/core/core_test.go +++ b/src/core/core_test.go @@ -2,6 +2,7 @@ package core import ( "bytes" + "crypto/ed25519" "math/rand" "net/url" "os" @@ -9,21 +10,8 @@ import ( "time" "github.com/gologme/log" - - "github.com/yggdrasil-network/yggdrasil-go/src/config" - "github.com/yggdrasil-network/yggdrasil-go/src/defaults" ) -// GenerateConfig produces default configuration with suitable modifications for tests. -func GenerateConfig() *config.NodeConfig { - cfg := defaults.GenerateConfig() - cfg.AdminListen = "none" - cfg.Listen = []string{"tcp://127.0.0.1:0"} - cfg.IfName = "none" - - return cfg -} - // GetLoggerWithPrefix creates a new logger instance with prefix. // If verbose is set to true, three log levels are enabled: "info", "warn", "error". func GetLoggerWithPrefix(prefix string, verbose bool) *log.Logger { @@ -40,13 +28,18 @@ func GetLoggerWithPrefix(prefix string, verbose bool) *log.Logger { // CreateAndConnectTwo creates two nodes. nodeB connects to nodeA. // Verbosity flag is passed to logger. func CreateAndConnectTwo(t testing.TB, verbose bool) (nodeA *Core, nodeB *Core) { - nodeA = new(Core) - if err := nodeA.Start(GenerateConfig(), GetLoggerWithPrefix("A: ", verbose)); err != nil { + var err error + var skA, skB ed25519.PrivateKey + if _, skA, err = ed25519.GenerateKey(nil); err != nil { t.Fatal(err) } - - nodeB = new(Core) - if err := nodeB.Start(GenerateConfig(), GetLoggerWithPrefix("B: ", verbose)); err != nil { + if _, skB, err = ed25519.GenerateKey(nil); err != nil { + t.Fatal(err) + } + if nodeA, err = New(skA, ListenAddress("tcp://127.0.0.1:0"), IfName("none")); err != nil { + t.Fatal(err) + } + if nodeB, err = New(skB, ListenAddress("tcp://127.0.0.1:0"), IfName("none")); err != nil { t.Fatal(err) } diff --git a/src/core/link.go b/src/core/link.go index 0b7e50a9..099a8af0 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -17,6 +17,7 @@ import ( "sync/atomic" + "github.com/Arceliar/phony" "github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/util" "golang.org/x/net/proxy" @@ -62,7 +63,14 @@ func (l *links) init(c *Core) error { l.mutex.Unlock() l.stopped = make(chan struct{}) - if err := l.tcp.init(l); err != nil { + var listeners []ListenAddress + phony.Block(c, func() { + listeners = make([]ListenAddress, 0, len(c.config._listeners)) + for listener := range c.config._listeners { + listeners = append(listeners, listener) + } + }) + if err := l.tcp.init(l, listeners); err != nil { c.log.Errorln("Failed to start TCP interface") return err } @@ -71,10 +79,6 @@ func (l *links) init(c *Core) error { } func (l *links) call(u *url.URL, sintf string) error { - //u, err := url.Parse(uri) - //if err != nil { - // return fmt.Errorf("peer %s is not correctly formatted (%s)", uri, err) - //} tcpOpts := tcpOptions{} if pubkeys, ok := u.Query()["key"]; ok && len(pubkeys) > 0 { tcpOpts.pinnedEd25519Keys = make(map[keyArray]struct{}) From d5c0dc9beeb7dfb3d9c1158be150c2ec19ed5574 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 6 Aug 2022 15:19:01 +0100 Subject: [PATCH 024/293] Go 1.19 in CI --- .github/workflows/ci.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b41e4336..7915026d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: steps: - uses: actions/setup-go@v3 with: - go-version: 1.18 + go-version: 1.19 - uses: actions/checkout@v3 - name: golangci-lint uses: golangci/golangci-lint-action@v3 @@ -51,7 +51,7 @@ jobs: strategy: fail-fast: false matrix: - goversion: ["1.17", "1.18"] + goversion: ["1.17", "1.18", "1.19"] name: Build & Test (Linux, Go ${{ matrix.goversion }}) needs: [lint] @@ -75,7 +75,7 @@ jobs: strategy: fail-fast: false matrix: - goversion: ["1.17", "1.18"] + goversion: ["1.17", "1.18", "1.19"] name: Build & Test (Windows, Go ${{ matrix.goversion }}) needs: [lint] @@ -99,7 +99,7 @@ jobs: strategy: fail-fast: false matrix: - goversion: ["1.17", "1.18"] + goversion: ["1.17", "1.18", "1.19"] name: Build & Test (macOS, Go ${{ matrix.goversion }}) needs: [lint] @@ -148,7 +148,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v3 with: - go-version: 1.18 + go-version: 1.19 - name: Build package env: @@ -180,7 +180,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v3 with: - go-version: 1.18 + go-version: 1.19 - name: Build package env: @@ -212,7 +212,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v3 with: - go-version: 1.18 + go-version: 1.19 - name: Build package run: sh contrib/msi/build-msi.sh ${{ matrix.pkgarch }} @@ -248,7 +248,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v3 with: - go-version: 1.18 + go-version: 1.19 - name: Build package env: From 16b81490525392bb87cc81e80f0118e1e5cf6184 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 6 Aug 2022 15:21:21 +0100 Subject: [PATCH 025/293] No longer use `ioutil` which is deprecated --- cmd/yggdrasil/main.go | 6 +++--- cmd/yggdrasilctl/cmd_line_env.go | 3 +-- src/core/core.go | 4 ++-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 58b8230d..30cdd5e5 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -8,7 +8,7 @@ import ( "encoding/json" "flag" "fmt" - "io/ioutil" + "io" "net" "os" "os/signal" @@ -51,10 +51,10 @@ func readConfig(log *log.Logger, useconf bool, useconffile string, normaliseconf var err error if useconffile != "" { // Read the file from the filesystem - conf, err = ioutil.ReadFile(useconffile) + conf, err = os.ReadFile(useconffile) } else { // Read the file from stdin. - conf, err = ioutil.ReadAll(os.Stdin) + conf, err = io.ReadAll(os.Stdin) } if err != nil { panic(err) diff --git a/cmd/yggdrasilctl/cmd_line_env.go b/cmd/yggdrasilctl/cmd_line_env.go index bd6df8fc..c1acacd6 100644 --- a/cmd/yggdrasilctl/cmd_line_env.go +++ b/cmd/yggdrasilctl/cmd_line_env.go @@ -4,7 +4,6 @@ import ( "bytes" "flag" "fmt" - "io/ioutil" "log" "os" @@ -61,7 +60,7 @@ func (cmdLineEnv *CmdLineEnv) parseFlagsAndArgs() { func (cmdLineEnv *CmdLineEnv) setEndpoint(logger *log.Logger) { if cmdLineEnv.server == cmdLineEnv.endpoint { - if config, err := ioutil.ReadFile(defaults.GetDefaults().DefaultConfigFile); err == nil { + if config, err := os.ReadFile(defaults.GetDefaults().DefaultConfigFile); err == nil { if bytes.Equal(config[0:2], []byte{0xFF, 0xFE}) || bytes.Equal(config[0:2], []byte{0xFE, 0xFF}) { utf := unicode.UTF16(unicode.BigEndian, unicode.UseBOM) diff --git a/src/core/core.go b/src/core/core.go index 0332980b..f77648b2 100644 --- a/src/core/core.go +++ b/src/core/core.go @@ -6,7 +6,7 @@ import ( "encoding/hex" "errors" "fmt" - "io/ioutil" + "io" "net" "net/url" "time" @@ -48,7 +48,7 @@ func (c *Core) _init() error { c.config.RLock() defer c.config.RUnlock() if c.log == nil { - c.log = log.New(ioutil.Discard, "", 0) + c.log = log.New(io.Discard, "", 0) } sigPriv, err := hex.DecodeString(c.config.PrivateKey) From f8e626dbe1ad9d498b8e097da3452192f2ce6150 Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Thu, 11 Aug 2022 05:54:02 +0800 Subject: [PATCH 026/293] Fix Android multicast crash (#930) * Do not exit on multicast errors (mobile) * Consistency with cmd/yggdrasil/main.go Co-authored-by: Neil Alexander --- contrib/mobile/mobile.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/contrib/mobile/mobile.go b/contrib/mobile/mobile.go index ba7cfdf6..d8c2d6b5 100644 --- a/contrib/mobile/mobile.go +++ b/contrib/mobile/mobile.go @@ -62,11 +62,8 @@ func (m *Yggdrasil) StartJSON(configjson []byte) error { if len(m.config.MulticastInterfaces) > 0 { if err := m.multicast.Init(&m.core, m.config, logger, nil); err != nil { logger.Errorln("An error occurred initialising multicast:", err) - return err - } - if err := m.multicast.Start(); err != nil { + } else if err := m.multicast.Start(); err != nil { logger.Errorln("An error occurred starting multicast:", err) - return err } } return nil From 486ffebedd41ff3b359c2b9bab7bfe678e8e2dfd Mon Sep 17 00:00:00 2001 From: Karandashov Daniil Date: Mon, 29 Aug 2022 22:40:19 +0300 Subject: [PATCH 027/293] Delete unused param (#935) --- cmd/yggdrasil/main.go | 2 +- src/admin/admin.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 5d1c76ac..826df007 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -366,7 +366,7 @@ func run(args yggArgs, ctx context.Context, done chan struct{}) { } else if err := n.admin.Start(); err != nil { logger.Errorln("An error occurred starting admin socket:", err) } - n.admin.SetupAdminHandlers(n.admin) + n.admin.SetupAdminHandlers() // Start the multicast interface if err := n.multicast.Init(n.core, cfg, logger, nil); err != nil { logger.Errorln("An error occurred initialising multicast:", err) diff --git a/src/admin/admin.go b/src/admin/admin.go index 56164533..c7c0f148 100644 --- a/src/admin/admin.go +++ b/src/admin/admin.go @@ -86,7 +86,7 @@ func (a *AdminSocket) Init(c *core.Core, nc *config.NodeConfig, log *log.Logger, return a.core.SetAdmin(a) } -func (a *AdminSocket) SetupAdminHandlers(na *AdminSocket) { +func (a *AdminSocket) SetupAdminHandlers() { _ = a.AddHandler("getSelf", []string{}, func(in json.RawMessage) (interface{}, error) { req := &GetSelfRequest{} res := &GetSelfResponse{} From 4f2abece817c1be59505dbd7c55f02bf47a7874d Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 1 Sep 2022 16:56:42 +0100 Subject: [PATCH 028/293] Fix panic in `tcp.init` for incorrectly formatted listen addresses --- src/core/tcp.go | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/tcp.go b/src/core/tcp.go index 8e1435ef..ab952806 100644 --- a/src/core/tcp.go +++ b/src/core/tcp.go @@ -109,6 +109,7 @@ func (t *tcp) init(l *links, listeners []ListenAddress) error { u, err := url.Parse(string(listenaddr)) if err != nil { t.links.core.log.Errorln("Failed to parse listener: listener", listenaddr, "is not correctly formatted, ignoring") + continue } if _, err := t.listenURL(u, ""); err != nil { return err From c6fe81b5d282ae8166b373e1920b26676dcf5aac Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 3 Sep 2022 10:50:43 +0100 Subject: [PATCH 029/293] Admin socket and `yggdrasilctl` refactoring (#939) --- cmd/yggdrasilctl/cmd_line_env.go | 8 +- cmd/yggdrasilctl/main.go | 548 ++++++++++--------------------- go.mod | 20 +- go.sum | 114 ++++++- src/admin/admin.go | 69 ++-- src/admin/getdht.go | 19 +- src/admin/getpaths.go | 19 +- src/admin/getpeers.go | 26 +- src/admin/getself.go | 20 +- src/admin/getsessions.go | 19 +- src/core/nodeinfo.go | 9 +- 11 files changed, 401 insertions(+), 470 deletions(-) diff --git a/cmd/yggdrasilctl/cmd_line_env.go b/cmd/yggdrasilctl/cmd_line_env.go index c1acacd6..9fcabad9 100644 --- a/cmd/yggdrasilctl/cmd_line_env.go +++ b/cmd/yggdrasilctl/cmd_line_env.go @@ -14,9 +14,9 @@ import ( ) type CmdLineEnv struct { - args []string - endpoint, server string - injson, verbose, ver bool + args []string + endpoint, server string + injson, ver bool } func newCmdLineEnv() CmdLineEnv { @@ -46,7 +46,6 @@ func (cmdLineEnv *CmdLineEnv) parseFlagsAndArgs() { server := flag.String("endpoint", cmdLineEnv.endpoint, "Admin socket endpoint") injson := flag.Bool("json", false, "Output in JSON format (as opposed to pretty-print)") - verbose := flag.Bool("v", false, "Verbose output (includes public keys)") ver := flag.Bool("version", false, "Prints the version of this build") flag.Parse() @@ -54,7 +53,6 @@ func (cmdLineEnv *CmdLineEnv) parseFlagsAndArgs() { cmdLineEnv.args = flag.Args() cmdLineEnv.server = *server cmdLineEnv.injson = *injson - cmdLineEnv.verbose = *verbose cmdLineEnv.ver = *ver } diff --git a/cmd/yggdrasilctl/main.go b/cmd/yggdrasilctl/main.go index 180bea09..5e8bee2a 100644 --- a/cmd/yggdrasilctl/main.go +++ b/cmd/yggdrasilctl/main.go @@ -10,15 +10,15 @@ import ( "net" "net/url" "os" - "sort" - "strconv" "strings" + "time" + "github.com/olekukonko/tablewriter" + "github.com/yggdrasil-network/yggdrasil-go/src/admin" + "github.com/yggdrasil-network/yggdrasil-go/src/core" "github.com/yggdrasil-network/yggdrasil-go/src/version" ) -type admin_info map[string]interface{} - func main() { // makes sure we can use defer and still return an error code to the OS os.Exit(run()) @@ -54,103 +54,13 @@ func run() int { cmdLineEnv.setEndpoint(logger) - conn := connect(cmdLineEnv.endpoint, logger) - logger.Println("Connected") - defer conn.Close() - - decoder := json.NewDecoder(conn) - encoder := json.NewEncoder(conn) - send := make(admin_info) - recv := make(admin_info) - - for c, a := range cmdLineEnv.args { - if c == 0 { - if strings.HasPrefix(a, "-") { - logger.Printf("Ignoring flag %s as it should be specified before other parameters\n", a) - continue - } - logger.Printf("Sending request: %v\n", a) - send["request"] = a - continue - } - tokens := strings.Split(a, "=") - if len(tokens) == 1 { - send[tokens[0]] = true - } else if len(tokens) > 2 { - send[tokens[0]] = strings.Join(tokens[1:], "=") - } else if len(tokens) == 2 { - if i, err := strconv.Atoi(tokens[1]); err == nil { - logger.Printf("Sending parameter %s: %d\n", tokens[0], i) - send[tokens[0]] = i - } else { - switch strings.ToLower(tokens[1]) { - case "true": - send[tokens[0]] = true - case "false": - send[tokens[0]] = false - default: - send[tokens[0]] = tokens[1] - } - logger.Printf("Sending parameter %s: %v\n", tokens[0], send[tokens[0]]) - } - } - } - - if err := encoder.Encode(&send); err != nil { - panic(err) - } - - logger.Printf("Request sent") - - if err := decoder.Decode(&recv); err == nil { - logger.Printf("Response received") - if recv["status"] == "error" { - if err, ok := recv["error"]; ok { - fmt.Println("Admin socket returned an error:", err) - } else { - fmt.Println("Admin socket returned an error but didn't specify any error text") - } - return 1 - } - if _, ok := recv["request"]; !ok { - fmt.Println("Missing request in response (malformed response?)") - return 1 - } - if _, ok := recv["response"]; !ok { - fmt.Println("Missing response body (malformed response?)") - return 1 - } - res := recv["response"].(map[string]interface{}) - - if cmdLineEnv.injson { - if json, err := json.MarshalIndent(res, "", " "); err == nil { - fmt.Println(string(json)) - } - return 0 - } - - handleAll(recv, cmdLineEnv.verbose) - } else { - logger.Println("Error receiving response:", err) - } - - if v, ok := recv["status"]; ok && v != "success" { - return 1 - } - - return 0 -} - -func connect(endpoint string, logger *log.Logger) net.Conn { var conn net.Conn - - u, err := url.Parse(endpoint) - + u, err := url.Parse(cmdLineEnv.endpoint) if err == nil { switch strings.ToLower(u.Scheme) { case "unix": - logger.Println("Connecting to UNIX socket", endpoint[7:]) - conn, err = net.Dial("unix", endpoint[7:]) + logger.Println("Connecting to UNIX socket", cmdLineEnv.endpoint[7:]) + conn, err = net.Dial("unix", cmdLineEnv.endpoint[7:]) case "tcp": logger.Println("Connecting to TCP socket", u.Host) conn, err = net.Dial("tcp", u.Host) @@ -160,298 +70,174 @@ func connect(endpoint string, logger *log.Logger) net.Conn { } } else { logger.Println("Connecting to TCP socket", u.Host) - conn, err = net.Dial("tcp", endpoint) + conn, err = net.Dial("tcp", cmdLineEnv.endpoint) } - if err != nil { panic(err) } - return conn -} + logger.Println("Connected") + defer conn.Close() -func handleAll(recv map[string]interface{}, verbose bool) { - req := recv["request"].(map[string]interface{}) - res := recv["response"].(map[string]interface{}) + decoder := json.NewDecoder(conn) + encoder := json.NewEncoder(conn) + send := &admin.AdminSocketRequest{} + recv := &admin.AdminSocketResponse{} - switch strings.ToLower(req["request"].(string)) { - case "dot": - handleDot(res) - case "list", "getpeers", "getswitchpeers", "getdht", "getsessions", "dhtping": - handleVariousInfo(res, verbose) - case "gettuntap", "settuntap": - handleGetAndSetTunTap(res) - case "getself": - handleGetSelf(res, verbose) - case "getswitchqueues": - handleGetSwitchQueues(res) - case "addpeer", "removepeer", "addallowedencryptionpublickey", "removeallowedencryptionpublickey", "addsourcesubnet", "addroute", "removesourcesubnet", "removeroute": - handleAddsAndRemoves(res) - case "getallowedencryptionpublickeys": - handleGetAllowedEncryptionPublicKeys(res) - case "getmulticastinterfaces": - handleGetMulticastInterfaces(res) - case "getsourcesubnets": - handleGetSourceSubnets(res) - case "getroutes": - handleGetRoutes(res) - case "settunnelrouting": - fallthrough - case "gettunnelrouting": - handleGetTunnelRouting(res) - default: - if json, err := json.MarshalIndent(recv["response"], "", " "); err == nil { + for c, a := range cmdLineEnv.args { + if c == 0 { + if strings.HasPrefix(a, "-") { + logger.Printf("Ignoring flag %s as it should be specified before other parameters\n", a) + continue + } + logger.Printf("Sending request: %v\n", a) + send.Name = a + continue + } + tokens := strings.SplitN(a, "=", 1) + switch { + case len(tokens) == 1: + panic("incomplete argument supplied") + default: + send.Arguments[tokens[0]] = tokens[1] + } + } + + if err := encoder.Encode(&send); err != nil { + panic(err) + } + logger.Printf("Request sent") + if err := decoder.Decode(&recv); err != nil { + panic(err) + } + if recv.Status == "error" { + if err := recv.Error; err != "" { + fmt.Println("Admin socket returned an error:", err) + } else { + fmt.Println("Admin socket returned an error but didn't specify any error text") + } + return 1 + } + if cmdLineEnv.injson { + if json, err := json.MarshalIndent(recv.Response, "", " "); err == nil { fmt.Println(string(json)) } + return 0 } -} - -func handleDot(res map[string]interface{}) { - fmt.Println(res["dot"]) -} - -func handleVariousInfo(res map[string]interface{}, verbose bool) { - maxWidths := make(map[string]int) - var keyOrder []string - keysOrdered := false - - for _, tlv := range res { - for slk, slv := range tlv.(map[string]interface{}) { - if !keysOrdered { - for k := range slv.(map[string]interface{}) { - if !verbose { - if k == "box_pub_key" || k == "box_sig_key" || k == "nodeinfo" || k == "was_mtu_fixed" { - continue - } - } - keyOrder = append(keyOrder, fmt.Sprint(k)) - } - sort.Strings(keyOrder) - keysOrdered = true - } - for k, v := range slv.(map[string]interface{}) { - if len(fmt.Sprint(slk)) > maxWidths["key"] { - maxWidths["key"] = len(fmt.Sprint(slk)) - } - if len(fmt.Sprint(v)) > maxWidths[k] { - maxWidths[k] = len(fmt.Sprint(v)) - if maxWidths[k] < len(k) { - maxWidths[k] = len(k) - } - } - } - } - - if len(keyOrder) > 0 { - fmt.Printf("%-"+fmt.Sprint(maxWidths["key"])+"s ", "") - for _, v := range keyOrder { - fmt.Printf("%-"+fmt.Sprint(maxWidths[v])+"s ", v) - } - fmt.Println() - } - - for slk, slv := range tlv.(map[string]interface{}) { - fmt.Printf("%-"+fmt.Sprint(maxWidths["key"])+"s ", slk) - for _, k := range keyOrder { - preformatted := slv.(map[string]interface{})[k] - var formatted string - switch k { - case "bytes_sent", "bytes_recvd": - formatted = fmt.Sprintf("%d", uint(preformatted.(float64))) - case "uptime", "last_seen": - seconds := uint(preformatted.(float64)) % 60 - minutes := uint(preformatted.(float64)/60) % 60 - hours := uint(preformatted.(float64) / 60 / 60) - formatted = fmt.Sprintf("%02d:%02d:%02d", hours, minutes, seconds) - default: - formatted = fmt.Sprint(preformatted) - } - fmt.Printf("%-"+fmt.Sprint(maxWidths[k])+"s ", formatted) - } - fmt.Println() - } - } -} - -func handleGetAndSetTunTap(res map[string]interface{}) { - for k, v := range res { - fmt.Println("Interface name:", k) - if mtu, ok := v.(map[string]interface{})["mtu"].(float64); ok { - fmt.Println("Interface MTU:", mtu) - } - if tap_mode, ok := v.(map[string]interface{})["tap_mode"].(bool); ok { - fmt.Println("TAP mode:", tap_mode) - } - } -} - -func handleGetSelf(res map[string]interface{}, verbose bool) { - for k, v := range res["self"].(map[string]interface{}) { - if buildname, ok := v.(map[string]interface{})["build_name"].(string); ok && buildname != "unknown" { - fmt.Println("Build name:", buildname) - } - if buildversion, ok := v.(map[string]interface{})["build_version"].(string); ok && buildversion != "unknown" { - fmt.Println("Build version:", buildversion) - } - fmt.Println("IPv6 address:", k) - if subnet, ok := v.(map[string]interface{})["subnet"].(string); ok { - fmt.Println("IPv6 subnet:", subnet) - } - if boxSigKey, ok := v.(map[string]interface{})["key"].(string); ok { - fmt.Println("Public key:", boxSigKey) - } - if coords, ok := v.(map[string]interface{})["coords"].([]interface{}); ok { - fmt.Println("Coords:", coords) - } - if verbose { - if nodeID, ok := v.(map[string]interface{})["node_id"].(string); ok { - fmt.Println("Node ID:", nodeID) - } - if boxPubKey, ok := v.(map[string]interface{})["box_pub_key"].(string); ok { - fmt.Println("Public encryption key:", boxPubKey) - } - if boxSigKey, ok := v.(map[string]interface{})["box_sig_key"].(string); ok { - fmt.Println("Public signing key:", boxSigKey) - } - } - } -} - -func handleGetSwitchQueues(res map[string]interface{}) { - maximumqueuesize := float64(4194304) - portqueues := make(map[float64]float64) - portqueuesize := make(map[float64]float64) - portqueuepackets := make(map[float64]float64) - v := res["switchqueues"].(map[string]interface{}) - if queuecount, ok := v["queues_count"].(float64); ok { - fmt.Printf("Active queue count: %d queues\n", uint(queuecount)) - } - if queuesize, ok := v["queues_size"].(float64); ok { - fmt.Printf("Active queue size: %d bytes\n", uint(queuesize)) - } - if highestqueuecount, ok := v["highest_queues_count"].(float64); ok { - fmt.Printf("Highest queue count: %d queues\n", uint(highestqueuecount)) - } - if highestqueuesize, ok := v["highest_queues_size"].(float64); ok { - fmt.Printf("Highest queue size: %d bytes\n", uint(highestqueuesize)) - } - if m, ok := v["maximum_queues_size"].(float64); ok { - maximumqueuesize = m - fmt.Printf("Maximum queue size: %d bytes\n", uint(maximumqueuesize)) - } - if queues, ok := v["queues"].([]interface{}); ok { - if len(queues) != 0 { - fmt.Println("Active queues:") - for _, v := range queues { - queueport := v.(map[string]interface{})["queue_port"].(float64) - queuesize := v.(map[string]interface{})["queue_size"].(float64) - queuepackets := v.(map[string]interface{})["queue_packets"].(float64) - queueid := v.(map[string]interface{})["queue_id"].(string) - portqueues[queueport]++ - portqueuesize[queueport] += queuesize - portqueuepackets[queueport] += queuepackets - queuesizepercent := (100 / maximumqueuesize) * queuesize - fmt.Printf("- Switch port %d, Stream ID: %v, size: %d bytes (%d%% full), %d packets\n", - uint(queueport), []byte(queueid), uint(queuesize), - uint(queuesizepercent), uint(queuepackets)) - } - } - } - if len(portqueuesize) > 0 && len(portqueuepackets) > 0 { - fmt.Println("Aggregated statistics by switchport:") - for k, v := range portqueuesize { - queuesizepercent := (100 / (portqueues[k] * maximumqueuesize)) * v - fmt.Printf("- Switch port %d, size: %d bytes (%d%% full), %d packets\n", - uint(k), uint(v), uint(queuesizepercent), uint(portqueuepackets[k])) - } - } -} - -func handleAddsAndRemoves(res map[string]interface{}) { - if _, ok := res["added"]; ok { - for _, v := range res["added"].([]interface{}) { - fmt.Println("Added:", fmt.Sprint(v)) - } - } - if _, ok := res["not_added"]; ok { - for _, v := range res["not_added"].([]interface{}) { - fmt.Println("Not added:", fmt.Sprint(v)) - } - } - if _, ok := res["removed"]; ok { - for _, v := range res["removed"].([]interface{}) { - fmt.Println("Removed:", fmt.Sprint(v)) - } - } - if _, ok := res["not_removed"]; ok { - for _, v := range res["not_removed"].([]interface{}) { - fmt.Println("Not removed:", fmt.Sprint(v)) - } - } -} - -func handleGetAllowedEncryptionPublicKeys(res map[string]interface{}) { - if _, ok := res["allowed_box_pubs"]; !ok { - fmt.Println("All connections are allowed") - } else if res["allowed_box_pubs"] == nil { - fmt.Println("All connections are allowed") - } else { - fmt.Println("Connections are allowed only from the following public box keys:") - for _, v := range res["allowed_box_pubs"].([]interface{}) { - fmt.Println("-", v) - } - } -} - -func handleGetMulticastInterfaces(res map[string]interface{}) { - if _, ok := res["multicast_interfaces"]; !ok { - fmt.Println("No multicast interfaces found") - } else if res["multicast_interfaces"] == nil { - fmt.Println("No multicast interfaces found") - } else { - fmt.Println("Multicast peer discovery is active on:") - for _, v := range res["multicast_interfaces"].([]interface{}) { - fmt.Println("-", v) - } - } -} - -func handleGetSourceSubnets(res map[string]interface{}) { - if _, ok := res["source_subnets"]; !ok { - fmt.Println("No source subnets found") - } else if res["source_subnets"] == nil { - fmt.Println("No source subnets found") - } else { - fmt.Println("Source subnets:") - for _, v := range res["source_subnets"].([]interface{}) { - fmt.Println("-", v) - } - } -} - -func handleGetRoutes(res map[string]interface{}) { - if routes, ok := res["routes"].(map[string]interface{}); !ok { - fmt.Println("No routes found") - } else { - if res["routes"] == nil || len(routes) == 0 { - fmt.Println("No routes found") - } else { - fmt.Println("Routes:") - for k, v := range routes { - if pv, ok := v.(string); ok { - fmt.Println("-", k, " via ", pv) - } - } - } - } -} - -func handleGetTunnelRouting(res map[string]interface{}) { - if enabled, ok := res["enabled"].(bool); !ok { - fmt.Println("Tunnel routing is disabled") - } else if !enabled { - fmt.Println("Tunnel routing is disabled") - } else { - fmt.Println("Tunnel routing is enabled") - } + + table := tablewriter.NewWriter(os.Stdout) + table.SetAlignment(tablewriter.ALIGN_LEFT) + table.SetAutoFormatHeaders(false) + table.SetCenterSeparator("") + table.SetColumnSeparator("") + table.SetRowSeparator("") + table.SetHeaderLine(false) + table.SetBorder(false) + table.SetTablePadding("\t") // pad with tabs + table.SetNoWhiteSpace(true) + + switch strings.ToLower(recv.Request.Name) { + case "list": + var resp admin.ListResponse + if err := json.Unmarshal(recv.Response, &resp); err != nil { + panic(err) + } + table.SetHeader([]string{"Command", "Arguments"}) + for _, entry := range resp.List { + table.Append([]string{entry.Command, strings.Join(entry.Fields, ", ")}) + } + table.Render() + + case "getself": + var resp admin.GetSelfResponse + if err := json.Unmarshal(recv.Response, &resp); err != nil { + panic(err) + } + table.Append([]string{"Build name:", resp.BuildName}) + table.Append([]string{"Build version:", resp.BuildVersion}) + table.Append([]string{"IPv6 address:", resp.IPAddress}) + table.Append([]string{"IPv6 subnet:", resp.Subnet}) + table.Append([]string{"Coordinates:", fmt.Sprintf("%v", resp.Coords)}) + table.Append([]string{"Public key:", resp.PublicKey}) + table.Render() + + case "getpeers": + var resp admin.GetPeersResponse + if err := json.Unmarshal(recv.Response, &resp); err != nil { + panic(err) + } + table.SetHeader([]string{"Port", "Public Key", "IP Address", "Peering URI", "Uptime", "RX", "TX"}) + for _, peer := range resp.Peers { + table.Append([]string{ + fmt.Sprintf("%d", peer.Port), + peer.PublicKey, + peer.IPAddress, + peer.Remote, + (time.Duration(peer.Uptime) * time.Second).String(), + peer.RXBytes.String(), + peer.TXBytes.String(), + }) + } + table.Render() + + case "getdht": + var resp admin.GetDHTResponse + if err := json.Unmarshal(recv.Response, &resp); err != nil { + panic(err) + } + table.SetHeader([]string{"Public Key", "IP Address", "Port", "Rest"}) + for _, dht := range resp.DHT { + table.Append([]string{ + dht.PublicKey, + dht.IPAddress, + fmt.Sprintf("%d", dht.Port), + fmt.Sprintf("%d", dht.Rest), + }) + } + table.Render() + + case "getpaths": + var resp admin.GetPathsResponse + if err := json.Unmarshal(recv.Response, &resp); err != nil { + panic(err) + } + table.SetHeader([]string{"Public Key", "IP Address", "Path"}) + for _, p := range resp.Paths { + table.Append([]string{ + p.PublicKey, + p.IPAddress, + fmt.Sprintf("%v", p.Path), + }) + } + table.Render() + + case "getsessions": + var resp admin.GetSessionsResponse + if err := json.Unmarshal(recv.Response, &resp); err != nil { + panic(err) + } + table.SetHeader([]string{"Public Key", "IP Address"}) + for _, p := range resp.Sessions { + table.Append([]string{ + p.PublicKey, + p.IPAddress, + }) + } + table.Render() + + case "getnodeinfo": + var resp core.GetNodeInfoResponse + if err := json.Unmarshal(recv.Response, &resp); err != nil { + panic(err) + } + for _, v := range resp { + fmt.Println(string(v)) + break + } + + default: + panic("unknown response type: " + recv.Request.Name) + } + + return 0 } diff --git a/go.mod b/go.mod index 11170a1b..c790b19d 100644 --- a/go.mod +++ b/go.mod @@ -13,23 +13,27 @@ require ( github.com/mitchellh/mapstructure v1.4.1 github.com/vishvananda/netlink v1.1.0 golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105 - golang.org/x/net v0.0.0-20211101193420-4a448f8816b3 - golang.org/x/sys v0.0.0-20211102192858-4dd72447c267 + golang.org/x/net v0.0.0-20220722155237-a158d28d115b + golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b golang.zx2c4.com/wireguard v0.0.0-20211017052713-f87e87af0d9a golang.zx2c4.com/wireguard/windows v0.4.12 ) +require ( + github.com/mattn/go-colorable v0.1.8 // indirect + github.com/rivo/uniseg v0.2.0 // indirect + golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect + golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect + golang.org/x/tools v0.1.12 // indirect +) + require ( github.com/VividCortex/ewma v1.2.0 // indirect github.com/fatih/color v1.12.0 // indirect - github.com/mattn/go-colorable v0.1.8 // indirect github.com/mattn/go-isatty v0.0.13 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect - github.com/rivo/uniseg v0.2.0 // indirect + github.com/olekukonko/tablewriter v0.0.5 + github.com/onsi/gomega v1.20.2 github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f // indirect - golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect - golang.org/x/mod v0.4.2 // indirect - golang.org/x/tools v0.1.8-0.20211022200916-316ba0b74098 // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect ) diff --git a/go.sum b/go.sum index 0fc5cadd..5d22d679 100644 --- a/go.sum +++ b/go.sum @@ -8,15 +8,40 @@ github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1o github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= github.com/cheggaaa/pb/v3 v3.0.8 h1:bC8oemdChbke2FHIIGy9mn4DPJ2caZYQnfbRqwmdCoA= github.com/cheggaaa/pb/v3 v3.0.8/go.mod h1:UICbiLec/XO6Hw6k+BHEtHeQFzzBH4i2/qk/ow1EJTA= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/gologme/log v1.2.0 h1:Ya5Ip/KD6FX7uH0S31QO87nCCSucKtF44TLbTtO7V4c= github.com/gologme/log v1.2.0/go.mod h1:gq31gQ8wEHkR+WekdWsqDuf8pXTUZA9BnnzTuPz1Y9U= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hjson/hjson-go v3.1.0+incompatible h1:DY/9yE8ey8Zv22bY+mHV1uk2yRy0h8tKhZ77hEdi0Aw= github.com/hjson/hjson-go v3.1.0+incompatible/go.mod h1:qsetwF8NlsTsOTwZTApNlTCerV+b2GjYRRcIk4JMFio= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/kardianos/minwinsvc v1.0.0 h1:+JfAi8IBJna0jY2dJGZqi7o15z13JelFIklJCAENALA= github.com/kardianos/minwinsvc v1.0.0/go.mod h1:Bgd0oc+D0Qo3bBytmNtyRKVlp85dAloLKhfxanPFFRc= github.com/lxn/walk v0.0.0-20210112085537-c389da54e794/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ= @@ -26,23 +51,48 @@ github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA= github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= +github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= +github.com/onsi/gomega v1.20.2 h1:8uQq0zMgLEfa0vRrrBgaJF2gyW9Da9BmfGV+OyUzfkY= +github.com/onsi/gomega v1.20.2/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f h1:p4VB7kIXpOQvVn1ZaTIVp+3vuYAXFe3OJEvjbUYJLaA= github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= @@ -50,59 +100,105 @@ golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9t golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20220112015953-858099ff7816 h1:jhDgkcu3yQ4tasBZ+1YwDmK7eFmuVf1w1k+NGGGxfmE= -golang.org/x/mobile v0.0.0-20220112015953-858099ff7816/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ= golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105 h1:3vUV5x5+3LfQbgk7paCM6INOaJG9xXQbn79xoNkwfIk= golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210927181540-4e4d966f7476/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211011170408-caeb26a5c8c0/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211101193420-4a448f8816b3 h1:VrJZAjbekhoRn7n5FBujY31gboH+iB3pdLxn3gE9FjU= -golang.org/x/net v0.0.0-20211101193420-4a448f8816b3/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211102192858-4dd72447c267 h1:7zYaz3tjChtpayGDzu6H0hDAUM5zIGA2XW7kRNgQ0jc= -golang.org/x/sys v0.0.0-20211102192858-4dd72447c267/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b h1:NXqSWXSRUSCaFuvitrWtU169I3876zRTalMRbfd6LL0= golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= -golang.org/x/tools v0.1.8-0.20211022200916-316ba0b74098 h1:YuekqPskqwCCPM79F1X5Dhv4ezTCj+Ki1oNwiafxkA0= golang.org/x/tools v0.1.8-0.20211022200916-316ba0b74098/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= +golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= +golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.zx2c4.com/wireguard v0.0.0-20211012062646-82d2aa87aa62/go.mod h1:id8Oh3eCCmpj9uVGWVjsUAl6UPX5ysMLzu6QxJU2UOU= golang.zx2c4.com/wireguard v0.0.0-20211017052713-f87e87af0d9a h1:tTbyylK9/D3u/wEP26Vx7L700UpY48nhioJWZM1vhZw= golang.zx2c4.com/wireguard v0.0.0-20211017052713-f87e87af0d9a/go.mod h1:id8Oh3eCCmpj9uVGWVjsUAl6UPX5ysMLzu6QxJU2UOU= golang.zx2c4.com/wireguard/windows v0.4.12 h1:CUmbdWKVNzTSsVb4yUAiEwL3KsabdJkEPdDjCHxBlhA= golang.zx2c4.com/wireguard/windows v0.4.12/go.mod h1:PW4y+d9oY83XU9rRwRwrJDwEMuhVjMxu2gfD1cfzS7w= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/src/admin/admin.go b/src/admin/admin.go index c7c0f148..9a00d8a1 100644 --- a/src/admin/admin.go +++ b/src/admin/admin.go @@ -7,6 +7,7 @@ import ( "net" "net/url" "os" + "sort" "strings" "time" @@ -28,13 +29,17 @@ type AdminSocket struct { done chan struct{} } +type AdminSocketRequest struct { + Name string `json:"request"` + Arguments map[string]string `json:"arguments,omitempty"` + KeepAlive bool `json:"keepalive,omitempty"` +} + type AdminSocketResponse struct { - Status string `json:"status"` - Request struct { - Name string `json:"request"` - KeepAlive bool `json:"keepalive"` - } `json:"request"` - Response interface{} `json:"response"` + Status string `json:"status"` + Error string `json:"error,omitempty"` + Request AdminSocketRequest `json:"request"` + Response json.RawMessage `json:"response"` } type handler struct { @@ -43,11 +48,12 @@ type handler struct { } type ListResponse struct { - List map[string]ListEntry `json:"list"` + List []ListEntry `json:"list"` } type ListEntry struct { - Fields []string `json:"fields"` + Command string `json:"command"` + Fields []string `json:"fields,omitempty"` } // AddHandler is called for each admin function to add the handler and help documentation to the API. @@ -73,14 +79,16 @@ func (a *AdminSocket) Init(c *core.Core, nc *config.NodeConfig, log *log.Logger, a.done = make(chan struct{}) close(a.done) // Start in a done / not-started state _ = a.AddHandler("list", []string{}, func(_ json.RawMessage) (interface{}, error) { - res := &ListResponse{ - List: map[string]ListEntry{}, - } + res := &ListResponse{} for name, handler := range a.handlers { - res.List[name] = ListEntry{ - Fields: handler.args, - } + res.List = append(res.List, ListEntry{ + Command: name, + Fields: handler.args, + }) } + sort.SliceStable(res.List, func(i, j int) bool { + return strings.Compare(res.List[i].Command, res.List[j].Command) < 0 + }) return res, nil }) return a.core.SetAdmin(a) @@ -277,22 +285,28 @@ func (a *AdminSocket) handleRequest(conn net.Conn) { if err = json.Unmarshal(buf, &resp.Request); err == nil { if resp.Request.Name == "" { resp.Status = "error" - resp.Response = &ErrorResponse{ + resp.Response, _ = json.Marshal(ErrorResponse{ Error: "No request specified", - } + }) } else if h, ok := a.handlers[strings.ToLower(resp.Request.Name)]; ok { - resp.Response, err = h.handler(buf) + res, err := h.handler(buf) if err != nil { resp.Status = "error" - resp.Response = &ErrorResponse{ + resp.Response, _ = json.Marshal(ErrorResponse{ Error: err.Error(), - } + }) + } + if resp.Response, err = json.Marshal(res); err != nil { + resp.Status = "error" + resp.Response, _ = json.Marshal(ErrorResponse{ + Error: err.Error(), + }) } } else { resp.Status = "error" - resp.Response = &ErrorResponse{ + resp.Response, _ = json.Marshal(ErrorResponse{ Error: fmt.Sprintf("Unknown action '%s', try 'list' for help", resp.Request.Name), - } + }) } } if err = encoder.Encode(resp); err != nil { @@ -305,3 +319,16 @@ func (a *AdminSocket) handleRequest(conn net.Conn) { } } } + +type DataUnit uint64 + +func (d DataUnit) String() string { + switch { + case d > 1024*1024*1024: + return fmt.Sprintf("%2.fgb", float64(d)/1024/1024/1024) + case d > 1024*1024: + return fmt.Sprintf("%2.fmb", float64(d)/1024/1024) + default: + return fmt.Sprintf("%2.fkb", float64(d)/1024) + } +} diff --git a/src/admin/getdht.go b/src/admin/getdht.go index 5dc95547..bfb21818 100644 --- a/src/admin/getdht.go +++ b/src/admin/getdht.go @@ -3,6 +3,8 @@ package admin import ( "encoding/hex" "net" + "sort" + "strings" "github.com/yggdrasil-network/yggdrasil-go/src/address" ) @@ -10,25 +12,30 @@ import ( type GetDHTRequest struct{} type GetDHTResponse struct { - DHT map[string]DHTEntry `json:"dht"` + DHT []DHTEntry `json:"dht"` } type DHTEntry struct { + IPAddress string `json:"address"` PublicKey string `json:"key"` Port uint64 `json:"port"` Rest uint64 `json:"rest"` } func (a *AdminSocket) getDHTHandler(req *GetDHTRequest, res *GetDHTResponse) error { - res.DHT = map[string]DHTEntry{} - for _, d := range a.core.GetDHT() { + dht := a.core.GetDHT() + res.DHT = make([]DHTEntry, 0, len(dht)) + for _, d := range dht { addr := address.AddrForKey(d.Key) - so := net.IP(addr[:]).String() - res.DHT[so] = DHTEntry{ + res.DHT = append(res.DHT, DHTEntry{ + IPAddress: net.IP(addr[:]).String(), PublicKey: hex.EncodeToString(d.Key[:]), Port: d.Port, Rest: d.Rest, - } + }) } + sort.SliceStable(res.DHT, func(i, j int) bool { + return strings.Compare(res.DHT[i].PublicKey, res.DHT[j].PublicKey) < 0 + }) return nil } diff --git a/src/admin/getpaths.go b/src/admin/getpaths.go index c8e97d01..fbd52481 100644 --- a/src/admin/getpaths.go +++ b/src/admin/getpaths.go @@ -3,6 +3,8 @@ package admin import ( "encoding/hex" "net" + "sort" + "strings" "github.com/yggdrasil-network/yggdrasil-go/src/address" ) @@ -11,23 +13,28 @@ type GetPathsRequest struct { } type GetPathsResponse struct { - Paths map[string]PathEntry `json:"paths"` + Paths []PathEntry `json:"paths"` } type PathEntry struct { + IPAddress string `json:"address"` PublicKey string `json:"key"` Path []uint64 `json:"path"` } func (a *AdminSocket) getPathsHandler(req *GetPathsRequest, res *GetPathsResponse) error { - res.Paths = map[string]PathEntry{} - for _, p := range a.core.GetPaths() { + paths := a.core.GetPaths() + res.Paths = make([]PathEntry, 0, len(paths)) + for _, p := range paths { addr := address.AddrForKey(p.Key) - so := net.IP(addr[:]).String() - res.Paths[so] = PathEntry{ + res.Paths = append(res.Paths, PathEntry{ + IPAddress: net.IP(addr[:]).String(), PublicKey: hex.EncodeToString(p.Key), Path: p.Path, - } + }) } + sort.SliceStable(res.Paths, func(i, j int) bool { + return strings.Compare(res.Paths[i].PublicKey, res.Paths[j].PublicKey) < 0 + }) return nil } diff --git a/src/admin/getpeers.go b/src/admin/getpeers.go index ecb28726..61d0937f 100644 --- a/src/admin/getpeers.go +++ b/src/admin/getpeers.go @@ -3,6 +3,7 @@ package admin import ( "encoding/hex" "net" + "sort" "github.com/yggdrasil-network/yggdrasil-go/src/address" ) @@ -11,33 +12,38 @@ type GetPeersRequest struct { } type GetPeersResponse struct { - Peers map[string]PeerEntry `json:"peers"` + Peers []PeerEntry `json:"peers"` } type PeerEntry struct { + IPAddress string `json:"address"` PublicKey string `json:"key"` Port uint64 `json:"port"` Coords []uint64 `json:"coords"` Remote string `json:"remote"` - RXBytes uint64 `json:"bytes_recvd"` - TXBytes uint64 `json:"bytes_sent"` + RXBytes DataUnit `json:"bytes_recvd"` + TXBytes DataUnit `json:"bytes_sent"` Uptime float64 `json:"uptime"` } func (a *AdminSocket) getPeersHandler(req *GetPeersRequest, res *GetPeersResponse) error { - res.Peers = map[string]PeerEntry{} - for _, p := range a.core.GetPeers() { + peers := a.core.GetPeers() + res.Peers = make([]PeerEntry, 0, len(peers)) + for _, p := range peers { addr := address.AddrForKey(p.Key) - so := net.IP(addr[:]).String() - res.Peers[so] = PeerEntry{ + res.Peers = append(res.Peers, PeerEntry{ + IPAddress: net.IP(addr[:]).String(), PublicKey: hex.EncodeToString(p.Key), Port: p.Port, Coords: p.Coords, Remote: p.Remote, - RXBytes: p.RXBytes, - TXBytes: p.TXBytes, + RXBytes: DataUnit(p.RXBytes), + TXBytes: DataUnit(p.TXBytes), Uptime: p.Uptime.Seconds(), - } + }) } + sort.Slice(res.Peers, func(i, j int) bool { + return res.Peers[i].Port < res.Peers[j].Port + }) return nil } diff --git a/src/admin/getself.go b/src/admin/getself.go index 7effcc46..f42dc750 100644 --- a/src/admin/getself.go +++ b/src/admin/getself.go @@ -9,28 +9,22 @@ import ( type GetSelfRequest struct{} type GetSelfResponse struct { - Self map[string]SelfEntry `json:"self"` -} - -type SelfEntry struct { BuildName string `json:"build_name"` BuildVersion string `json:"build_version"` PublicKey string `json:"key"` + IPAddress string `json:"address"` Coords []uint64 `json:"coords"` Subnet string `json:"subnet"` } func (a *AdminSocket) getSelfHandler(req *GetSelfRequest, res *GetSelfResponse) error { - res.Self = make(map[string]SelfEntry) self := a.core.GetSelf() - addr := a.core.Address().String() snet := a.core.Subnet() - res.Self[addr] = SelfEntry{ - BuildName: version.BuildName(), - BuildVersion: version.BuildVersion(), - PublicKey: hex.EncodeToString(self.Key[:]), - Subnet: snet.String(), - Coords: self.Coords, - } + res.BuildName = version.BuildName() + res.BuildVersion = version.BuildVersion() + res.PublicKey = hex.EncodeToString(self.Key[:]) + res.IPAddress = a.core.Address().String() + res.Subnet = snet.String() + res.Coords = self.Coords return nil } diff --git a/src/admin/getsessions.go b/src/admin/getsessions.go index 3a0c19b6..58ce378e 100644 --- a/src/admin/getsessions.go +++ b/src/admin/getsessions.go @@ -3,6 +3,8 @@ package admin import ( "encoding/hex" "net" + "sort" + "strings" "github.com/yggdrasil-network/yggdrasil-go/src/address" ) @@ -10,21 +12,26 @@ import ( type GetSessionsRequest struct{} type GetSessionsResponse struct { - Sessions map[string]SessionEntry `json:"sessions"` + Sessions []SessionEntry `json:"sessions"` } type SessionEntry struct { + IPAddress string `json:"address"` PublicKey string `json:"key"` } func (a *AdminSocket) getSessionsHandler(req *GetSessionsRequest, res *GetSessionsResponse) error { - res.Sessions = map[string]SessionEntry{} - for _, s := range a.core.GetSessions() { + sessions := a.core.GetSessions() + res.Sessions = make([]SessionEntry, 0, len(sessions)) + for _, s := range sessions { addr := address.AddrForKey(s.Key) - so := net.IP(addr[:]).String() - res.Sessions[so] = SessionEntry{ + res.Sessions = append(res.Sessions, SessionEntry{ + IPAddress: net.IP(addr[:]).String(), PublicKey: hex.EncodeToString(s.Key[:]), - } + }) } + sort.SliceStable(res.Sessions, func(i, j int) bool { + return strings.Compare(res.Sessions[i].PublicKey, res.Sessions[j].PublicKey) < 0 + }) return nil } diff --git a/src/core/nodeinfo.go b/src/core/nodeinfo.go index 4ca21d73..a6132ec2 100644 --- a/src/core/nodeinfo.go +++ b/src/core/nodeinfo.go @@ -4,7 +4,6 @@ import ( "encoding/hex" "encoding/json" "errors" - "net" "runtime" "strings" "time" @@ -13,7 +12,7 @@ import ( "github.com/Arceliar/phony" //"github.com/yggdrasil-network/yggdrasil-go/src/crypto" - "github.com/yggdrasil-network/yggdrasil-go/src/address" + "github.com/yggdrasil-network/yggdrasil-go/src/version" ) @@ -154,7 +153,7 @@ func (m *nodeinfo) _sendRes(key keyArray) { type GetNodeInfoRequest struct { Key string `json:"key"` } -type GetNodeInfoResponse map[string]interface{} +type GetNodeInfoResponse map[string]json.RawMessage func (m *nodeinfo) nodeInfoAdminHandler(in json.RawMessage) (interface{}, error) { var req GetNodeInfoRequest @@ -182,8 +181,8 @@ func (m *nodeinfo) nodeInfoAdminHandler(in json.RawMessage) (interface{}, error) if err := msg.UnmarshalJSON(info); err != nil { return nil, err } - ip := net.IP(address.AddrForKey(kbs)[:]) - res := GetNodeInfoResponse{ip.String(): msg} + key := hex.EncodeToString(kbs[:]) + res := GetNodeInfoResponse{key: msg} return res, nil } } From dad0b10dfeb801049c6473e047fd22ff0e7b8e61 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 3 Sep 2022 10:51:44 +0100 Subject: [PATCH 030/293] Move `Core._applyOption` --- src/core/core.go | 21 --------------------- src/core/options.go | 21 +++++++++++++++++++++ 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/core/core.go b/src/core/core.go index 37a1d841..bc7ac831 100644 --- a/src/core/core.go +++ b/src/core/core.go @@ -88,27 +88,6 @@ func New(secret ed25519.PrivateKey, opts ...SetupOption) (*Core, error) { return c, nil } -func (c *Core) _applyOption(opt SetupOption) { - switch v := opt.(type) { - case Peer: - c.config._peers[v] = struct{}{} - case ListenAddress: - c.config._listeners[v] = struct{}{} - case NodeInfo: - c.config.nodeinfo = v - case NodeInfoPrivacy: - c.config.nodeinfoPrivacy = v - case IfName: - c.config.ifname = v - case IfMTU: - c.config.ifmtu = v - case AllowedPublicKey: - pk := [32]byte{} - copy(pk[:], v) - c.config._allowedPublicKeys[pk] = struct{}{} - } -} - // 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. diff --git a/src/core/options.go b/src/core/options.go index 0210a4ef..d46c9def 100644 --- a/src/core/options.go +++ b/src/core/options.go @@ -4,6 +4,27 @@ import ( "crypto/ed25519" ) +func (c *Core) _applyOption(opt SetupOption) { + switch v := opt.(type) { + case Peer: + c.config._peers[v] = struct{}{} + case ListenAddress: + c.config._listeners[v] = struct{}{} + case NodeInfo: + c.config.nodeinfo = v + case NodeInfoPrivacy: + c.config.nodeinfoPrivacy = v + case IfName: + c.config.ifname = v + case IfMTU: + c.config.ifmtu = v + case AllowedPublicKey: + pk := [32]byte{} + copy(pk[:], v) + c.config._allowedPublicKeys[pk] = struct{}{} + } +} + type SetupOption interface { isSetupOption() } From 493208fb378b76760c8cf6b7625ccbe0b60185d3 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 3 Sep 2022 11:42:05 +0100 Subject: [PATCH 031/293] Refactor multicast setup (isolated config, etc) --- cmd/yggdrasil/main.go | 76 +++++++++------ contrib/mobile/mobile.go | 75 +++++++++------ src/core/api.go | 10 +- src/core/core.go | 8 +- src/core/core_test.go | 5 +- src/core/options.go | 16 ++-- src/multicast/multicast.go | 131 ++++++++++++-------------- src/multicast/multicast_darwin_cgo.go | 2 +- src/multicast/options.go | 28 ++++++ src/util/util.go | 14 +++ 10 files changed, 215 insertions(+), 150 deletions(-) create mode 100644 src/multicast/options.go diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 826df007..71453aaa 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -12,6 +12,7 @@ import ( "net" "os" "os/signal" + "regexp" "strings" "syscall" @@ -325,40 +326,59 @@ func run(args yggArgs, ctx context.Context, done chan struct{}) { default: } - // Setup the Yggdrasil node itself. The node{} type includes a Core, so we - // don't need to create this manually. - sk, err := hex.DecodeString(cfg.PrivateKey) - if err != nil { - panic(err) - } - options := []core.SetupOption{ - core.IfName(cfg.IfName), - core.IfMTU(cfg.IfMTU), - } - for _, peer := range cfg.Peers { - options = append(options, core.Peer{URI: peer}) - } - for intf, peers := range cfg.InterfacePeers { - for _, peer := range peers { - options = append(options, core.Peer{URI: peer, SourceInterface: intf}) - } - } - for _, allowed := range cfg.AllowedPublicKeys { - k, err := hex.DecodeString(allowed) + n := &node{config: cfg} + + // Setup the Yggdrasil node itself. + { + sk, err := hex.DecodeString(cfg.PrivateKey) + if err != nil { + panic(err) + } + options := []core.SetupOption{ + core.IfName(cfg.IfName), + core.IfMTU(cfg.IfMTU), + } + for _, peer := range cfg.Peers { + options = append(options, core.Peer{URI: peer}) + } + for intf, peers := range cfg.InterfacePeers { + for _, peer := range peers { + options = append(options, core.Peer{URI: peer, SourceInterface: intf}) + } + } + for _, allowed := range cfg.AllowedPublicKeys { + k, err := hex.DecodeString(allowed) + if err != nil { + panic(err) + } + options = append(options, core.AllowedPublicKey(k[:])) + } + n.core, err = core.New(sk[:], logger, options...) if err != nil { panic(err) } - options = append(options, core.AllowedPublicKey(k[:])) } - n := node{config: cfg} - n.core, err = core.New(sk[:], options...) - if err != nil { - panic(err) + + // Setup the multicast module. + { + options := []multicast.SetupOption{} + for _, intf := range cfg.MulticastInterfaces { + options = append(options, multicast.MulticastInterface{ + Regex: regexp.MustCompile(intf.Regex), + Beacon: intf.Beacon, + Listen: intf.Listen, + Port: intf.Port, + }) + } + n.multicast, err = multicast.New(n.core, logger, options...) + if err != nil { + panic(err) + } } + // Register the session firewall gatekeeper function // Allocate our modules n.admin = &admin.AdminSocket{} - n.multicast = &multicast.Multicast{} n.tuntap = &tuntap.TunAdapter{} // Start the admin socket if err := n.admin.Init(n.core, cfg, logger, nil); err != nil { @@ -368,10 +388,8 @@ func run(args yggArgs, ctx context.Context, done chan struct{}) { } n.admin.SetupAdminHandlers() // Start the multicast interface - if err := n.multicast.Init(n.core, cfg, logger, nil); err != nil { + if n.multicast, err = multicast.New(n.core, logger, nil); err != nil { logger.Errorln("An error occurred initialising multicast:", err) - } else if err := n.multicast.Start(); err != nil { - logger.Errorln("An error occurred starting multicast:", err) } n.multicast.SetupAdminHandlers(n.admin) // Start the TUN/TAP interface diff --git a/contrib/mobile/mobile.go b/contrib/mobile/mobile.go index 85535ee8..d7da03de 100644 --- a/contrib/mobile/mobile.go +++ b/contrib/mobile/mobile.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "net" + "regexp" "github.com/gologme/log" @@ -28,7 +29,7 @@ type Yggdrasil struct { core *core.Core iprwc *ipv6rwc.ReadWriteCloser config *config.NodeConfig - multicast multicast.Multicast + multicast *multicast.Multicast log MobileLogger } @@ -49,46 +50,60 @@ func (m *Yggdrasil) StartJSON(configjson []byte) error { return err } // Setup the Yggdrasil node itself. - sk, err := hex.DecodeString(m.config.PrivateKey) - if err != nil { - panic(err) - } - options := []core.SetupOption{ - core.IfName("none"), - core.IfMTU(m.config.IfMTU), - } - for _, peer := range m.config.Peers { - options = append(options, core.Peer{URI: peer}) - } - for intf, peers := range m.config.InterfacePeers { - for _, peer := range peers { - options = append(options, core.Peer{URI: peer, SourceInterface: intf}) - } - } - for _, allowed := range m.config.AllowedPublicKeys { - k, err := hex.DecodeString(allowed) + { + sk, err := hex.DecodeString(m.config.PrivateKey) + if err != nil { + panic(err) + } + options := []core.SetupOption{ + core.IfName("none"), + core.IfMTU(m.config.IfMTU), + } + for _, peer := range m.config.Peers { + options = append(options, core.Peer{URI: peer}) + } + for intf, peers := range m.config.InterfacePeers { + for _, peer := range peers { + options = append(options, core.Peer{URI: peer, SourceInterface: intf}) + } + } + for _, allowed := range m.config.AllowedPublicKeys { + k, err := hex.DecodeString(allowed) + if err != nil { + panic(err) + } + options = append(options, core.AllowedPublicKey(k[:])) + } + m.core, err = core.New(sk[:], logger, options...) if err != nil { panic(err) } - options = append(options, core.AllowedPublicKey(k[:])) } - m.core, err = core.New(sk[:], options...) - if err != nil { - panic(err) + + // Setup the multicast module. + if len(m.config.MulticastInterfaces) > 0 { + var err error + options := []multicast.SetupOption{} + for _, intf := range m.config.MulticastInterfaces { + options = append(options, multicast.MulticastInterface{ + Regex: regexp.MustCompile(intf.Regex), + Beacon: intf.Beacon, + Listen: intf.Listen, + Port: intf.Port, + }) + } + m.multicast, err = multicast.New(m.core, logger, options...) + if err != nil { + panic(err) + } } + mtu := m.config.IfMTU m.iprwc = ipv6rwc.NewReadWriteCloser(m.core) if m.iprwc.MaxMTU() < mtu { mtu = m.iprwc.MaxMTU() } m.iprwc.SetMTU(mtu) - if len(m.config.MulticastInterfaces) > 0 { - if err := m.multicast.Init(m.core, m.config, logger, nil); err != nil { - logger.Errorln("An error occurred initialising multicast:", err) - } else if err := m.multicast.Start(); err != nil { - logger.Errorln("An error occurred starting multicast:", err) - } - } return nil } diff --git a/src/core/api.go b/src/core/api.go index 3ab26ee5..679d43d5 100644 --- a/src/core/api.go +++ b/src/core/api.go @@ -15,8 +15,8 @@ import ( //"sort" //"time" - "github.com/gologme/log" "github.com/yggdrasil-network/yggdrasil-go/src/address" + "github.com/yggdrasil-network/yggdrasil-go/src/util" //"github.com/yggdrasil-network/yggdrasil-go/src/crypto" //"github.com/Arceliar/phony" ) @@ -159,7 +159,7 @@ func (c *Core) Subnet() net.IPNet { // may be useful if you want to redirect the output later. Note that this // expects a Logger from the github.com/gologme/log package and not from Go's // built-in log package. -func (c *Core) SetLogger(log *log.Logger) { +func (c *Core) SetLogger(log util.Logger) { c.log = log } @@ -239,8 +239,10 @@ func (c *Core) RemovePeer(addr string, sintf string) error { // CallPeer calls a peer once. This should be specified in the peer URI format, // e.g.: -// tcp://a.b.c.d:e -// socks://a.b.c.d:e/f.g.h.i:j +// +// tcp://a.b.c.d:e +// socks://a.b.c.d:e/f.g.h.i:j +// // This does not add the peer to the peer list, so if the connection drops, the // peer will not be called again automatically. func (c *Core) CallPeer(u *url.URL, sintf string) error { diff --git a/src/core/core.go b/src/core/core.go index bc7ac831..47389a83 100644 --- a/src/core/core.go +++ b/src/core/core.go @@ -7,7 +7,6 @@ import ( "io" "net" "net/url" - "os" "time" iwe "github.com/Arceliar/ironwood/encrypted" @@ -15,6 +14,7 @@ import ( "github.com/Arceliar/phony" "github.com/gologme/log" + "github.com/yggdrasil-network/yggdrasil-go/src/util" "github.com/yggdrasil-network/yggdrasil-go/src/version" //"github.com/yggdrasil-network/yggdrasil-go/src/crypto" ) @@ -33,7 +33,7 @@ type Core struct { public ed25519.PublicKey links links proto protoHandler - log *log.Logger + log util.Logger addPeerTimer *time.Timer config struct { _peers map[Peer]struct{} // configurable after startup @@ -46,14 +46,14 @@ type Core struct { } } -func New(secret ed25519.PrivateKey, opts ...SetupOption) (*Core, error) { +func New(secret ed25519.PrivateKey, logger util.Logger, opts ...SetupOption) (*Core, error) { if len(secret) != ed25519.PrivateKeySize { return nil, fmt.Errorf("private key is incorrect length") } c := &Core{ secret: secret, public: secret.Public().(ed25519.PublicKey), - log: log.New(os.Stdout, "", 0), // TODO: not this + log: logger, } c.ctx, c.cancel = context.WithCancel(context.Background()) var err error diff --git a/src/core/core_test.go b/src/core/core_test.go index 823e5e95..ed5b4255 100644 --- a/src/core/core_test.go +++ b/src/core/core_test.go @@ -36,10 +36,11 @@ func CreateAndConnectTwo(t testing.TB, verbose bool) (nodeA *Core, nodeB *Core) if _, skB, err = ed25519.GenerateKey(nil); err != nil { t.Fatal(err) } - if nodeA, err = New(skA, ListenAddress("tcp://127.0.0.1:0"), IfName("none")); err != nil { + logger := GetLoggerWithPrefix("", false) + if nodeA, err = New(skA, logger, ListenAddress("tcp://127.0.0.1:0"), IfName("none")); err != nil { t.Fatal(err) } - if nodeB, err = New(skB, ListenAddress("tcp://127.0.0.1:0"), IfName("none")); err != nil { + if nodeB, err = New(skB, logger, ListenAddress("tcp://127.0.0.1:0"), IfName("none")); err != nil { t.Fatal(err) } diff --git a/src/core/options.go b/src/core/options.go index d46c9def..b3d06c63 100644 --- a/src/core/options.go +++ b/src/core/options.go @@ -30,7 +30,6 @@ type SetupOption interface { } type ListenAddress string -type AdminListenAddress string type Peer struct { URI string SourceInterface string @@ -41,11 +40,10 @@ type IfName string type IfMTU uint16 type AllowedPublicKey ed25519.PublicKey -func (a ListenAddress) isSetupOption() {} -func (a AdminListenAddress) isSetupOption() {} -func (a Peer) isSetupOption() {} -func (a NodeInfo) isSetupOption() {} -func (a NodeInfoPrivacy) isSetupOption() {} -func (a IfName) isSetupOption() {} -func (a IfMTU) isSetupOption() {} -func (a AllowedPublicKey) isSetupOption() {} +func (a ListenAddress) isSetupOption() {} +func (a Peer) isSetupOption() {} +func (a NodeInfo) isSetupOption() {} +func (a NodeInfoPrivacy) isSetupOption() {} +func (a IfName) isSetupOption() {} +func (a IfMTU) isSetupOption() {} +func (a AllowedPublicKey) isSetupOption() {} diff --git a/src/multicast/multicast.go b/src/multicast/multicast.go index 9093e4cf..b6d290e1 100644 --- a/src/multicast/multicast.go +++ b/src/multicast/multicast.go @@ -9,13 +9,11 @@ import ( "fmt" "net" "net/url" - "regexp" "time" "github.com/Arceliar/phony" "github.com/gologme/log" - "github.com/yggdrasil-network/yggdrasil-go/src/config" "github.com/yggdrasil-network/yggdrasil-go/src/core" "golang.org/x/net/ipv6" ) @@ -27,13 +25,15 @@ import ( type Multicast struct { phony.Inbox core *core.Core - config *config.NodeConfig log *log.Logger sock *ipv6.PacketConn - groupAddr string - listeners map[string]*listenerInfo - isOpen bool - _interfaces map[string]interfaceInfo + _isOpen bool + _listeners map[string]*listenerInfo + _interfaces map[string]*interfaceInfo + config struct { + _groupAddr GroupAddress + _interfaces map[MulticastInterface]struct{} + } } type interfaceInfo struct { @@ -51,40 +51,38 @@ type listenerInfo struct { port uint16 } -// Init prepares the multicast interface for use. -func (m *Multicast) Init(core *core.Core, nc *config.NodeConfig, log *log.Logger, options interface{}) error { - m.core = core - m.config = nc - m.log = log - m.listeners = make(map[string]*listenerInfo) - m._interfaces = make(map[string]interfaceInfo) - m.groupAddr = "[ff02::114]:9001" - return nil -} - // Start starts the multicast interface. This launches goroutines which will // listen for multicast beacons from other hosts and will advertise multicast // beacons out to the network. -func (m *Multicast) Start() error { +func New(core *core.Core, log *log.Logger, opts ...SetupOption) (*Multicast, error) { + m := &Multicast{ + core: core, + log: log, + _listeners: make(map[string]*listenerInfo), + _interfaces: make(map[string]*interfaceInfo), + } + m.config._interfaces = map[MulticastInterface]struct{}{} + m.config._groupAddr = GroupAddress("[ff02::114]:9001") + for _, opt := range opts { + m._applyOption(opt) + } var err error phony.Block(m, func() { err = m._start() }) - m.log.Debugln("Started multicast module") - return err + return m, err } func (m *Multicast) _start() error { - if m.isOpen { + if m._isOpen { return fmt.Errorf("multicast module is already started") } - m.config.RLock() - defer m.config.RUnlock() - if len(m.config.MulticastInterfaces) == 0 { + if len(m.config._interfaces) == 0 { return nil } m.log.Infoln("Starting multicast module") - addr, err := net.ResolveUDPAddr("udp", m.groupAddr) + defer m.log.Infoln("Started multicast module") + addr, err := net.ResolveUDPAddr("udp", string(m.config._groupAddr)) if err != nil { return err } @@ -101,7 +99,7 @@ func (m *Multicast) _start() error { // Windows can't set this flag, so we need to handle it in other ways } - m.isOpen = true + m._isOpen = true go m.listen() m.Act(nil, m._multicastStarted) m.Act(nil, m._announce) @@ -113,7 +111,7 @@ func (m *Multicast) _start() error { func (m *Multicast) IsStarted() bool { var isOpen bool phony.Block(m, func() { - isOpen = m.isOpen + isOpen = m._isOpen }) return isOpen } @@ -130,7 +128,7 @@ func (m *Multicast) Stop() error { func (m *Multicast) _stop() error { m.log.Infoln("Stopping multicast module") - m.isOpen = false + m._isOpen = false if m.sock != nil { m.sock.Close() } @@ -138,7 +136,7 @@ func (m *Multicast) _stop() error { } func (m *Multicast) _updateInterfaces() { - interfaces := m.getAllowedInterfaces() + interfaces := m._getAllowedInterfaces() for name, info := range interfaces { addrs, err := info.iface.Addrs() if err != nil { @@ -163,10 +161,8 @@ func (m *Multicast) Interfaces() map[string]net.Interface { } // getAllowedInterfaces returns the currently known/enabled multicast interfaces. -func (m *Multicast) getAllowedInterfaces() map[string]interfaceInfo { - interfaces := make(map[string]interfaceInfo) - // Get interface expressions from config - ifcfgs := m.config.MulticastInterfaces +func (m *Multicast) _getAllowedInterfaces() map[string]*interfaceInfo { + interfaces := make(map[string]*interfaceInfo) // Ask the system for network interfaces allifaces, err := net.Interfaces() if err != nil { @@ -176,62 +172,55 @@ func (m *Multicast) getAllowedInterfaces() map[string]interfaceInfo { } // Work out which interfaces to announce on for _, iface := range allifaces { - if iface.Flags&net.FlagUp == 0 { - // Ignore interfaces that are down - continue + switch { + case iface.Flags&net.FlagUp == 0: + continue // Ignore interfaces that are down + case iface.Flags&net.FlagMulticast == 0: + continue // Ignore non-multicast interfaces + case iface.Flags&net.FlagPointToPoint != 0: + continue // Ignore point-to-point interfaces } - if iface.Flags&net.FlagMulticast == 0 { - // Ignore non-multicast interfaces - continue - } - if iface.Flags&net.FlagPointToPoint != 0 { - // Ignore point-to-point interfaces - continue - } - for _, ifcfg := range ifcfgs { + for ifcfg := range m.config._interfaces { // Compile each regular expression - e, err := regexp.Compile(ifcfg.Regex) - if err != nil { - panic(err) - } // Does the interface match the regular expression? Store it if so - if e.MatchString(iface.Name) { - if ifcfg.Beacon || ifcfg.Listen { - info := interfaceInfo{ - iface: iface, - beacon: ifcfg.Beacon, - listen: ifcfg.Listen, - port: ifcfg.Port, - } - interfaces[iface.Name] = info - } - break + if !ifcfg.Beacon && !ifcfg.Listen { + continue } + if !ifcfg.Regex.MatchString(iface.Name) { + continue + } + interfaces[iface.Name] = &interfaceInfo{ + iface: iface, + beacon: ifcfg.Beacon, + listen: ifcfg.Listen, + port: ifcfg.Port, + } + break } } return interfaces } func (m *Multicast) _announce() { - if !m.isOpen { + if !m._isOpen { return } m._updateInterfaces() - groupAddr, err := net.ResolveUDPAddr("udp6", m.groupAddr) + groupAddr, err := net.ResolveUDPAddr("udp6", string(m.config._groupAddr)) if err != nil { panic(err) } - destAddr, err := net.ResolveUDPAddr("udp6", m.groupAddr) + destAddr, err := net.ResolveUDPAddr("udp6", string(m.config._groupAddr)) if err != nil { panic(err) } // There might be interfaces that we configured listeners for but are no // longer up - if that's the case then we should stop the listeners - for name, info := range m.listeners { + for name, info := range m._listeners { // Prepare our stop function! stop := func() { info.listener.Stop() - delete(m.listeners, name) + delete(m._listeners, name) m.log.Debugln("No longer multicasting on", name) } // If the interface is no longer visible on the system then stop the @@ -290,7 +279,7 @@ func (m *Multicast) _announce() { } // Try and see if we already have a TCP listener for this interface var linfo *listenerInfo - if nfo, ok := m.listeners[iface.Name]; !ok || nfo.listener.Listener == nil { + if nfo, ok := m._listeners[iface.Name]; !ok || nfo.listener.Listener == nil { // No listener was found - let's create one urlString := fmt.Sprintf("tls://[%s]:%d", addrIP, info.port) u, err := url.Parse(urlString) @@ -301,13 +290,13 @@ func (m *Multicast) _announce() { m.log.Debugln("Started multicasting on", iface.Name) // Store the listener so that we can stop it later if needed linfo = &listenerInfo{listener: li, time: time.Now(), port: info.port} - m.listeners[iface.Name] = linfo + m._listeners[iface.Name] = linfo } else { m.log.Warnln("Not multicasting on", iface.Name, "due to error:", err) } } else { // An existing listener was found - linfo = m.listeners[iface.Name] + linfo = m._listeners[iface.Name] } // Make sure nothing above failed for some reason if linfo == nil { @@ -340,7 +329,7 @@ func (m *Multicast) _announce() { } func (m *Multicast) listen() { - groupAddr, err := net.ResolveUDPAddr("udp6", m.groupAddr) + groupAddr, err := net.ResolveUDPAddr("udp6", string(m.config._groupAddr)) if err != nil { panic(err) } @@ -388,7 +377,7 @@ func (m *Multicast) listen() { if !from.IP.Equal(addr.IP) { continue } - var interfaces map[string]interfaceInfo + var interfaces map[string]*interfaceInfo phony.Block(m, func() { interfaces = m._interfaces }) diff --git a/src/multicast/multicast_darwin_cgo.go b/src/multicast/multicast_darwin_cgo.go index b7d7358c..5c2af7ab 100644 --- a/src/multicast/multicast_darwin_cgo.go +++ b/src/multicast/multicast_darwin_cgo.go @@ -31,7 +31,7 @@ import ( ) func (m *Multicast) _multicastStarted() { - if !m.isOpen { + if !m._isOpen { return } C.StopAWDLBrowsing() diff --git a/src/multicast/options.go b/src/multicast/options.go new file mode 100644 index 00000000..a03b0677 --- /dev/null +++ b/src/multicast/options.go @@ -0,0 +1,28 @@ +package multicast + +import "regexp" + +func (m *Multicast) _applyOption(opt SetupOption) { + switch v := opt.(type) { + case MulticastInterface: + m.config._interfaces[v] = struct{}{} + case GroupAddress: + m.config._groupAddr = v + } +} + +type SetupOption interface { + isSetupOption() +} + +type MulticastInterface struct { + Regex *regexp.Regexp + Beacon bool + Listen bool + Port uint16 +} + +type GroupAddress string + +func (a MulticastInterface) isSetupOption() {} +func (a GroupAddress) isSetupOption() {} diff --git a/src/util/util.go b/src/util/util.go index 507426d0..e2e21464 100644 --- a/src/util/util.go +++ b/src/util/util.go @@ -8,6 +8,20 @@ import ( "time" ) +// Any logger that satisfies this interface is suitable for Yggdrasil. +type Logger interface { + Printf(string, ...interface{}) + Println(...interface{}) + Infof(string, ...interface{}) + Infoln(...interface{}) + Warnf(string, ...interface{}) + Warnln(...interface{}) + Errorf(string, ...interface{}) + Errorln(...interface{}) + Debugf(string, ...interface{}) + Debugln(...interface{}) +} + // TimerStop stops a timer and makes sure the channel is drained, returns true if the timer was stopped before firing. func TimerStop(t *time.Timer) bool { stopped := t.Stop() From b1f61fb0a8d2776164bf151868f85c937c111311 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 3 Sep 2022 11:54:46 +0100 Subject: [PATCH 032/293] Refactor admin socket setup (isolated config) --- cmd/yggdrasil/main.go | 31 ++++++++--------- src/admin/admin.go | 81 +++++++++++++++++++++---------------------- src/admin/options.go | 16 +++++++++ 3 files changed, 70 insertions(+), 58 deletions(-) create mode 100644 src/admin/options.go diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 71453aaa..f8d882d6 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -359,6 +359,17 @@ func run(args yggArgs, ctx context.Context, done chan struct{}) { } } + // Setup the admin socket. + { + options := []admin.SetupOption{ + admin.ListenAddress(cfg.AdminListen), + } + n.admin, err = admin.New(n.core, logger, options...) + if err != nil { + panic(err) + } + } + // Setup the multicast module. { options := []multicast.SetupOption{} @@ -374,25 +385,13 @@ func run(args yggArgs, ctx context.Context, done chan struct{}) { if err != nil { panic(err) } + if n.admin != nil { + n.multicast.SetupAdminHandlers(n.admin) + } } - // Register the session firewall gatekeeper function - // Allocate our modules - n.admin = &admin.AdminSocket{} - n.tuntap = &tuntap.TunAdapter{} - // Start the admin socket - if err := n.admin.Init(n.core, cfg, logger, nil); err != nil { - logger.Errorln("An error occurred initialising admin socket:", err) - } else if err := n.admin.Start(); err != nil { - logger.Errorln("An error occurred starting admin socket:", err) - } - n.admin.SetupAdminHandlers() - // Start the multicast interface - if n.multicast, err = multicast.New(n.core, logger, nil); err != nil { - logger.Errorln("An error occurred initialising multicast:", err) - } - n.multicast.SetupAdminHandlers(n.admin) // Start the TUN/TAP interface + n.tuntap = &tuntap.TunAdapter{} rwc := ipv6rwc.NewReadWriteCloser(n.core) if err := n.tuntap.Init(rwc, cfg, logger, nil); err != nil { logger.Errorln("An error occurred initialising TUN/TAP:", err) diff --git a/src/admin/admin.go b/src/admin/admin.go index 9a00d8a1..d3e95dfa 100644 --- a/src/admin/admin.go +++ b/src/admin/admin.go @@ -12,21 +12,21 @@ import ( "strings" "time" - "github.com/gologme/log" - - "github.com/yggdrasil-network/yggdrasil-go/src/config" "github.com/yggdrasil-network/yggdrasil-go/src/core" + "github.com/yggdrasil-network/yggdrasil-go/src/util" ) // TODO: Add authentication type AdminSocket struct { - core *core.Core - log *log.Logger - listenaddr string - listener net.Listener - handlers map[string]handler - done chan struct{} + core *core.Core + log util.Logger + listener net.Listener + handlers map[string]handler + done chan struct{} + config struct { + listenaddr ListenAddress + } } type AdminSocketRequest struct { @@ -69,15 +69,18 @@ func (a *AdminSocket) AddHandler(name string, args []string, handlerfunc core.Ad } // Init runs the initial admin setup. -func (a *AdminSocket) Init(c *core.Core, nc *config.NodeConfig, log *log.Logger, options interface{}) error { - a.core = c - a.log = log - a.handlers = make(map[string]handler) - nc.RLock() - a.listenaddr = nc.AdminListen - nc.RUnlock() - a.done = make(chan struct{}) - close(a.done) // Start in a done / not-started state +func New(c *core.Core, log util.Logger, opts ...SetupOption) (*AdminSocket, error) { + a := &AdminSocket{ + core: c, + log: log, + handlers: make(map[string]handler), + } + for _, opt := range opts { + a._applyOption(opt) + } + if a.config.listenaddr == "none" || a.config.listenaddr == "" { + return nil, nil + } _ = a.AddHandler("list", []string{}, func(_ json.RawMessage) (interface{}, error) { res := &ListResponse{} for name, handler := range a.handlers { @@ -91,7 +94,9 @@ func (a *AdminSocket) Init(c *core.Core, nc *config.NodeConfig, log *log.Logger, }) return res, nil }) - return a.core.SetAdmin(a) + a.done = make(chan struct{}) + go a.listen() + return a, a.core.SetAdmin(a) } func (a *AdminSocket) SetupAdminHandlers() { @@ -156,15 +161,6 @@ func (a *AdminSocket) SetupAdminHandlers() { //_ = a.AddHandler("debug_remoteGetDHT", []string{"key"}, t.proto.getDHTHandler) } -// Start runs the admin API socket to listen for / respond to admin API calls. -func (a *AdminSocket) Start() error { - if a.listenaddr != "none" && a.listenaddr != "" { - a.done = make(chan struct{}) - go a.listen() - } - return nil -} - // IsStarted returns true if the module has been started. func (a *AdminSocket) IsStarted() bool { select { @@ -192,31 +188,32 @@ func (a *AdminSocket) Stop() error { // listen is run by start and manages API connections. func (a *AdminSocket) listen() { - u, err := url.Parse(a.listenaddr) + listenaddr := string(a.config.listenaddr) + u, err := url.Parse(listenaddr) if err == nil { switch strings.ToLower(u.Scheme) { case "unix": - if _, err := os.Stat(a.listenaddr[7:]); err == nil { - a.log.Debugln("Admin socket", a.listenaddr[7:], "already exists, trying to clean up") - if _, err := net.DialTimeout("unix", a.listenaddr[7:], time.Second*2); err == nil || err.(net.Error).Timeout() { - a.log.Errorln("Admin socket", a.listenaddr[7:], "already exists and is in use by another process") + if _, err := os.Stat(listenaddr[7:]); err == nil { + a.log.Debugln("Admin socket", listenaddr[7:], "already exists, trying to clean up") + if _, err := net.DialTimeout("unix", listenaddr[7:], time.Second*2); err == nil || err.(net.Error).Timeout() { + a.log.Errorln("Admin socket", listenaddr[7:], "already exists and is in use by another process") os.Exit(1) } else { - if err := os.Remove(a.listenaddr[7:]); err == nil { - a.log.Debugln(a.listenaddr[7:], "was cleaned up") + if err := os.Remove(listenaddr[7:]); err == nil { + a.log.Debugln(listenaddr[7:], "was cleaned up") } else { - a.log.Errorln(a.listenaddr[7:], "already exists and was not cleaned up:", err) + a.log.Errorln(listenaddr[7:], "already exists and was not cleaned up:", err) os.Exit(1) } } } - a.listener, err = net.Listen("unix", a.listenaddr[7:]) + a.listener, err = net.Listen("unix", listenaddr[7:]) if err == nil { - switch a.listenaddr[7:8] { + switch listenaddr[7:8] { case "@": // maybe abstract namespace default: - if err := os.Chmod(a.listenaddr[7:], 0660); err != nil { - a.log.Warnln("WARNING:", a.listenaddr[:7], "may have unsafe permissions!") + if err := os.Chmod(listenaddr[7:], 0660); err != nil { + a.log.Warnln("WARNING:", listenaddr[:7], "may have unsafe permissions!") } } } @@ -224,10 +221,10 @@ func (a *AdminSocket) listen() { a.listener, err = net.Listen("tcp", u.Host) default: // err = errors.New(fmt.Sprint("protocol not supported: ", u.Scheme)) - a.listener, err = net.Listen("tcp", a.listenaddr) + a.listener, err = net.Listen("tcp", listenaddr) } } else { - a.listener, err = net.Listen("tcp", a.listenaddr) + a.listener, err = net.Listen("tcp", listenaddr) } if err != nil { a.log.Errorf("Admin socket failed to listen: %v", err) diff --git a/src/admin/options.go b/src/admin/options.go new file mode 100644 index 00000000..4607384f --- /dev/null +++ b/src/admin/options.go @@ -0,0 +1,16 @@ +package admin + +func (c *AdminSocket) _applyOption(opt SetupOption) { + switch v := opt.(type) { + case ListenAddress: + c.config.listenaddr = v + } +} + +type SetupOption interface { + isSetupOption() +} + +type ListenAddress string + +func (a ListenAddress) isSetupOption() {} From a7d06e048aec7068d31c2564580af7f20d79eeb6 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 3 Sep 2022 12:20:57 +0100 Subject: [PATCH 033/293] Refactor TUN setup (isolated config) --- cmd/yggdrasil/main.go | 26 +++++++++++++-------- src/multicast/multicast.go | 4 ++-- src/tuntap/options.go | 20 ++++++++++++++++ src/tuntap/tun.go | 47 +++++++++++++++----------------------- 4 files changed, 57 insertions(+), 40 deletions(-) create mode 100644 src/tuntap/options.go diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index f8d882d6..f239ed34 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -38,7 +38,6 @@ import ( type node struct { core *core.Core - config *config.NodeConfig tuntap *tuntap.TunAdapter multicast *multicast.Multicast admin *admin.AdminSocket @@ -326,7 +325,7 @@ func run(args yggArgs, ctx context.Context, done chan struct{}) { default: } - n := &node{config: cfg} + n := &node{} // Setup the Yggdrasil node itself. { @@ -390,15 +389,22 @@ func run(args yggArgs, ctx context.Context, done chan struct{}) { } } - // Start the TUN/TAP interface - n.tuntap = &tuntap.TunAdapter{} - rwc := ipv6rwc.NewReadWriteCloser(n.core) - if err := n.tuntap.Init(rwc, cfg, logger, nil); err != nil { - logger.Errorln("An error occurred initialising TUN/TAP:", err) - } else if err := n.tuntap.Start(); err != nil { - logger.Errorln("An error occurred starting TUN/TAP:", err) + // Setup the TUN module. + { + options := []tuntap.SetupOption{ + tuntap.InterfaceName(cfg.IfName), + tuntap.InterfaceMTU(cfg.IfMTU), + } + rwc := ipv6rwc.NewReadWriteCloser(n.core) + n.tuntap, err = tuntap.New(rwc, logger, options...) + if err != nil { + panic(err) + } + if n.admin != nil { + n.tuntap.SetupAdminHandlers(n.admin) + } } - n.tuntap.SetupAdminHandlers(n.admin) + // Make some nice output that tells us what our IPv6 address and subnet are. // This is just logged to stdout for the user. address := n.core.Address() diff --git a/src/multicast/multicast.go b/src/multicast/multicast.go index b6d290e1..24e9d04a 100644 --- a/src/multicast/multicast.go +++ b/src/multicast/multicast.go @@ -80,8 +80,8 @@ func (m *Multicast) _start() error { if len(m.config._interfaces) == 0 { return nil } - m.log.Infoln("Starting multicast module") - defer m.log.Infoln("Started multicast module") + m.log.Debugln("Starting multicast module") + defer m.log.Debugln("Started multicast module") addr, err := net.ResolveUDPAddr("udp", string(m.config._groupAddr)) if err != nil { return err diff --git a/src/tuntap/options.go b/src/tuntap/options.go new file mode 100644 index 00000000..10af8d96 --- /dev/null +++ b/src/tuntap/options.go @@ -0,0 +1,20 @@ +package tuntap + +func (m *TunAdapter) _applyOption(opt SetupOption) { + switch v := opt.(type) { + case InterfaceName: + m.config.name = v + case InterfaceMTU: + m.config.mtu = v + } +} + +type SetupOption interface { + isSetupOption() +} + +type InterfaceName string +type InterfaceMTU uint64 + +func (a InterfaceName) isSetupOption() {} +func (a InterfaceMTU) isSetupOption() {} diff --git a/src/tuntap/tun.go b/src/tuntap/tun.go index eddccbcd..b0c444f4 100644 --- a/src/tuntap/tun.go +++ b/src/tuntap/tun.go @@ -16,13 +16,12 @@ import ( //"sync" "github.com/Arceliar/phony" - "github.com/gologme/log" "golang.zx2c4.com/wireguard/tun" "github.com/yggdrasil-network/yggdrasil-go/src/address" - "github.com/yggdrasil-network/yggdrasil-go/src/config" "github.com/yggdrasil-network/yggdrasil-go/src/defaults" "github.com/yggdrasil-network/yggdrasil-go/src/ipv6rwc" + "github.com/yggdrasil-network/yggdrasil-go/src/util" ) type MTU uint16 @@ -33,8 +32,7 @@ type MTU uint16 // calling yggdrasil.Start(). type TunAdapter struct { rwc *ipv6rwc.ReadWriteCloser - config *config.NodeConfig - log *log.Logger + log util.Logger addr address.Address subnet address.Subnet mtu uint64 @@ -43,6 +41,10 @@ type TunAdapter struct { //mutex sync.RWMutex // Protects the below isOpen bool isEnabled bool // Used by the writer to drop sessionTraffic if not enabled + config struct { + name InterfaceName + mtu InterfaceMTU + } } // Gets the maximum supported MTU for the platform based on the defaults in @@ -93,50 +95,39 @@ func MaximumMTU() uint64 { // Init initialises the TUN module. You must have acquired a Listener from // the Yggdrasil core before this point and it must not be in use elsewhere. -func (tun *TunAdapter) Init(rwc *ipv6rwc.ReadWriteCloser, config *config.NodeConfig, log *log.Logger, options interface{}) error { - tun.rwc = rwc - tun.config = config - tun.log = log - return nil -} - -// Start the setup process for the TUN adapter. If successful, starts the -// reader actor to handle packets on that interface. -func (tun *TunAdapter) Start() error { - var err error - phony.Block(tun, func() { - err = tun._start() - }) - return err +func New(rwc *ipv6rwc.ReadWriteCloser, log util.Logger, opts ...SetupOption) (*TunAdapter, error) { + tun := &TunAdapter{ + rwc: rwc, + log: log, + } + for _, opt := range opts { + tun._applyOption(opt) + } + return tun, tun._start() } func (tun *TunAdapter) _start() error { if tun.isOpen { return errors.New("TUN module is already started") } - if tun.config == nil { - return errors.New("no configuration available to TUN") - } - tun.config.RLock() - defer tun.config.RUnlock() tun.addr = tun.rwc.Address() tun.subnet = tun.rwc.Subnet() addr := fmt.Sprintf("%s/%d", net.IP(tun.addr[:]).String(), 8*len(address.GetPrefix())-1) - if tun.config.IfName == "none" || tun.config.IfName == "dummy" { + if tun.config.name == "none" || tun.config.name == "dummy" { tun.log.Debugln("Not starting TUN as ifname is none or dummy") tun.isEnabled = false go tun.write() return nil } - mtu := tun.config.IfMTU + mtu := uint64(tun.config.mtu) if tun.rwc.MaxMTU() < mtu { mtu = tun.rwc.MaxMTU() } - if err := tun.setup(tun.config.IfName, addr, mtu); err != nil { + if err := tun.setup(string(tun.config.name), addr, mtu); err != nil { return err } if tun.MTU() != mtu { - tun.log.Warnf("Warning: Interface MTU %d automatically adjusted to %d (supported range is 1280-%d)", tun.config.IfMTU, tun.MTU(), MaximumMTU()) + tun.log.Warnf("Warning: Interface MTU %d automatically adjusted to %d (supported range is 1280-%d)", tun.config.mtu, tun.MTU(), MaximumMTU()) } tun.rwc.SetMTU(tun.MTU()) tun.isOpen = true From 9cdfd5947618046df68047fd31375a9ae73586db Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 3 Sep 2022 12:34:29 +0100 Subject: [PATCH 034/293] Tidy up a bit, make sure to copy the private key at startup --- cmd/yggdrasil/main.go | 56 ++++--------------------------------------- src/core/core.go | 14 ++++++----- 2 files changed, 12 insertions(+), 58 deletions(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index f239ed34..3235c4ca 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -81,48 +81,6 @@ func readConfig(log *log.Logger, useconf bool, useconffile string, normaliseconf if err := hjson.Unmarshal(conf, &dat); err != nil { panic(err) } - // Check if we have old field names - if _, ok := dat["TunnelRouting"]; ok { - log.Warnln("WARNING: Tunnel routing is no longer supported") - } - if old, ok := dat["SigningPrivateKey"]; ok { - log.Warnln("WARNING: The \"SigningPrivateKey\" configuration option has been renamed to \"PrivateKey\"") - if _, ok := dat["PrivateKey"]; !ok { - if privstr, err := hex.DecodeString(old.(string)); err == nil { - priv := ed25519.PrivateKey(privstr) - pub := priv.Public().(ed25519.PublicKey) - dat["PrivateKey"] = hex.EncodeToString(priv[:]) - dat["PublicKey"] = hex.EncodeToString(pub[:]) - } else { - log.Warnln("WARNING: The \"SigningPrivateKey\" configuration option contains an invalid value and will be ignored") - } - } - } - if oldmc, ok := dat["MulticastInterfaces"]; ok { - if oldmcvals, ok := oldmc.([]interface{}); ok { - var newmc []config.MulticastInterfaceConfig - for _, oldmcval := range oldmcvals { - if str, ok := oldmcval.(string); ok { - newmc = append(newmc, config.MulticastInterfaceConfig{ - Regex: str, - Beacon: true, - Listen: true, - }) - } - } - if newmc != nil { - if oldport, ok := dat["LinkLocalTCPPort"]; ok { - // numbers parse to float64 by default - if port, ok := oldport.(float64); ok { - for idx := range newmc { - newmc[idx].Port = uint16(port) - } - } - } - dat["MulticastInterfaces"] = newmc - } - } - } // Sanitise the config confJson, err := json.Marshal(dat) if err != nil { @@ -322,7 +280,6 @@ func run(args yggArgs, ctx context.Context, done chan struct{}) { fmt.Println(ipnet.String()) } return - default: } n := &node{} @@ -352,8 +309,7 @@ func run(args yggArgs, ctx context.Context, done chan struct{}) { } options = append(options, core.AllowedPublicKey(k[:])) } - n.core, err = core.New(sk[:], logger, options...) - if err != nil { + if n.core, err = core.New(sk[:], logger, options...); err != nil { panic(err) } } @@ -363,8 +319,7 @@ func run(args yggArgs, ctx context.Context, done chan struct{}) { options := []admin.SetupOption{ admin.ListenAddress(cfg.AdminListen), } - n.admin, err = admin.New(n.core, logger, options...) - if err != nil { + if n.admin, err = admin.New(n.core, logger, options...); err != nil { panic(err) } } @@ -380,8 +335,7 @@ func run(args yggArgs, ctx context.Context, done chan struct{}) { Port: intf.Port, }) } - n.multicast, err = multicast.New(n.core, logger, options...) - if err != nil { + if n.multicast, err = multicast.New(n.core, logger, options...); err != nil { panic(err) } if n.admin != nil { @@ -395,9 +349,7 @@ func run(args yggArgs, ctx context.Context, done chan struct{}) { tuntap.InterfaceName(cfg.IfName), tuntap.InterfaceMTU(cfg.IfMTU), } - rwc := ipv6rwc.NewReadWriteCloser(n.core) - n.tuntap, err = tuntap.New(rwc, logger, options...) - if err != nil { + if n.tuntap, err = tuntap.New(ipv6rwc.NewReadWriteCloser(n.core), logger, options...); err != nil { panic(err) } if n.admin != nil { diff --git a/src/core/core.go b/src/core/core.go index 47389a83..22baa58a 100644 --- a/src/core/core.go +++ b/src/core/core.go @@ -47,15 +47,17 @@ type Core struct { } func New(secret ed25519.PrivateKey, logger util.Logger, opts ...SetupOption) (*Core, error) { + c := &Core{ + log: logger, + } + c.ctx, c.cancel = context.WithCancel(context.Background()) + // Take a copy of the private key so that it is in our own memory space. if len(secret) != ed25519.PrivateKeySize { return nil, fmt.Errorf("private key is incorrect length") } - c := &Core{ - secret: secret, - public: secret.Public().(ed25519.PublicKey), - log: logger, - } - c.ctx, c.cancel = context.WithCancel(context.Background()) + c.secret = make(ed25519.PrivateKey, 0, ed25519.PrivateKeySize) + copy(c.secret, secret) + c.public = secret.Public().(ed25519.PublicKey) var err error if c.PacketConn, err = iwe.NewPacketConn(c.secret); err != nil { return nil, fmt.Errorf("error creating encryption: %w", err) From 5477566fa9cb1b5dc2552f8a0f70e1857adad246 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 3 Sep 2022 12:38:42 +0100 Subject: [PATCH 035/293] Length not capacity --- src/core/core.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/core.go b/src/core/core.go index 22baa58a..6b9fe547 100644 --- a/src/core/core.go +++ b/src/core/core.go @@ -55,7 +55,7 @@ func New(secret ed25519.PrivateKey, logger util.Logger, opts ...SetupOption) (*C if len(secret) != ed25519.PrivateKeySize { return nil, fmt.Errorf("private key is incorrect length") } - c.secret = make(ed25519.PrivateKey, 0, ed25519.PrivateKeySize) + c.secret = make(ed25519.PrivateKey, ed25519.PrivateKeySize) copy(c.secret, secret) c.public = secret.Public().(ed25519.PublicKey) var err error From dc9720e580e09a150beca5b1e2a637b2290cc650 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 3 Sep 2022 16:55:57 +0100 Subject: [PATCH 036/293] Extend `getSessions` admin call to include uptime/TX/RX --- cmd/yggdrasil/main.go | 1 + cmd/yggdrasilctl/main.go | 5 ++- go.mod | 3 +- go.sum | 93 +--------------------------------------- src/admin/getsessions.go | 10 ++++- src/core/api.go | 8 +++- 6 files changed, 23 insertions(+), 97 deletions(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 3235c4ca..a2aec15b 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -322,6 +322,7 @@ func run(args yggArgs, ctx context.Context, done chan struct{}) { if n.admin, err = admin.New(n.core, logger, options...); err != nil { panic(err) } + n.admin.SetupAdminHandlers() } // Setup the multicast module. diff --git a/cmd/yggdrasilctl/main.go b/cmd/yggdrasilctl/main.go index 5e8bee2a..78700a40 100644 --- a/cmd/yggdrasilctl/main.go +++ b/cmd/yggdrasilctl/main.go @@ -216,11 +216,14 @@ func run() int { if err := json.Unmarshal(recv.Response, &resp); err != nil { panic(err) } - table.SetHeader([]string{"Public Key", "IP Address"}) + table.SetHeader([]string{"Public Key", "IP Address", "Uptime", "RX", "TX"}) for _, p := range resp.Sessions { table.Append([]string{ p.PublicKey, p.IPAddress, + (time.Duration(p.Uptime) * time.Second).String(), + p.RXBytes.String(), + p.TXBytes.String(), }) } table.Render() diff --git a/go.mod b/go.mod index c790b19d..b72bf534 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/yggdrasil-network/yggdrasil-go go 1.17 require ( - github.com/Arceliar/ironwood v0.0.0-20220409035209-b7f71f05435a + github.com/Arceliar/ironwood v0.0.0-20220903132624-ee60c16bcfcf github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 github.com/cheggaaa/pb/v3 v3.0.8 github.com/gologme/log v1.2.0 @@ -34,6 +34,5 @@ require ( github.com/mattn/go-isatty v0.0.13 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect github.com/olekukonko/tablewriter v0.0.5 - github.com/onsi/gomega v1.20.2 github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f // indirect ) diff --git a/go.sum b/go.sum index 5d22d679..ec5ea18f 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Arceliar/ironwood v0.0.0-20220409035209-b7f71f05435a h1:yfbnOyqPcx2gi5cFIJ2rlPz5M6rFPHT/c8FgZmFjCdc= -github.com/Arceliar/ironwood v0.0.0-20220409035209-b7f71f05435a/go.mod h1:RP72rucOFm5udrnEzTmIWLRVGQiV/fSUAQXJ0RST/nk= +github.com/Arceliar/ironwood v0.0.0-20220903132624-ee60c16bcfcf h1:kjPkmDHUTWUma/4tqDl208bOk3jsUEqOJA6TsMZo5Jk= +github.com/Arceliar/ironwood v0.0.0-20220903132624-ee60c16bcfcf/go.mod h1:RP72rucOFm5udrnEzTmIWLRVGQiV/fSUAQXJ0RST/nk= github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 h1:WndgpSW13S32VLQ3ugUxx2EnnWmgba1kCqPkd4Gk1yQ= github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979/go.mod h1:6Lkn+/zJilRMsKmbmG1RPoamiArC6HS73xbwRyp3UyI= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= @@ -8,40 +8,15 @@ github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1o github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= github.com/cheggaaa/pb/v3 v3.0.8 h1:bC8oemdChbke2FHIIGy9mn4DPJ2caZYQnfbRqwmdCoA= github.com/cheggaaa/pb/v3 v3.0.8/go.mod h1:UICbiLec/XO6Hw6k+BHEtHeQFzzBH4i2/qk/ow1EJTA= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/gologme/log v1.2.0 h1:Ya5Ip/KD6FX7uH0S31QO87nCCSucKtF44TLbTtO7V4c= github.com/gologme/log v1.2.0/go.mod h1:gq31gQ8wEHkR+WekdWsqDuf8pXTUZA9BnnzTuPz1Y9U= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hjson/hjson-go v3.1.0+incompatible h1:DY/9yE8ey8Zv22bY+mHV1uk2yRy0h8tKhZ77hEdi0Aw= github.com/hjson/hjson-go v3.1.0+incompatible/go.mod h1:qsetwF8NlsTsOTwZTApNlTCerV+b2GjYRRcIk4JMFio= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/kardianos/minwinsvc v1.0.0 h1:+JfAi8IBJna0jY2dJGZqi7o15z13JelFIklJCAENALA= github.com/kardianos/minwinsvc v1.0.0/go.mod h1:Bgd0oc+D0Qo3bBytmNtyRKVlp85dAloLKhfxanPFFRc= github.com/lxn/walk v0.0.0-20210112085537-c389da54e794/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ= @@ -57,42 +32,21 @@ github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4 github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= -github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= -github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= -github.com/onsi/gomega v1.20.2 h1:8uQq0zMgLEfa0vRrrBgaJF2gyW9Da9BmfGV+OyUzfkY= -github.com/onsi/gomega v1.20.2/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f h1:p4VB7kIXpOQvVn1ZaTIVp+3vuYAXFe3OJEvjbUYJLaA= github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= @@ -103,59 +57,36 @@ golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105 h1:3vUV5x5+3LfQbgk7paCM6INOaJG9xXQbn79xoNkwfIk= golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210927181540-4e4d966f7476/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211011170408-caeb26a5c8c0/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -170,35 +101,15 @@ golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b/go.mod h1:EFNZuWvGYxIRUEX golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.8-0.20211022200916-316ba0b74098/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= -golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.zx2c4.com/wireguard v0.0.0-20211012062646-82d2aa87aa62/go.mod h1:id8Oh3eCCmpj9uVGWVjsUAl6UPX5ysMLzu6QxJU2UOU= golang.zx2c4.com/wireguard v0.0.0-20211017052713-f87e87af0d9a h1:tTbyylK9/D3u/wEP26Vx7L700UpY48nhioJWZM1vhZw= golang.zx2c4.com/wireguard v0.0.0-20211017052713-f87e87af0d9a/go.mod h1:id8Oh3eCCmpj9uVGWVjsUAl6UPX5ysMLzu6QxJU2UOU= golang.zx2c4.com/wireguard/windows v0.4.12 h1:CUmbdWKVNzTSsVb4yUAiEwL3KsabdJkEPdDjCHxBlhA= golang.zx2c4.com/wireguard/windows v0.4.12/go.mod h1:PW4y+d9oY83XU9rRwRwrJDwEMuhVjMxu2gfD1cfzS7w= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/src/admin/getsessions.go b/src/admin/getsessions.go index 58ce378e..324119d4 100644 --- a/src/admin/getsessions.go +++ b/src/admin/getsessions.go @@ -16,8 +16,11 @@ type GetSessionsResponse struct { } type SessionEntry struct { - IPAddress string `json:"address"` - PublicKey string `json:"key"` + IPAddress string `json:"address"` + PublicKey string `json:"key"` + RXBytes DataUnit `json:"bytes_recvd"` + TXBytes DataUnit `json:"bytes_sent"` + Uptime float64 `json:"uptime"` } func (a *AdminSocket) getSessionsHandler(req *GetSessionsRequest, res *GetSessionsResponse) error { @@ -28,6 +31,9 @@ func (a *AdminSocket) getSessionsHandler(req *GetSessionsRequest, res *GetSessio res.Sessions = append(res.Sessions, SessionEntry{ IPAddress: net.IP(addr[:]).String(), PublicKey: hex.EncodeToString(s.Key[:]), + RXBytes: DataUnit(s.RXBytes), + TXBytes: DataUnit(s.TXBytes), + Uptime: s.Uptime.Seconds(), }) } sort.SliceStable(res.Sessions, func(i, j int) bool { diff --git a/src/core/api.go b/src/core/api.go index 679d43d5..9f56983b 100644 --- a/src/core/api.go +++ b/src/core/api.go @@ -50,7 +50,10 @@ type PathEntryInfo struct { } type SessionInfo struct { - Key ed25519.PublicKey + Key ed25519.PublicKey + RXBytes uint64 + TXBytes uint64 + Uptime time.Duration } func (c *Core) GetSelf() SelfInfo { @@ -122,6 +125,9 @@ func (c *Core) GetSessions() []SessionInfo { for _, s := range ss { var info SessionInfo info.Key = s.Key + info.RXBytes = s.RX + info.TXBytes = s.TX + info.Uptime = s.Uptime sessions = append(sessions, info) } return sessions From 88a393a7b334d51983f042e835820586e937b275 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 3 Sep 2022 17:26:12 +0100 Subject: [PATCH 037/293] Load listen addresses --- cmd/yggdrasil/main.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index a2aec15b..2486df45 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -294,6 +294,9 @@ func run(args yggArgs, ctx context.Context, done chan struct{}) { core.IfName(cfg.IfName), core.IfMTU(cfg.IfMTU), } + for _, addr := range cfg.Listen { + options = append(options, core.ListenAddress(addr)) + } for _, peer := range cfg.Peers { options = append(options, core.Peer{URI: peer}) } From 414aaf6eb942e0ccf9656ea5eb03808c968f3838 Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Mon, 5 Sep 2022 19:55:35 +0800 Subject: [PATCH 038/293] Update mobile.go (#942) --- contrib/mobile/mobile.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/mobile/mobile.go b/contrib/mobile/mobile.go index d7da03de..1e6d3780 100644 --- a/contrib/mobile/mobile.go +++ b/contrib/mobile/mobile.go @@ -94,7 +94,7 @@ func (m *Yggdrasil) StartJSON(configjson []byte) error { } m.multicast, err = multicast.New(m.core, logger, options...) if err != nil { - panic(err) + logger.Errorln("An error occurred starting multicast:", err) } } From 5ef61faeff9028b928600ce116c6b069a5de171e Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 17 Sep 2022 20:07:00 +0100 Subject: [PATCH 039/293] Link refactor (#941) * Link refactoring * More refactoring * More tweaking * Cleaner shutdowns, UNIX socket support, more tweaks * Actorise links, remove mutex * SOCKS support --- cmd/yggdrasil/main.go | 15 +- contrib/mobile/mobile.go | 5 +- src/admin/admin.go | 5 + src/core/api.go | 25 +- src/core/core.go | 33 +- src/core/core_test.go | 4 +- src/core/link.go | 278 +++++++----- src/core/link_socks.go | 52 +++ src/core/link_tcp.go | 183 ++++++++ .../{tcp_darwin.go => link_tcp_darwin.go} | 4 +- src/core/{tcp_linux.go => link_tcp_linux.go} | 4 +- src/core/{tcp_other.go => link_tcp_other.go} | 4 +- src/core/link_tls.go | 171 +++++++ src/core/link_unix.go | 98 ++++ src/core/options.go | 8 - src/core/tcp.go | 417 ------------------ src/core/tls.go | 126 ------ src/multicast/multicast.go | 4 +- 18 files changed, 738 insertions(+), 698 deletions(-) create mode 100644 src/core/link_socks.go create mode 100644 src/core/link_tcp.go rename src/core/{tcp_darwin.go => link_tcp_darwin.go} (74%) rename src/core/{tcp_linux.go => link_tcp_linux.go} (86%) rename src/core/{tcp_other.go => link_tcp_other.go} (55%) create mode 100644 src/core/link_tls.go create mode 100644 src/core/link_unix.go delete mode 100644 src/core/tcp.go delete mode 100644 src/core/tls.go diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 2486df45..1ef6738b 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -28,9 +28,9 @@ import ( "github.com/yggdrasil-network/yggdrasil-go/src/admin" "github.com/yggdrasil-network/yggdrasil-go/src/config" "github.com/yggdrasil-network/yggdrasil-go/src/defaults" + "github.com/yggdrasil-network/yggdrasil-go/src/ipv6rwc" "github.com/yggdrasil-network/yggdrasil-go/src/core" - "github.com/yggdrasil-network/yggdrasil-go/src/ipv6rwc" "github.com/yggdrasil-network/yggdrasil-go/src/multicast" "github.com/yggdrasil-network/yggdrasil-go/src/tuntap" "github.com/yggdrasil-network/yggdrasil-go/src/version" @@ -290,10 +290,7 @@ func run(args yggArgs, ctx context.Context, done chan struct{}) { if err != nil { panic(err) } - options := []core.SetupOption{ - core.IfName(cfg.IfName), - core.IfMTU(cfg.IfMTU), - } + options := []core.SetupOption{} for _, addr := range cfg.Listen { options = append(options, core.ListenAddress(addr)) } @@ -325,7 +322,9 @@ func run(args yggArgs, ctx context.Context, done chan struct{}) { if n.admin, err = admin.New(n.core, logger, options...); err != nil { panic(err) } - n.admin.SetupAdminHandlers() + if n.admin != nil { + n.admin.SetupAdminHandlers() + } } // Setup the multicast module. @@ -342,7 +341,7 @@ func run(args yggArgs, ctx context.Context, done chan struct{}) { if n.multicast, err = multicast.New(n.core, logger, options...); err != nil { panic(err) } - if n.admin != nil { + if n.admin != nil && n.multicast != nil { n.multicast.SetupAdminHandlers(n.admin) } } @@ -356,7 +355,7 @@ func run(args yggArgs, ctx context.Context, done chan struct{}) { if n.tuntap, err = tuntap.New(ipv6rwc.NewReadWriteCloser(n.core), logger, options...); err != nil { panic(err) } - if n.admin != nil { + if n.admin != nil && n.tuntap != nil { n.tuntap.SetupAdminHandlers(n.admin) } } diff --git a/contrib/mobile/mobile.go b/contrib/mobile/mobile.go index 1e6d3780..0cf87180 100644 --- a/contrib/mobile/mobile.go +++ b/contrib/mobile/mobile.go @@ -55,10 +55,7 @@ func (m *Yggdrasil) StartJSON(configjson []byte) error { if err != nil { panic(err) } - options := []core.SetupOption{ - core.IfName("none"), - core.IfMTU(m.config.IfMTU), - } + options := []core.SetupOption{} for _, peer := range m.config.Peers { options = append(options, core.Peer{URI: peer}) } diff --git a/src/admin/admin.go b/src/admin/admin.go index d3e95dfa..4e98c891 100644 --- a/src/admin/admin.go +++ b/src/admin/admin.go @@ -175,6 +175,9 @@ func (a *AdminSocket) IsStarted() bool { // Stop will stop the admin API and close the socket. func (a *AdminSocket) Stop() error { + if a == nil { + return nil + } if a.listener != nil { select { case <-a.done: @@ -321,6 +324,8 @@ type DataUnit uint64 func (d DataUnit) String() string { switch { + case d > 1024*1024*1024*1024: + return fmt.Sprintf("%2.ftb", float64(d)/1024/1024/1024/1024) case d > 1024*1024*1024: return fmt.Sprintf("%2.fgb", float64(d)/1024/1024/1024) case d > 1024*1024: diff --git a/src/core/api.go b/src/core/api.go index 9f56983b..657b5510 100644 --- a/src/core/api.go +++ b/src/core/api.go @@ -2,6 +2,7 @@ package core import ( "crypto/ed25519" + "fmt" "sync/atomic" "time" @@ -15,6 +16,7 @@ import ( //"sort" //"time" + "github.com/Arceliar/phony" "github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/util" //"github.com/yggdrasil-network/yggdrasil-go/src/crypto" @@ -68,11 +70,11 @@ func (c *Core) GetSelf() SelfInfo { func (c *Core) GetPeers() []PeerInfo { var peers []PeerInfo names := make(map[net.Conn]string) - c.links.mutex.Lock() - for _, info := range c.links.links { - names[info.conn] = info.lname - } - c.links.mutex.Unlock() + phony.Block(&c.links, func() { + for _, info := range c.links._links { + names[info.conn] = info.lname + } + }) ps := c.PacketConn.PacketConn.Debug.GetPeers() for _, p := range ps { var info PeerInfo @@ -136,8 +138,17 @@ func (c *Core) GetSessions() []SessionInfo { // Listen starts a new listener (either TCP or TLS). The input should be a url.URL // parsed from a string of the form e.g. "tcp://a.b.c.d:e". In the case of a // link-local address, the interface should be provided as the second argument. -func (c *Core) Listen(u *url.URL, sintf string) (*TcpListener, error) { - return c.links.tcp.listenURL(u, sintf) +func (c *Core) Listen(u *url.URL, sintf string) (*Listener, error) { + switch u.Scheme { + case "tcp": + return c.links.tcp.listen(u, sintf) + case "tls": + return c.links.tls.listen(u, sintf) + case "unix": + return c.links.unix.listen(u, sintf) + default: + return nil, fmt.Errorf("unrecognised scheme %q", u.Scheme) + } } // Address gets the IPv6 address of the Yggdrasil node. This is always a /128 diff --git a/src/core/core.go b/src/core/core.go index 6b9fe547..4cc08ad6 100644 --- a/src/core/core.go +++ b/src/core/core.go @@ -40,8 +40,6 @@ type Core struct { _listeners map[ListenAddress]struct{} // configurable after startup nodeinfo NodeInfo // immutable after startup nodeinfoPrivacy NodeInfoPrivacy // immutable after startup - ifname IfName // immutable after startup - ifmtu IfMTU // immutable after startup _allowedPublicKeys map[[32]byte]struct{} // configurable after startup } } @@ -50,6 +48,12 @@ func New(secret ed25519.PrivateKey, logger util.Logger, opts ...SetupOption) (*C c := &Core{ log: logger, } + if name := version.BuildName(); name != "unknown" { + c.log.Infoln("Build name:", name) + } + if version := version.BuildVersion(); version != "unknown" { + c.log.Infoln("Build version:", version) + } c.ctx, c.cancel = context.WithCancel(context.Background()) // Take a copy of the private key so that it is in our own memory space. if len(secret) != ed25519.PrivateKeySize { @@ -78,15 +82,17 @@ func New(secret ed25519.PrivateKey, logger util.Logger, opts ...SetupOption) (*C if err := c.proto.nodeinfo.setNodeInfo(c.config.nodeinfo, bool(c.config.nodeinfoPrivacy)); err != nil { return nil, fmt.Errorf("error setting node info: %w", err) } - c.addPeerTimer = time.AfterFunc(time.Minute, func() { - c.Act(nil, c._addPeerLoop) - }) - if name := version.BuildName(); name != "unknown" { - c.log.Infoln("Build name:", name) - } - if version := version.BuildVersion(); version != "unknown" { - c.log.Infoln("Build version:", version) + for listenaddr := range c.config._listeners { + u, err := url.Parse(string(listenaddr)) + if err != nil { + c.log.Errorf("Invalid listener URI %q specified, ignoring\n", listenaddr) + continue + } + if _, err = c.links.listen(u, ""); err != nil { + c.log.Errorf("Failed to start listener %q: %s\n", listenaddr, err) + } } + c.Act(nil, c._addPeerLoop) return c, nil } @@ -94,10 +100,11 @@ func New(secret ed25519.PrivateKey, logger util.Logger, opts ...SetupOption) (*C // configure them. The loop ensures that disconnected peers will eventually // be reconnected with. func (c *Core) _addPeerLoop() { - if c.addPeerTimer == nil { + select { + case <-c.ctx.Done(): return + default: } - // Add peers from the Peers section for peer := range c.config._peers { go func(peer string, intf string) { @@ -128,12 +135,12 @@ func (c *Core) Stop() { // This function is unsafe and should only be ran by the core actor. func (c *Core) _close() error { c.cancel() + _ = c.links.shutdown() err := c.PacketConn.Close() if c.addPeerTimer != nil { c.addPeerTimer.Stop() c.addPeerTimer = nil } - _ = c.links.stop() return err } diff --git a/src/core/core_test.go b/src/core/core_test.go index ed5b4255..8d57f336 100644 --- a/src/core/core_test.go +++ b/src/core/core_test.go @@ -37,10 +37,10 @@ func CreateAndConnectTwo(t testing.TB, verbose bool) (nodeA *Core, nodeB *Core) t.Fatal(err) } logger := GetLoggerWithPrefix("", false) - if nodeA, err = New(skA, logger, ListenAddress("tcp://127.0.0.1:0"), IfName("none")); err != nil { + if nodeA, err = New(skA, logger, ListenAddress("tcp://127.0.0.1:0")); err != nil { t.Fatal(err) } - if nodeB, err = New(skB, logger, ListenAddress("tcp://127.0.0.1:0"), IfName("none")); err != nil { + if nodeB, err = New(skB, logger, ListenAddress("tcp://127.0.0.1:0")); err != nil { t.Fatal(err) } diff --git a/src/core/link.go b/src/core/link.go index 099a8af0..08e8dfc2 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -2,7 +2,6 @@ package core import ( "bytes" - "crypto/ed25519" "encoding/hex" "errors" "fmt" @@ -10,7 +9,6 @@ import ( "net" "net/url" "strings" - "sync" //"sync/atomic" "time" @@ -20,22 +18,22 @@ import ( "github.com/Arceliar/phony" "github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/util" - "golang.org/x/net/proxy" //"github.com/Arceliar/phony" // TODO? use instead of mutexes ) type links struct { - core *Core - mutex sync.RWMutex // protects links below - links map[linkInfo]*link - tcp tcp // TCP interface support - stopped chan struct{} + phony.Inbox + core *Core + tcp *linkTCP // TCP interface support + tls *linkTLS // TLS interface support + unix *linkUNIX // UNIX interface support + socks *linkSOCKS // SOCKS interface support + _links map[linkInfo]*link // *link is nil if connection in progress // TODO timeout (to remove from switch), read from config.ReadTimeout } // linkInfo is used as a map key type linkInfo struct { - key keyArray linkType string // Type of link, e.g. TCP, AWDL local string // Local name or address remote string // Remote name or address @@ -49,19 +47,30 @@ type link struct { info linkInfo incoming bool force bool - closed chan struct{} } type linkOptions struct { pinnedEd25519Keys map[keyArray]struct{} } +type Listener struct { + net.Listener + closed chan struct{} +} + +func (l *Listener) Close() error { + err := l.Listener.Close() + <-l.closed + return err +} + func (l *links) init(c *Core) error { l.core = c - l.mutex.Lock() - l.links = make(map[linkInfo]*link) - l.mutex.Unlock() - l.stopped = make(chan struct{}) + l.tcp = l.newLinkTCP() + l.tls = l.newLinkTLS(l.tcp) + l.unix = l.newLinkUNIX() + l.socks = l.newLinkSOCKS() + l._links = make(map[linkInfo]*link) var listeners []ListenAddress phony.Block(c, func() { @@ -70,96 +79,160 @@ func (l *links) init(c *Core) error { listeners = append(listeners, listener) } }) - if err := l.tcp.init(l, listeners); err != nil { - c.log.Errorln("Failed to start TCP interface") - return err - } return nil } +func (l *links) shutdown() error { + phony.Block(l.tcp, func() { + for l := range l.tcp._listeners { + l.Close() + } + }) + phony.Block(l.tls, func() { + for l := range l.tls._listeners { + l.Close() + } + }) + phony.Block(l.unix, func() { + for l := range l.unix._listeners { + l.Close() + } + }) + return nil +} + +func (l *links) isConnectedTo(info linkInfo) bool { + var isConnected bool + phony.Block(l, func() { + _, isConnected = l._links[info] + }) + return isConnected +} + func (l *links) call(u *url.URL, sintf string) error { - tcpOpts := tcpOptions{} - if pubkeys, ok := u.Query()["key"]; ok && len(pubkeys) > 0 { - tcpOpts.pinnedEd25519Keys = make(map[keyArray]struct{}) - for _, pubkey := range pubkeys { - if sigPub, err := hex.DecodeString(pubkey); err == nil { - var sigPubKey keyArray - copy(sigPubKey[:], sigPub) - tcpOpts.pinnedEd25519Keys[sigPubKey] = struct{}{} - } + info := linkInfoFor(u.Scheme, sintf, u.Host) + if l.isConnectedTo(info) { + return fmt.Errorf("already connected to this node") + } + options := linkOptions{ + pinnedEd25519Keys: map[keyArray]struct{}{}, + } + for _, pubkey := range u.Query()["key"] { + if sigPub, err := hex.DecodeString(pubkey); err == nil { + var sigPubKey keyArray + copy(sigPubKey[:], sigPub) + options.pinnedEd25519Keys[sigPubKey] = struct{}{} } } - switch u.Scheme { + switch info.linkType { case "tcp": - l.tcp.call(u.Host, tcpOpts, sintf) + go func() { + if err := l.tcp.dial(u, options, sintf); err != nil { + l.core.log.Warnf("Failed to dial TCP %s: %s\n", u.Host, err) + } + }() + case "socks": - tcpOpts.socksProxyAddr = u.Host - if u.User != nil { - tcpOpts.socksProxyAuth = &proxy.Auth{} - tcpOpts.socksProxyAuth.User = u.User.Username() - tcpOpts.socksProxyAuth.Password, _ = u.User.Password() - } - tcpOpts.upgrade = l.tcp.tls.forDialer // TODO make this configurable - pathtokens := strings.Split(strings.Trim(u.Path, "/"), "/") - l.tcp.call(pathtokens[0], tcpOpts, sintf) + go func() { + if err := l.socks.dial(u, options); err != nil { + l.core.log.Warnf("Failed to dial SOCKS %s: %s\n", u.Host, err) + } + }() + case "tls": - tcpOpts.upgrade = l.tcp.tls.forDialer // SNI headers must contain hostnames and not IP addresses, so we must make sure // that we do not populate the SNI with an IP literal. We do this by splitting // the host-port combo from the query option and then seeing if it parses to an // IP address successfully or not. + var tlsSNI string if sni := u.Query().Get("sni"); sni != "" { if net.ParseIP(sni) == nil { - tcpOpts.tlsSNI = sni + tlsSNI = sni } } // If the SNI is not configured still because the above failed then we'll try // again but this time we'll use the host part of the peering URI instead. - if tcpOpts.tlsSNI == "" { + if tlsSNI == "" { if host, _, err := net.SplitHostPort(u.Host); err == nil && net.ParseIP(host) == nil { - tcpOpts.tlsSNI = host + tlsSNI = host } } - l.tcp.call(u.Host, tcpOpts, sintf) + go func() { + if err := l.tls.dial(u, options, sintf, tlsSNI); err != nil { + l.core.log.Warnf("Failed to dial TLS %s: %s\n", u.Host, err) + } + }() + + case "unix": + go func() { + if err := l.unix.dial(u, options, sintf); err != nil { + l.core.log.Warnf("Failed to dial UNIX %s: %s\n", u.Host, err) + } + }() + default: return errors.New("unknown call scheme: " + u.Scheme) } return nil } -func (l *links) create(conn net.Conn, name, linkType, local, remote string, incoming, force bool, options linkOptions) (*link, error) { - // Technically anything unique would work for names, but let's pick something human readable, just for debugging +func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { + var listener *Listener + var err error + switch u.Scheme { + case "tcp": + listener, err = l.tcp.listen(u, sintf) + case "tls": + listener, err = l.tls.listen(u, sintf) + case "unix": + listener, err = l.unix.listen(u, sintf) + default: + return nil, fmt.Errorf("unrecognised scheme %q", u.Scheme) + } + return listener, err +} + +func (l *links) create(conn net.Conn, name string, info linkInfo, incoming, force bool, options linkOptions) error { intf := link{ conn: &linkConn{ Conn: conn, up: time.Now(), }, - lname: name, - links: l, - options: options, - info: linkInfo{ - linkType: linkType, - local: local, - remote: remote, - }, + lname: name, + links: l, + options: options, + info: info, incoming: incoming, force: force, } - return &intf, nil -} - -func (l *links) stop() error { - close(l.stopped) - if err := l.tcp.stop(); err != nil { - return err - } + go func() { + if err := intf.handler(); err != nil { + l.core.log.Errorf("Link handler %s error (%s): %s", name, conn.RemoteAddr(), err) + } + }() return nil } -func (intf *link) handler() (chan struct{}, error) { - // TODO split some of this into shorter functions, so it's easier to read, and for the FIXME duplicate peer issue mentioned later +func (intf *link) handler() error { defer intf.conn.Close() + + // Don't connect to this link more than once. + if intf.links.isConnectedTo(intf.info) { + return fmt.Errorf("already connected to this node") + } + + // Mark the connection as in progress. + phony.Block(intf.links, func() { + intf.links._links[intf.info] = nil + }) + + // When we're done, clean up the connection entry. + defer phony.Block(intf.links, func() { + delete(intf.links._links, intf.info) + }) + + // TODO split some of this into shorter functions, so it's easier to read, and for the FIXME duplicate peer issue mentioned later meta := version_getBaseMetadata() meta.key = intf.links.core.public metaBytes := meta.encode() @@ -172,10 +245,10 @@ func (intf *link) handler() (chan struct{}, error) { err = errors.New("incomplete metadata send") } }) { - return nil, errors.New("timeout on metadata send") + return errors.New("timeout on metadata send") } if err != nil { - return nil, err + return fmt.Errorf("write handshake: %w", err) } if !util.FuncTimeout(30*time.Second, func() { var n int @@ -184,15 +257,15 @@ func (intf *link) handler() (chan struct{}, error) { err = errors.New("incomplete metadata recv") } }) { - return nil, errors.New("timeout on metadata recv") + return errors.New("timeout on metadata recv") } if err != nil { - return nil, err + return fmt.Errorf("read handshake: %w", err) } meta = version_metadata{} base := version_getBaseMetadata() if !meta.decode(metaBytes) { - return nil, errors.New("failed to decode metadata") + return errors.New("failed to decode metadata") } if !meta.check() { var connectError string @@ -207,16 +280,16 @@ func (intf *link) handler() (chan struct{}, error) { fmt.Sprintf("%d.%d", base.ver, base.minorVer), fmt.Sprintf("%d.%d", meta.ver, meta.minorVer), ) - return nil, errors.New("remote node is incompatible version") + return errors.New("remote node is incompatible version") } // Check if the remote side matches the keys we expected. This is a bit of a weak // check - in future versions we really should check a signature or something like that. - if pinned := intf.options.pinnedEd25519Keys; pinned != nil { + if pinned := intf.options.pinnedEd25519Keys; len(pinned) > 0 { var key keyArray copy(key[:], meta.key) if _, allowed := pinned[key]; !allowed { intf.links.core.log.Errorf("Failed to connect to node: %q sent ed25519 key that does not match pinned keys", intf.name()) - return nil, fmt.Errorf("failed to connect: host sent ed25519 key that does not match pinned keys") + return fmt.Errorf("failed to connect: host sent ed25519 key that does not match pinned keys") } } // Check if we're authorized to connect to this key / IP @@ -232,55 +305,50 @@ func (intf *link) handler() (chan struct{}, error) { intf.links.core.log.Warnf("%s connection from %s forbidden: AllowedEncryptionPublicKeys does not contain key %s", strings.ToUpper(intf.info.linkType), intf.info.remote, hex.EncodeToString(meta.key)) intf.close() - return nil, nil + return fmt.Errorf("forbidden connection") } - // Check if we already have a link to this node - copy(intf.info.key[:], meta.key) - intf.links.mutex.Lock() - if oldIntf, isIn := intf.links.links[intf.info]; isIn { - intf.links.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.links.core.log.Debugln("DEBUG: found existing interface for", intf.name()) - return oldIntf.closed, nil - } else { - intf.closed = make(chan struct{}) - intf.links.links[intf.info] = intf - defer func() { - intf.links.mutex.Lock() - delete(intf.links.links, intf.info) - intf.links.mutex.Unlock() - close(intf.closed) - }() - intf.links.core.log.Debugln("DEBUG: registered interface for", intf.name()) - } - intf.links.mutex.Unlock() - themAddr := address.AddrForKey(ed25519.PublicKey(intf.info.key[:])) - themAddrString := net.IP(themAddr[:]).String() - themString := fmt.Sprintf("%s@%s", themAddrString, intf.info.remote) + + phony.Block(intf.links, func() { + intf.links._links[intf.info] = intf + }) + + remoteAddr := net.IP(address.AddrForKey(meta.key)[:]).String() + remoteStr := fmt.Sprintf("%s@%s", remoteAddr, intf.info.remote) + localStr := intf.conn.LocalAddr() intf.links.core.log.Infof("Connected %s: %s, source %s", - strings.ToUpper(intf.info.linkType), themString, intf.info.local) - // Run the handler - err = intf.links.core.HandleConn(ed25519.PublicKey(intf.info.key[:]), intf.conn) + strings.ToUpper(intf.info.linkType), remoteStr, localStr) + // TODO don't report an error if it's just a 'use of closed network connection' - if err != nil { + if err = intf.links.core.HandleConn(meta.key, intf.conn); err != nil && err != io.EOF { intf.links.core.log.Infof("Disconnected %s: %s, source %s; error: %s", - strings.ToUpper(intf.info.linkType), themString, intf.info.local, err) + strings.ToUpper(intf.info.linkType), remoteStr, localStr, err) } else { intf.links.core.log.Infof("Disconnected %s: %s, source %s", - strings.ToUpper(intf.info.linkType), themString, intf.info.local) + strings.ToUpper(intf.info.linkType), remoteStr, localStr) } - return nil, err + + return nil } -func (intf *link) close() { - intf.conn.Close() +func (intf *link) close() error { + return intf.conn.Close() } func (intf *link) name() string { return intf.lname } +func linkInfoFor(linkType, sintf, remote string) linkInfo { + if h, _, err := net.SplitHostPort(remote); err == nil { + remote = h + } + return linkInfo{ + linkType: linkType, + local: sintf, + remote: remote, + } +} + type linkConn struct { // tx and rx are at the beginning of the struct to ensure 64-bit alignment // on 32-bit platforms, see https://pkg.go.dev/sync/atomic#pkg-note-BUG diff --git a/src/core/link_socks.go b/src/core/link_socks.go new file mode 100644 index 00000000..ad5b8c98 --- /dev/null +++ b/src/core/link_socks.go @@ -0,0 +1,52 @@ +package core + +import ( + "fmt" + "net" + "net/url" + "strings" + + "golang.org/x/net/proxy" +) + +type linkSOCKS struct { + *links +} + +func (l *links) newLinkSOCKS() *linkSOCKS { + lt := &linkSOCKS{ + links: l, + } + return lt +} + +func (l *linkSOCKS) dial(url *url.URL, options linkOptions) error { + info := linkInfoFor("socks", "", url.Path) + if l.links.isConnectedTo(info) { + return fmt.Errorf("duplicate connection attempt") + } + proxyAuth := &proxy.Auth{} + proxyAuth.User = url.User.Username() + proxyAuth.Password, _ = url.User.Password() + dialer, err := proxy.SOCKS5("tcp", url.Host, proxyAuth, proxy.Direct) + if err != nil { + return fmt.Errorf("failed to configure proxy") + } + pathtokens := strings.Split(strings.Trim(url.Path, "/"), "/") + conn, err := dialer.Dial("tcp", pathtokens[0]) + if err != nil { + return err + } + return l.handler(url.String(), info, conn, options, false) +} + +func (l *linkSOCKS) handler(name string, info linkInfo, conn net.Conn, options linkOptions, incoming bool) error { + return l.links.create( + conn, // connection + name, // connection name + info, // connection info + incoming, // not incoming + false, // not forced + options, // connection options + ) +} diff --git a/src/core/link_tcp.go b/src/core/link_tcp.go new file mode 100644 index 00000000..fcac8b4d --- /dev/null +++ b/src/core/link_tcp.go @@ -0,0 +1,183 @@ +package core + +import ( + "context" + "fmt" + "net" + "net/url" + "strings" + "time" + + "github.com/Arceliar/phony" +) + +type linkTCP struct { + phony.Inbox + *links + listener *net.ListenConfig + _listeners map[*Listener]context.CancelFunc +} + +func (l *links) newLinkTCP() *linkTCP { + lt := &linkTCP{ + links: l, + listener: &net.ListenConfig{ + KeepAlive: -1, + }, + _listeners: map[*Listener]context.CancelFunc{}, + } + lt.listener.Control = lt.tcpContext + return lt +} + +func (l *linkTCP) dial(url *url.URL, options linkOptions, sintf string) error { + info := linkInfoFor("tcp", sintf, strings.SplitN(url.Host, "%", 2)[0]) + if l.links.isConnectedTo(info) { + return fmt.Errorf("duplicate connection attempt") + } + addr, err := net.ResolveTCPAddr("tcp", url.Host) + if err != nil { + return err + } + addr.Zone = sintf + dialer, err := l.dialerFor(addr.String(), sintf) + if err != nil { + return err + } + conn, err := dialer.DialContext(l.core.ctx, "tcp", addr.String()) + if err != nil { + return err + } + return l.handler(url.String(), info, conn, options, false) +} + +func (l *linkTCP) listen(url *url.URL, sintf string) (*Listener, error) { + ctx, cancel := context.WithCancel(l.core.ctx) + hostport := url.Host + if sintf != "" { + if host, port, err := net.SplitHostPort(hostport); err == nil { + hostport = fmt.Sprintf("[%s%%%s]:%s", host, sintf, port) + } + } + listener, err := l.listener.Listen(ctx, "tcp", hostport) + if err != nil { + cancel() + return nil, err + } + entry := &Listener{ + Listener: listener, + closed: make(chan struct{}), + } + phony.Block(l, func() { + l._listeners[entry] = cancel + }) + l.core.log.Printf("TCP listener started on %s", listener.Addr()) + go func() { + defer phony.Block(l, func() { + delete(l._listeners, entry) + }) + for { + conn, err := listener.Accept() + if err != nil { + cancel() + break + } + addr := conn.RemoteAddr().(*net.TCPAddr) + name := fmt.Sprintf("tls://%s", addr) + info := linkInfoFor("tcp", sintf, strings.SplitN(addr.IP.String(), "%", 2)[0]) + if err = l.handler(name, info, conn, linkOptions{}, true); err != nil { + l.core.log.Errorln("Failed to create inbound link:", err) + } + } + listener.Close() + close(entry.closed) + l.core.log.Printf("TCP listener stopped on %s", listener.Addr()) + }() + return entry, nil +} + +func (l *linkTCP) handler(name string, info linkInfo, conn net.Conn, options linkOptions, incoming bool) error { + return l.links.create( + conn, // connection + name, // connection name + info, // connection info + incoming, // not incoming + false, // not forced + options, // connection options + ) +} + +// Returns the address of the listener. +func (l *linkTCP) getAddr() *net.TCPAddr { + // TODO: Fix this, because this will currently only give a single address + // to multicast.go, which obviously is not great, but right now multicast.go + // doesn't have the ability to send more than one address in a packet either + var addr *net.TCPAddr + phony.Block(l, func() { + for listener := range l._listeners { + addr = listener.Addr().(*net.TCPAddr) + } + }) + return addr +} + +func (l *linkTCP) dialerFor(saddr, sintf string) (*net.Dialer, error) { + dst, err := net.ResolveTCPAddr("tcp", saddr) + if err != nil { + return nil, err + } + if dst.IP.IsLinkLocalUnicast() { + dst.Zone = sintf + if dst.Zone == "" { + return nil, fmt.Errorf("link-local address requires a zone") + } + } + dialer := &net.Dialer{ + Timeout: time.Second * 5, + KeepAlive: -1, + Control: l.tcpContext, + } + if sintf != "" { + dialer.Control = l.getControl(sintf) + ief, err := net.InterfaceByName(sintf) + if err != nil { + return nil, fmt.Errorf("interface %q not found", sintf) + } + if ief.Flags&net.FlagUp == 0 { + return nil, fmt.Errorf("interface %q is not up", sintf) + } + addrs, err := ief.Addrs() + if err != nil { + return nil, fmt.Errorf("interface %q addresses not available: %w", sintf, err) + } + for addrindex, addr := range addrs { + src, _, err := net.ParseCIDR(addr.String()) + if err != nil { + 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 { + return nil, fmt.Errorf("no suitable source address found on interface %q", sintf) + } + } + return dialer, nil +} diff --git a/src/core/tcp_darwin.go b/src/core/link_tcp_darwin.go similarity index 74% rename from src/core/tcp_darwin.go rename to src/core/link_tcp_darwin.go index 2ea3abc8..daa51df0 100644 --- a/src/core/tcp_darwin.go +++ b/src/core/link_tcp_darwin.go @@ -11,7 +11,7 @@ import ( // WARNING: This context is used both by net.Dialer and net.Listen in tcp.go -func (t *tcp) tcpContext(network, address string, c syscall.RawConn) error { +func (t *linkTCP) tcpContext(network, address string, c syscall.RawConn) error { var control error var recvanyif error @@ -28,6 +28,6 @@ func (t *tcp) tcpContext(network, address string, c syscall.RawConn) error { } } -func (t *tcp) getControl(sintf string) func(string, string, syscall.RawConn) error { +func (t *linkTCP) getControl(sintf string) func(string, string, syscall.RawConn) error { return t.tcpContext } diff --git a/src/core/tcp_linux.go b/src/core/link_tcp_linux.go similarity index 86% rename from src/core/tcp_linux.go rename to src/core/link_tcp_linux.go index e59c3121..9e875fee 100644 --- a/src/core/tcp_linux.go +++ b/src/core/link_tcp_linux.go @@ -11,7 +11,7 @@ import ( // WARNING: This context is used both by net.Dialer and net.Listen in tcp.go -func (t *tcp) tcpContext(network, address string, c syscall.RawConn) error { +func (t *linkTCP) tcpContext(network, address string, c syscall.RawConn) error { var control error var bbr error @@ -31,7 +31,7 @@ func (t *tcp) tcpContext(network, address string, c syscall.RawConn) error { return nil } -func (t *tcp) getControl(sintf string) func(string, string, syscall.RawConn) error { +func (t *linkTCP) getControl(sintf string) func(string, string, syscall.RawConn) error { return func(network, address string, c syscall.RawConn) error { var err error btd := func(fd uintptr) { diff --git a/src/core/tcp_other.go b/src/core/link_tcp_other.go similarity index 55% rename from src/core/tcp_other.go rename to src/core/link_tcp_other.go index 8dd76f28..f8a5aece 100644 --- a/src/core/tcp_other.go +++ b/src/core/link_tcp_other.go @@ -9,10 +9,10 @@ import ( // WARNING: This context is used both by net.Dialer and net.Listen in tcp.go -func (t *tcp) tcpContext(network, address string, c syscall.RawConn) error { +func (t *linkTCP) tcpContext(network, address string, c syscall.RawConn) error { return nil } -func (t *tcp) getControl(sintf string) func(string, string, syscall.RawConn) error { +func (t *linkTCP) getControl(sintf string) func(string, string, syscall.RawConn) error { return t.tcpContext } diff --git a/src/core/link_tls.go b/src/core/link_tls.go new file mode 100644 index 00000000..e6ffd5a8 --- /dev/null +++ b/src/core/link_tls.go @@ -0,0 +1,171 @@ +package core + +import ( + "bytes" + "context" + "crypto/rand" + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" + "encoding/hex" + "encoding/pem" + "fmt" + "math/big" + "net" + "net/url" + "strings" + "time" + + "github.com/Arceliar/phony" +) + +type linkTLS struct { + phony.Inbox + *links + tcp *linkTCP + listener *net.ListenConfig + config *tls.Config + _listeners map[*Listener]context.CancelFunc +} + +func (l *links) newLinkTLS(tcp *linkTCP) *linkTLS { + lt := &linkTLS{ + links: l, + tcp: tcp, + listener: &net.ListenConfig{ + Control: tcp.tcpContext, + KeepAlive: -1, + }, + _listeners: map[*Listener]context.CancelFunc{}, + } + var err error + lt.config, err = lt.generateConfig() + if err != nil { + panic(err) + } + return lt +} + +func (l *linkTLS) dial(url *url.URL, options linkOptions, sintf, sni string) error { + info := linkInfoFor("tls", sintf, strings.SplitN(url.Host, "%", 2)[0]) + if l.links.isConnectedTo(info) { + return fmt.Errorf("duplicate connection attempt") + } + addr, err := net.ResolveTCPAddr("tcp", url.Host) + if err != nil { + return err + } + addr.Zone = sintf + dialer, err := l.tcp.dialerFor(addr.String(), sintf) + if err != nil { + return err + } + tlsconfig := l.config.Clone() + tlsconfig.ServerName = sni + tlsdialer := &tls.Dialer{ + NetDialer: dialer, + Config: tlsconfig, + } + conn, err := tlsdialer.DialContext(l.core.ctx, "tcp", addr.String()) + if err != nil { + return err + } + return l.handler(url.String(), info, conn, options, false) +} + +func (l *linkTLS) listen(url *url.URL, sintf string) (*Listener, error) { + ctx, cancel := context.WithCancel(l.core.ctx) + hostport := url.Host + if sintf != "" { + if host, port, err := net.SplitHostPort(hostport); err == nil { + hostport = fmt.Sprintf("[%s%%%s]:%s", host, sintf, port) + } + } + listener, err := l.listener.Listen(ctx, "tcp", hostport) + if err != nil { + cancel() + return nil, err + } + tlslistener := tls.NewListener(listener, l.config) + entry := &Listener{ + Listener: tlslistener, + closed: make(chan struct{}), + } + phony.Block(l, func() { + l._listeners[entry] = cancel + }) + l.core.log.Printf("TLS listener started on %s", listener.Addr()) + go func() { + defer phony.Block(l, func() { + delete(l._listeners, entry) + }) + for { + conn, err := tlslistener.Accept() + if err != nil { + cancel() + break + } + addr := conn.RemoteAddr().(*net.TCPAddr) + name := fmt.Sprintf("tls://%s", addr) + info := linkInfoFor("tls", sintf, strings.SplitN(addr.IP.String(), "%", 2)[0]) + if err = l.handler(name, info, conn, linkOptions{}, true); err != nil { + l.core.log.Errorln("Failed to create inbound link:", err) + } + } + tlslistener.Close() + close(entry.closed) + l.core.log.Printf("TLS listener stopped on %s", listener.Addr()) + }() + return entry, nil +} + +func (l *linkTLS) generateConfig() (*tls.Config, error) { + certBuf := &bytes.Buffer{} + + // TODO: because NotAfter is finite, we should add some mechanism to + // regenerate the certificate and restart the listeners periodically + // for nodes with very high uptimes. Perhaps regenerate certs and restart + // listeners every few months or so. + cert := x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + CommonName: hex.EncodeToString(l.links.core.public[:]), + }, + NotBefore: time.Now(), + NotAfter: time.Now().Add(time.Hour * 24 * 365), + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + } + + certbytes, err := x509.CreateCertificate(rand.Reader, &cert, &cert, l.links.core.public, l.links.core.secret) + if err != nil { + return nil, err + } + + if err := pem.Encode(certBuf, &pem.Block{ + Type: "CERTIFICATE", + Bytes: certbytes, + }); err != nil { + return nil, err + } + + rootCAs := x509.NewCertPool() + rootCAs.AppendCertsFromPEM(certbytes) + + return &tls.Config{ + RootCAs: rootCAs, + Certificates: []tls.Certificate{ + { + Certificate: [][]byte{certbytes}, + PrivateKey: l.links.core.secret, + }, + }, + InsecureSkipVerify: true, + MinVersion: tls.VersionTLS13, + }, nil +} + +func (l *linkTLS) handler(name string, info linkInfo, conn net.Conn, options linkOptions, incoming bool) error { + return l.tcp.handler(name, info, conn, options, incoming) +} diff --git a/src/core/link_unix.go b/src/core/link_unix.go new file mode 100644 index 00000000..d63c1d90 --- /dev/null +++ b/src/core/link_unix.go @@ -0,0 +1,98 @@ +package core + +import ( + "context" + "fmt" + "net" + "net/url" + "time" + + "github.com/Arceliar/phony" +) + +type linkUNIX struct { + phony.Inbox + *links + dialer *net.Dialer + listener *net.ListenConfig + _listeners map[*Listener]context.CancelFunc +} + +func (l *links) newLinkUNIX() *linkUNIX { + lt := &linkUNIX{ + links: l, + dialer: &net.Dialer{ + Timeout: time.Second * 5, + KeepAlive: -1, + }, + listener: &net.ListenConfig{ + KeepAlive: -1, + }, + _listeners: map[*Listener]context.CancelFunc{}, + } + return lt +} + +func (l *linkUNIX) dial(url *url.URL, options linkOptions, _ string) error { + info := linkInfoFor("unix", "", url.Path) + if l.links.isConnectedTo(info) { + return fmt.Errorf("duplicate connection attempt") + } + addr, err := net.ResolveUnixAddr("unix", url.Path) + if err != nil { + return err + } + conn, err := l.dialer.DialContext(l.core.ctx, "unix", addr.String()) + if err != nil { + return err + } + return l.handler(url.String(), info, conn, options, false) +} + +func (l *linkUNIX) listen(url *url.URL, _ string) (*Listener, error) { + ctx, cancel := context.WithCancel(l.core.ctx) + listener, err := l.listener.Listen(ctx, "unix", url.Path) + if err != nil { + cancel() + return nil, err + } + entry := &Listener{ + Listener: listener, + closed: make(chan struct{}), + } + phony.Block(l, func() { + l._listeners[entry] = cancel + }) + l.core.log.Printf("UNIX listener started on %s", listener.Addr()) + go func() { + defer phony.Block(l, func() { + delete(l._listeners, entry) + }) + for { + conn, err := listener.Accept() + if err != nil { + cancel() + break + } + info := linkInfoFor("unix", "", url.String()) + if err = l.handler(url.String(), info, conn, linkOptions{}, true); err != nil { + l.core.log.Errorln("Failed to create inbound link:", err) + } + } + listener.Close() + close(entry.closed) + l.core.log.Printf("UNIX listener stopped on %s", listener.Addr()) + }() + return entry, nil +} + +func (l *linkUNIX) handler(name string, info linkInfo, conn net.Conn, options linkOptions, incoming bool) error { + return l.links.create( + conn, // connection + name, // connection name + info, // connection info + incoming, // not incoming + false, // not forced + options, // connection options + ) +} diff --git a/src/core/options.go b/src/core/options.go index b3d06c63..8009aa3b 100644 --- a/src/core/options.go +++ b/src/core/options.go @@ -14,10 +14,6 @@ func (c *Core) _applyOption(opt SetupOption) { c.config.nodeinfo = v case NodeInfoPrivacy: c.config.nodeinfoPrivacy = v - case IfName: - c.config.ifname = v - case IfMTU: - c.config.ifmtu = v case AllowedPublicKey: pk := [32]byte{} copy(pk[:], v) @@ -36,14 +32,10 @@ type Peer struct { } type NodeInfo map[string]interface{} type NodeInfoPrivacy bool -type IfName string -type IfMTU uint16 type AllowedPublicKey ed25519.PublicKey func (a ListenAddress) isSetupOption() {} func (a Peer) isSetupOption() {} func (a NodeInfo) isSetupOption() {} func (a NodeInfoPrivacy) isSetupOption() {} -func (a IfName) isSetupOption() {} -func (a IfMTU) isSetupOption() {} func (a AllowedPublicKey) isSetupOption() {} diff --git a/src/core/tcp.go b/src/core/tcp.go deleted file mode 100644 index ab952806..00000000 --- a/src/core/tcp.go +++ /dev/null @@ -1,417 +0,0 @@ -package core - -// This sends packets to peers using TCP as a transport -// It's generally better tested than the UDP implementation -// Using it regularly is insane, but I find TCP easier to test/debug with it -// Updating and optimizing the UDP version is a higher priority - -// TODO: -// Something needs to make sure we're getting *valid* packets -// Could be used to DoS (connect, give someone else's keys, spew garbage) -// I guess the "peer" part should watch for link packets, disconnect? - -// TCP connections start with a metadata exchange. -// It involves exchanging version numbers and crypto keys -// See version.go for version metadata format - -import ( - "context" - "fmt" - "math/rand" - "net" - "net/url" - "strings" - "sync" - "time" - - "golang.org/x/net/proxy" - - "github.com/yggdrasil-network/yggdrasil-go/src/address" - //"github.com/yggdrasil-network/yggdrasil-go/src/util" -) - -const default_timeout = 6 * time.Second - -// The TCP listener and information about active TCP connections, to avoid duplication. -type tcp struct { - links *links - waitgroup sync.WaitGroup - mutex sync.Mutex // Protecting the below - listeners map[string]*TcpListener - calls map[string]struct{} - conns map[linkInfo](chan struct{}) - tls tcptls -} - -// TcpListener is a stoppable TCP listener interface. These are typically -// returned from calls to the ListenTCP() function and are also used internally -// to represent listeners created by the "Listen" configuration option and for -// multicast interfaces. -type TcpListener struct { - Listener net.Listener - opts tcpOptions - stop chan struct{} -} - -type TcpUpgrade struct { - upgrade func(c net.Conn, o *tcpOptions) (net.Conn, error) - name string -} - -type tcpOptions struct { - linkOptions - upgrade *TcpUpgrade - socksProxyAddr string - socksProxyAuth *proxy.Auth - socksPeerAddr string - tlsSNI string -} - -func (l *TcpListener) Stop() { - defer func() { _ = recover() }() - close(l.stop) -} - -// Wrapper function to set additional options for specific connection types. -func (t *tcp) setExtraOptions(c net.Conn) { - switch sock := c.(type) { - case *net.TCPConn: - _ = sock.SetNoDelay(true) - // TODO something for socks5 - default: - } -} - -// Returns the address of the listener. -func (t *tcp) getAddr() *net.TCPAddr { - // TODO: Fix this, because this will currently only give a single address - // to multicast.go, which obviously is not great, but right now multicast.go - // doesn't have the ability to send more than one address in a packet either - t.mutex.Lock() - defer t.mutex.Unlock() - for _, l := range t.listeners { - return l.Listener.Addr().(*net.TCPAddr) - } - return nil -} - -// Initializes the struct. -func (t *tcp) init(l *links, listeners []ListenAddress) error { - t.links = l - t.tls.init(t) - t.mutex.Lock() - t.calls = make(map[string]struct{}) - t.conns = make(map[linkInfo](chan struct{})) - t.listeners = make(map[string]*TcpListener) - t.mutex.Unlock() - - for _, listenaddr := range listeners { - u, err := url.Parse(string(listenaddr)) - if err != nil { - t.links.core.log.Errorln("Failed to parse listener: listener", listenaddr, "is not correctly formatted, ignoring") - continue - } - if _, err := t.listenURL(u, ""); err != nil { - return err - } - } - - return nil -} - -func (t *tcp) stop() error { - t.mutex.Lock() - for _, listener := range t.listeners { - listener.Stop() - } - t.mutex.Unlock() - t.waitgroup.Wait() - return nil -} - -func (t *tcp) listenURL(u *url.URL, sintf string) (*TcpListener, error) { - var listener *TcpListener - var err error - hostport := u.Host // Used for tcp and tls - if len(sintf) != 0 { - host, port, err := net.SplitHostPort(hostport) - if err == nil { - hostport = fmt.Sprintf("[%s%%%s]:%s", host, sintf, port) - } - } - switch u.Scheme { - case "tcp": - listener, err = t.listen(hostport, nil) - case "tls": - listener, err = t.listen(hostport, t.tls.forListener) - default: - t.links.core.log.Errorln("Failed to add listener: listener", u.String(), "is not correctly formatted, ignoring") - } - return listener, err -} - -func (t *tcp) listen(listenaddr string, upgrade *TcpUpgrade) (*TcpListener, error) { - var err error - - ctx := t.links.core.ctx - lc := net.ListenConfig{ - Control: t.tcpContext, - } - listener, err := lc.Listen(ctx, "tcp", listenaddr) - if err == nil { - l := TcpListener{ - Listener: listener, - opts: tcpOptions{upgrade: upgrade}, - stop: make(chan struct{}), - } - t.waitgroup.Add(1) - go t.listener(&l, listenaddr) - return &l, nil - } - - return nil, err -} - -// Runs the listener, which spawns off goroutines for incoming connections. -func (t *tcp) listener(l *TcpListener, listenaddr string) { - defer t.waitgroup.Done() - if l == nil { - return - } - // Track the listener so that we can find it again in future - t.mutex.Lock() - if _, isIn := t.listeners[listenaddr]; isIn { - t.mutex.Unlock() - l.Listener.Close() - return - } - callproto := "TCP" - if l.opts.upgrade != nil { - callproto = strings.ToUpper(l.opts.upgrade.name) - } - t.listeners[listenaddr] = l - t.mutex.Unlock() - // And here we go! - defer func() { - t.links.core.log.Infoln("Stopping", callproto, "listener on:", l.Listener.Addr().String()) - l.Listener.Close() - t.mutex.Lock() - delete(t.listeners, listenaddr) - t.mutex.Unlock() - }() - t.links.core.log.Infoln("Listening for", callproto, "on:", l.Listener.Addr().String()) - go func() { - <-l.stop - l.Listener.Close() - }() - defer l.Stop() - for { - sock, err := l.Listener.Accept() - if err != nil { - t.links.core.log.Errorln("Failed to accept connection:", err) - select { - case <-l.stop: - return - default: - } - time.Sleep(time.Second) // So we don't busy loop - continue - } - t.waitgroup.Add(1) - options := l.opts - go t.handler(sock, true, options) - } -} - -// Checks if we already are calling this address -func (t *tcp) startCalling(saddr string) bool { - t.mutex.Lock() - defer t.mutex.Unlock() - _, isIn := t.calls[saddr] - t.calls[saddr] = struct{}{} - 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. -// When finished, it removes the outgoing call, so reconnection attempts can be made later. -// This all happens in a separate goroutine that it spawns. -func (t *tcp) call(saddr string, options tcpOptions, sintf string) { - go func() { - callname := saddr - callproto := "TCP" - if options.upgrade != nil { - callproto = strings.ToUpper(options.upgrade.name) - } - if sintf != "" { - callname = fmt.Sprintf("%s/%s/%s", callproto, saddr, sintf) - } - if !t.startCalling(callname) { - return - } - defer func() { - // Block new calls for a little while, to mitigate livelock scenarios - rand.Seed(time.Now().UnixNano()) - delay := default_timeout + time.Duration(rand.Intn(10000))*time.Millisecond - time.Sleep(delay) - t.mutex.Lock() - delete(t.calls, callname) - t.mutex.Unlock() - }() - var conn net.Conn - var err error - if options.socksProxyAddr != "" { - if sintf != "" { - return - } - dialerdst, er := net.ResolveTCPAddr("tcp", options.socksProxyAddr) - if er != nil { - return - } - var dialer proxy.Dialer - dialer, err = proxy.SOCKS5("tcp", dialerdst.String(), options.socksProxyAuth, proxy.Direct) - if err != nil { - return - } - ctx, done := context.WithTimeout(t.links.core.ctx, default_timeout) - conn, err = dialer.(proxy.ContextDialer).DialContext(ctx, "tcp", saddr) - done() - if err != nil { - return - } - t.waitgroup.Add(1) - options.socksPeerAddr = saddr - if ch := t.handler(conn, false, options); ch != nil { - <-ch - } - } else { - dst, err := net.ResolveTCPAddr("tcp", saddr) - if err != nil { - return - } - if dst.IP.IsLinkLocalUnicast() { - dst.Zone = sintf - if dst.Zone == "" { - return - } - } - dialer := net.Dialer{ - Control: t.tcpContext, - } - if sintf != "" { - dialer.Control = t.getControl(sintf) - ief, err := net.InterfaceByName(sintf) - if err != nil { - return - } - if ief.Flags&net.FlagUp == 0 { - return - } - addrs, err := ief.Addrs() - if err == nil { - for addrindex, addr := range addrs { - src, _, err := net.ParseCIDR(addr.String()) - if err != nil { - continue - } - 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 { - return - } - } - } - ctx, done := context.WithTimeout(t.links.core.ctx, default_timeout) - conn, err = dialer.DialContext(ctx, "tcp", dst.String()) - done() - if err != nil { - t.links.core.log.Debugf("Failed to dial %s: %s", callproto, err) - return - } - t.waitgroup.Add(1) - if ch := t.handler(conn, false, options); ch != nil { - <-ch - } - } - }() -} - -func (t *tcp) handler(sock net.Conn, incoming bool, options tcpOptions) chan struct{} { - defer t.waitgroup.Done() // Happens after sock.close - defer sock.Close() - t.setExtraOptions(sock) - var upgraded bool - if options.upgrade != nil { - var err error - if sock, err = options.upgrade.upgrade(sock, &options); err != nil { - t.links.core.log.Errorln("TCP handler upgrade failed:", err) - return nil - } - upgraded = true - } - var name, proto, local, remote string - if options.socksProxyAddr != "" { - name = "socks://" + sock.RemoteAddr().String() + "/" + options.socksPeerAddr - proto = "socks" - local, _, _ = net.SplitHostPort(sock.LocalAddr().String()) - remote, _, _ = net.SplitHostPort(options.socksPeerAddr) - } else { - if upgraded { - proto = options.upgrade.name - name = proto + "://" + sock.RemoteAddr().String() - } else { - proto = "tcp" - name = proto + "://" + sock.RemoteAddr().String() - } - local, _, _ = net.SplitHostPort(sock.LocalAddr().String()) - remote, _, _ = net.SplitHostPort(sock.RemoteAddr().String()) - } - localIP := net.ParseIP(local) - if localIP = localIP.To16(); localIP != nil { - var laddr address.Address - var lsubnet address.Subnet - copy(laddr[:], localIP) - copy(lsubnet[:], localIP) - if laddr.IsValid() || lsubnet.IsValid() { - // The local address is with the network address/prefix range - // This would route ygg over ygg, which we don't want - // FIXME ideally this check should happen outside of the core library - // Maybe dial/listen at the application level - // Then pass a net.Conn to the core library (after these kinds of checks are done) - t.links.core.log.Debugln("Dropping ygg-tunneled connection", local, remote) - return nil - } - } - force := net.ParseIP(strings.Split(remote, "%")[0]).IsLinkLocalUnicast() - link, err := t.links.create(sock, name, proto, local, remote, incoming, force, options.linkOptions) - if err != nil { - t.links.core.log.Println(err) - panic(err) - } - t.links.core.log.Debugln("DEBUG: starting handler for", name) - ch, err := link.handler() - t.links.core.log.Debugln("DEBUG: stopped handler for", name, err) - return ch -} diff --git a/src/core/tls.go b/src/core/tls.go deleted file mode 100644 index 9e340ac4..00000000 --- a/src/core/tls.go +++ /dev/null @@ -1,126 +0,0 @@ -package core - -import ( - "bytes" - "crypto/ed25519" - "crypto/rand" - "crypto/tls" - "crypto/x509" - "crypto/x509/pkix" - "encoding/hex" - "encoding/pem" - "errors" - "log" - "math/big" - "net" - "time" -) - -type tcptls struct { - tcp *tcp - config *tls.Config - forDialer *TcpUpgrade - forListener *TcpUpgrade -} - -func (t *tcptls) init(tcp *tcp) { - t.tcp = tcp - t.forDialer = &TcpUpgrade{ - upgrade: t.upgradeDialer, - name: "tls", - } - t.forListener = &TcpUpgrade{ - upgrade: t.upgradeListener, - name: "tls", - } - - edpriv := make(ed25519.PrivateKey, ed25519.PrivateKeySize) - copy(edpriv[:], tcp.links.core.secret[:]) - - certBuf := &bytes.Buffer{} - - // TODO: because NotAfter is finite, we should add some mechanism to regenerate the certificate and restart the listeners periodically for nodes with very high uptimes. Perhaps regenerate certs and restart listeners every few months or so. - pubtemp := x509.Certificate{ - SerialNumber: big.NewInt(1), - Subject: pkix.Name{ - CommonName: hex.EncodeToString(tcp.links.core.public[:]), - }, - NotBefore: time.Now(), - NotAfter: time.Now().Add(time.Hour * 24 * 365), - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, - BasicConstraintsValid: true, - } - - derbytes, err := x509.CreateCertificate(rand.Reader, &pubtemp, &pubtemp, edpriv.Public(), edpriv) - if err != nil { - log.Fatalf("Failed to create certificate: %s", err) - } - - if err := pem.Encode(certBuf, &pem.Block{Type: "CERTIFICATE", Bytes: derbytes}); err != nil { - panic("failed to encode certificate into PEM") - } - - cpool := x509.NewCertPool() - cpool.AppendCertsFromPEM(derbytes) - - t.config = &tls.Config{ - RootCAs: cpool, - Certificates: []tls.Certificate{ - { - Certificate: [][]byte{derbytes}, - PrivateKey: edpriv, - }, - }, - InsecureSkipVerify: true, - MinVersion: tls.VersionTLS13, - } -} - -func (t *tcptls) configForOptions(options *tcpOptions) *tls.Config { - config := t.config.Clone() - config.VerifyPeerCertificate = func(rawCerts [][]byte, _ [][]*x509.Certificate) error { - if len(rawCerts) != 1 { - return errors.New("tls not exactly 1 cert") - } - cert, err := x509.ParseCertificate(rawCerts[0]) - if err != nil { - return errors.New("tls failed to parse cert") - } - if cert.PublicKeyAlgorithm != x509.Ed25519 { - return errors.New("tls wrong cert algorithm") - } - pk := cert.PublicKey.(ed25519.PublicKey) - var key keyArray - copy(key[:], pk) - // If options does not have a pinned key, then pin one now - if options.pinnedEd25519Keys == nil { - options.pinnedEd25519Keys = make(map[keyArray]struct{}) - options.pinnedEd25519Keys[key] = struct{}{} - } - if _, isIn := options.pinnedEd25519Keys[key]; !isIn { - return errors.New("tls key does not match pinned key") - } - return nil - } - return config -} - -func (t *tcptls) upgradeListener(c net.Conn, options *tcpOptions) (net.Conn, error) { - config := t.configForOptions(options) - conn := tls.Server(c, config) - if err := conn.Handshake(); err != nil { - return c, err - } - return conn, nil -} - -func (t *tcptls) upgradeDialer(c net.Conn, options *tcpOptions) (net.Conn, error) { - config := t.configForOptions(options) - config.ServerName = options.tlsSNI - conn := tls.Client(c, config) - if err := conn.Handshake(); err != nil { - return c, err - } - return conn, nil -} diff --git a/src/multicast/multicast.go b/src/multicast/multicast.go index 24e9d04a..d40bcfc0 100644 --- a/src/multicast/multicast.go +++ b/src/multicast/multicast.go @@ -45,7 +45,7 @@ type interfaceInfo struct { } type listenerInfo struct { - listener *core.TcpListener + listener *core.Listener time time.Time interval time.Duration port uint16 @@ -219,7 +219,7 @@ func (m *Multicast) _announce() { for name, info := range m._listeners { // Prepare our stop function! stop := func() { - info.listener.Stop() + info.listener.Close() delete(m._listeners, name) m.log.Debugln("No longer multicasting on", name) } From b67c313f449427845b46da123f4767683fdf83b3 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 24 Sep 2022 12:22:38 +0100 Subject: [PATCH 040/293] Admin socket and `yggdrasilctl` improvements This refactors the request parsing, as well as improving the output for some request types. It also tweaks `yggdrasilctl` output, which should help with #947. --- cmd/yggdrasilctl/main.go | 35 ++++++- src/admin/admin.go | 194 +++++++++++++++++++++------------------ src/core/api.go | 22 ++++- src/multicast/admin.go | 25 ++--- src/tuntap/admin.go | 40 ++++---- 5 files changed, 193 insertions(+), 123 deletions(-) diff --git a/cmd/yggdrasilctl/main.go b/cmd/yggdrasilctl/main.go index 78700a40..0ec0ccf8 100644 --- a/cmd/yggdrasilctl/main.go +++ b/cmd/yggdrasilctl/main.go @@ -16,6 +16,8 @@ import ( "github.com/olekukonko/tablewriter" "github.com/yggdrasil-network/yggdrasil-go/src/admin" "github.com/yggdrasil-network/yggdrasil-go/src/core" + "github.com/yggdrasil-network/yggdrasil-go/src/multicast" + "github.com/yggdrasil-network/yggdrasil-go/src/tuntap" "github.com/yggdrasil-network/yggdrasil-go/src/version" ) @@ -135,6 +137,7 @@ func run() int { table.SetBorder(false) table.SetTablePadding("\t") // pad with tabs table.SetNoWhiteSpace(true) + table.SetAutoWrapText(false) switch strings.ToLower(recv.Request.Name) { case "list": @@ -142,9 +145,12 @@ func run() int { if err := json.Unmarshal(recv.Response, &resp); err != nil { panic(err) } - table.SetHeader([]string{"Command", "Arguments"}) + table.SetHeader([]string{"Command", "Arguments", "Description"}) for _, entry := range resp.List { - table.Append([]string{entry.Command, strings.Join(entry.Fields, ", ")}) + for i := range entry.Fields { + entry.Fields[i] = entry.Fields[i] + "=..." + } + table.Append([]string{entry.Command, strings.Join(entry.Fields, ", "), entry.Description}) } table.Render() @@ -238,8 +244,31 @@ func run() int { break } + case "getmulticastinterfaces": + var resp multicast.GetMulticastInterfacesResponse + if err := json.Unmarshal(recv.Response, &resp); err != nil { + panic(err) + } + table.SetHeader([]string{"Interface"}) + for _, p := range resp.Interfaces { + table.Append([]string{p}) + } + table.Render() + + case "gettun": + var resp tuntap.GetTUNResponse + if err := json.Unmarshal(recv.Response, &resp); err != nil { + panic(err) + } + table.Append([]string{"TUN enabled:", fmt.Sprintf("%#v", resp.Enabled)}) + if resp.Enabled { + table.Append([]string{"Interface name:", resp.Name}) + table.Append([]string{"Interface MTU:", fmt.Sprintf("%d", resp.MTU)}) + } + table.Render() + default: - panic("unknown response type: " + recv.Request.Name) + fmt.Println(string(recv.Response)) } return 0 diff --git a/src/admin/admin.go b/src/admin/admin.go index 4e98c891..376f79a6 100644 --- a/src/admin/admin.go +++ b/src/admin/admin.go @@ -43,6 +43,7 @@ type AdminSocketResponse struct { } type handler struct { + desc string // What does the endpoint do? args []string // List of human-readable argument names handler core.AddHandlerFunc // First is input map, second is output } @@ -52,16 +53,18 @@ type ListResponse struct { } type ListEntry struct { - Command string `json:"command"` - Fields []string `json:"fields,omitempty"` + Command string `json:"command"` + Description string `json:"description"` + Fields []string `json:"fields,omitempty"` } // AddHandler is called for each admin function to add the handler and help documentation to the API. -func (a *AdminSocket) AddHandler(name string, args []string, handlerfunc core.AddHandlerFunc) error { +func (a *AdminSocket) AddHandler(name, desc string, args []string, handlerfunc core.AddHandlerFunc) error { if _, ok := a.handlers[strings.ToLower(name)]; ok { return errors.New("handler already exists") } a.handlers[strings.ToLower(name)] = handler{ + desc: desc, args: args, handler: handlerfunc, } @@ -81,12 +84,13 @@ func New(c *core.Core, log util.Logger, opts ...SetupOption) (*AdminSocket, erro if a.config.listenaddr == "none" || a.config.listenaddr == "" { return nil, nil } - _ = a.AddHandler("list", []string{}, func(_ json.RawMessage) (interface{}, error) { + _ = a.AddHandler("list", "List available commands", []string{}, func(_ json.RawMessage) (interface{}, error) { res := &ListResponse{} for name, handler := range a.handlers { res.List = append(res.List, ListEntry{ - Command: name, - Fields: handler.args, + Command: name, + Description: handler.desc, + Fields: handler.args, }) } sort.SliceStable(res.List, func(i, j int) bool { @@ -100,61 +104,76 @@ func New(c *core.Core, log util.Logger, opts ...SetupOption) (*AdminSocket, erro } func (a *AdminSocket) SetupAdminHandlers() { - _ = a.AddHandler("getSelf", []string{}, func(in json.RawMessage) (interface{}, error) { - req := &GetSelfRequest{} - res := &GetSelfResponse{} - if err := json.Unmarshal(in, &req); err != nil { - return nil, err - } - if err := a.getSelfHandler(req, res); err != nil { - return nil, err - } - return res, nil - }) - _ = a.AddHandler("getPeers", []string{}, func(in json.RawMessage) (interface{}, error) { - req := &GetPeersRequest{} - res := &GetPeersResponse{} - if err := json.Unmarshal(in, &req); err != nil { - return nil, err - } - if err := a.getPeersHandler(req, res); err != nil { - return nil, err - } - return res, nil - }) - _ = a.AddHandler("getDHT", []string{}, func(in json.RawMessage) (interface{}, error) { - req := &GetDHTRequest{} - res := &GetDHTResponse{} - if err := json.Unmarshal(in, &req); err != nil { - return nil, err - } - if err := a.getDHTHandler(req, res); err != nil { - return nil, err - } - return res, nil - }) - _ = a.AddHandler("getPaths", []string{}, func(in json.RawMessage) (interface{}, error) { - req := &GetPathsRequest{} - res := &GetPathsResponse{} - if err := json.Unmarshal(in, &req); err != nil { - return nil, err - } - if err := a.getPathsHandler(req, res); err != nil { - return nil, err - } - return res, nil - }) - _ = a.AddHandler("getSessions", []string{}, func(in json.RawMessage) (interface{}, error) { - req := &GetSessionsRequest{} - res := &GetSessionsResponse{} - if err := json.Unmarshal(in, &req); err != nil { - return nil, err - } - if err := a.getSessionsHandler(req, res); err != nil { - return nil, err - } - return res, nil - }) + _ = a.AddHandler( + "getSelf", "Show details about this node", []string{}, + func(in json.RawMessage) (interface{}, error) { + req := &GetSelfRequest{} + res := &GetSelfResponse{} + if err := json.Unmarshal(in, &req); err != nil { + return nil, err + } + if err := a.getSelfHandler(req, res); err != nil { + return nil, err + } + return res, nil + }, + ) + _ = a.AddHandler( + "getPeers", "Show directly connected peers", []string{}, + func(in json.RawMessage) (interface{}, error) { + req := &GetPeersRequest{} + res := &GetPeersResponse{} + if err := json.Unmarshal(in, &req); err != nil { + return nil, err + } + if err := a.getPeersHandler(req, res); err != nil { + return nil, err + } + return res, nil + }, + ) + _ = a.AddHandler( + "getDHT", "Show known DHT entries", []string{}, + func(in json.RawMessage) (interface{}, error) { + req := &GetDHTRequest{} + res := &GetDHTResponse{} + if err := json.Unmarshal(in, &req); err != nil { + return nil, err + } + if err := a.getDHTHandler(req, res); err != nil { + return nil, err + } + return res, nil + }, + ) + _ = a.AddHandler( + "getPaths", "Show established paths through this node", []string{}, + func(in json.RawMessage) (interface{}, error) { + req := &GetPathsRequest{} + res := &GetPathsResponse{} + if err := json.Unmarshal(in, &req); err != nil { + return nil, err + } + if err := a.getPathsHandler(req, res); err != nil { + return nil, err + } + return res, nil + }, + ) + _ = a.AddHandler( + "getSessions", "Show established traffic sessions with remote nodes", []string{}, + func(in json.RawMessage) (interface{}, error) { + req := &GetSessionsRequest{} + res := &GetSessionsResponse{} + if err := json.Unmarshal(in, &req); err != nil { + return nil, err + } + if err := a.getSessionsHandler(req, res); err != nil { + return nil, err + } + return res, nil + }, + ) //_ = a.AddHandler("getNodeInfo", []string{"key"}, t.proto.nodeinfo.nodeInfoAdminHandler) //_ = a.AddHandler("debug_remoteGetSelf", []string{"key"}, t.proto.getSelfHandler) //_ = a.AddHandler("debug_remoteGetPeers", []string{"key"}, t.proto.getPeersHandler) @@ -279,35 +298,34 @@ func (a *AdminSocket) handleRequest(conn net.Conn) { for { var err error var buf json.RawMessage - _ = decoder.Decode(&buf) var resp AdminSocketResponse - resp.Status = "success" - if err = json.Unmarshal(buf, &resp.Request); err == nil { - if resp.Request.Name == "" { - resp.Status = "error" - resp.Response, _ = json.Marshal(ErrorResponse{ - Error: "No request specified", - }) - } else if h, ok := a.handlers[strings.ToLower(resp.Request.Name)]; ok { - res, err := h.handler(buf) - if err != nil { - resp.Status = "error" - resp.Response, _ = json.Marshal(ErrorResponse{ - Error: err.Error(), - }) - } - if resp.Response, err = json.Marshal(res); err != nil { - resp.Status = "error" - resp.Response, _ = json.Marshal(ErrorResponse{ - Error: err.Error(), - }) - } - } else { - resp.Status = "error" - resp.Response, _ = json.Marshal(ErrorResponse{ - Error: fmt.Sprintf("Unknown action '%s', try 'list' for help", resp.Request.Name), - }) + if err := func() error { + if err = decoder.Decode(&buf); err != nil { + return fmt.Errorf("Failed to find request") } + if err = json.Unmarshal(buf, &resp.Request); err != nil { + return fmt.Errorf("Failed to unmarshal request") + } + if resp.Request.Name == "" { + return fmt.Errorf("No request specified") + } + reqname := strings.ToLower(resp.Request.Name) + handler, ok := a.handlers[reqname] + if !ok { + return fmt.Errorf("Unknown action '%s', try 'list' for help", reqname) + } + res, err := handler.handler(buf) + if err != nil { + return fmt.Errorf("Handler returned error: %w", err) + } + if resp.Response, err = json.Marshal(res); err != nil { + return fmt.Errorf("Failed to marshal response: %w", err) + } + resp.Status = "success" + return nil + }(); err != nil { + resp.Status = "error" + resp.Error = err.Error() } if err = encoder.Encode(resp); err != nil { a.log.Debugln("Encode error:", err) diff --git a/src/core/api.go b/src/core/api.go index 657b5510..a67ecf3f 100644 --- a/src/core/api.go +++ b/src/core/api.go @@ -273,7 +273,7 @@ func (c *Core) PublicKey() ed25519.PublicKey { // Hack to get the admin stuff working, TODO something cleaner type AddHandler interface { - AddHandler(name string, args []string, handlerfunc AddHandlerFunc) error + AddHandler(name, desc string, args []string, handlerfunc AddHandlerFunc) error } type AddHandlerFunc func(json.RawMessage) (interface{}, error) @@ -281,16 +281,28 @@ type AddHandlerFunc func(json.RawMessage) (interface{}, error) // SetAdmin must be called after Init and before Start. // It sets the admin handler for NodeInfo and the Debug admin functions. func (c *Core) SetAdmin(a AddHandler) error { - if err := a.AddHandler("getNodeInfo", []string{"key"}, c.proto.nodeinfo.nodeInfoAdminHandler); err != nil { + if err := a.AddHandler( + "getNodeInfo", "Request nodeinfo from a remote node by its public key", []string{"key"}, + c.proto.nodeinfo.nodeInfoAdminHandler, + ); err != nil { return err } - if err := a.AddHandler("debug_remoteGetSelf", []string{"key"}, c.proto.getSelfHandler); err != nil { + if err := a.AddHandler( + "debug_remoteGetSelf", "Debug use only", []string{"key"}, + c.proto.getSelfHandler, + ); err != nil { return err } - if err := a.AddHandler("debug_remoteGetPeers", []string{"key"}, c.proto.getPeersHandler); err != nil { + if err := a.AddHandler( + "debug_remoteGetPeers", "Debug use only", []string{"key"}, + c.proto.getPeersHandler, + ); err != nil { return err } - if err := a.AddHandler("debug_remoteGetDHT", []string{"key"}, c.proto.getDHTHandler); err != nil { + if err := a.AddHandler( + "debug_remoteGetDHT", "Debug use only", []string{"key"}, + c.proto.getDHTHandler, + ); err != nil { return err } return nil diff --git a/src/multicast/admin.go b/src/multicast/admin.go index 2ae6ec08..9bcc257b 100644 --- a/src/multicast/admin.go +++ b/src/multicast/admin.go @@ -20,15 +20,18 @@ func (m *Multicast) getMulticastInterfacesHandler(req *GetMulticastInterfacesReq } func (m *Multicast) SetupAdminHandlers(a *admin.AdminSocket) { - _ = a.AddHandler("getMulticastInterfaces", []string{}, func(in json.RawMessage) (interface{}, error) { - req := &GetMulticastInterfacesRequest{} - res := &GetMulticastInterfacesResponse{} - if err := json.Unmarshal(in, &req); err != nil { - return nil, err - } - if err := m.getMulticastInterfacesHandler(req, res); err != nil { - return nil, err - } - return res, nil - }) + _ = a.AddHandler( + "getMulticastInterfaces", "Show which interfaces multicast is enabled on", []string{}, + func(in json.RawMessage) (interface{}, error) { + req := &GetMulticastInterfacesRequest{} + res := &GetMulticastInterfacesResponse{} + if err := json.Unmarshal(in, &req); err != nil { + return nil, err + } + if err := m.getMulticastInterfacesHandler(req, res); err != nil { + return nil, err + } + return res, nil + }, + ) } diff --git a/src/tuntap/admin.go b/src/tuntap/admin.go index 862a3c66..24521fe2 100644 --- a/src/tuntap/admin.go +++ b/src/tuntap/admin.go @@ -7,31 +7,39 @@ import ( ) type GetTUNRequest struct{} -type GetTUNResponse map[string]TUNEntry +type GetTUNResponse struct { + Enabled bool `json:"enabled"` + Name string `json:"name,omitempty"` + MTU uint64 `json:"mtu,omitempty"` +} type TUNEntry struct { MTU uint64 `json:"mtu"` } func (t *TunAdapter) getTUNHandler(req *GetTUNRequest, res *GetTUNResponse) error { - *res = GetTUNResponse{ - t.Name(): TUNEntry{ - MTU: t.MTU(), - }, + res.Enabled = t.isEnabled + if !t.isEnabled { + return nil } + res.Name = t.Name() + res.MTU = t.MTU() return nil } func (t *TunAdapter) SetupAdminHandlers(a *admin.AdminSocket) { - _ = a.AddHandler("getTunTap", []string{}, func(in json.RawMessage) (interface{}, error) { - req := &GetTUNRequest{} - res := &GetTUNResponse{} - if err := json.Unmarshal(in, &req); err != nil { - return nil, err - } - if err := t.getTUNHandler(req, res); err != nil { - return nil, err - } - return res, nil - }) + _ = a.AddHandler( + "getTun", "Show information about the node's TUN interface", []string{}, + func(in json.RawMessage) (interface{}, error) { + req := &GetTUNRequest{} + res := &GetTUNResponse{} + if err := json.Unmarshal(in, &req); err != nil { + return nil, err + } + if err := t.getTUNHandler(req, res); err != nil { + return nil, err + } + return res, nil + }, + ) } From 5ad8c33d26501a59412042adc2d90c89a9cd4aa7 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 24 Sep 2022 13:38:14 +0100 Subject: [PATCH 041/293] Remove packaging from main CI run --- .github/workflows/ci.yml | 134 +------------------------------------ .github/workflows/pkg.yml | 137 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 138 insertions(+), 133 deletions(-) create mode 100644 .github/workflows/pkg.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7915026d..0c99ac06 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -128,136 +128,4 @@ jobs: - name: Check all tests passed uses: re-actors/alls-green@release/v1 with: - jobs: ${{ toJSON(needs) }} - - build-packages-debian: - strategy: - fail-fast: false - matrix: - pkgarch: ["amd64", "i386", "mips", "mipsel", "armhf", "armel", "arm64"] - - name: Create Package (Debian, ${{ matrix.pkgarch }}) - needs: [tests-ok] - - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Set up Go - uses: actions/setup-go@v3 - with: - go-version: 1.19 - - - name: Build package - env: - PKGARCH: ${{ matrix.pkgarch }} - run: sh contrib/deb/generate.sh - - - name: Upload artifacts - uses: actions/upload-artifact@v3 - with: - name: Debian package (${{ matrix.pkgarch }}) - path: "*.deb" - if-no-files-found: error - - build-packages-macos: - strategy: - fail-fast: false - matrix: - pkgarch: ["amd64", "arm64"] - - name: Create Package (macOS, ${{ matrix.pkgarch }}) - needs: [tests-ok] - - runs-on: macos-latest - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Set up Go - uses: actions/setup-go@v3 - with: - go-version: 1.19 - - - name: Build package - env: - PKGARCH: ${{ matrix.pkgarch }} - run: sh contrib/macos/create-pkg.sh - - - name: Upload artifacts - uses: actions/upload-artifact@v3 - with: - name: macOS package (${{ matrix.pkgarch }}) - path: "*.pkg" - if-no-files-found: error - - build-packages-windows: - strategy: - fail-fast: false - matrix: - pkgarch: ["x64", "x86", "arm", "arm64"] - - name: Create Package (Windows, ${{ matrix.pkgarch }}) - needs: [tests-ok] - - runs-on: windows-latest - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Set up Go - uses: actions/setup-go@v3 - with: - go-version: 1.19 - - - name: Build package - run: sh contrib/msi/build-msi.sh ${{ matrix.pkgarch }} - - - name: Upload artifacts - uses: actions/upload-artifact@v3 - with: - name: Windows package (${{ matrix.pkgarch }}) - path: "*.msi" - if-no-files-found: error - - build-packages-router: - strategy: - fail-fast: false - matrix: - pkgarch: ["edgerouter-x", "edgerouter-lite", "vyos-amd64", "vyos-i386"] - - name: Create Package (Router, ${{ matrix.pkgarch }}) - needs: [tests-ok] - - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - path: yggdrasil - - - uses: actions/checkout@v3 - with: - repository: neilalexander/vyatta-yggdrasil - path: vyatta-yggdrasil - - - name: Set up Go - uses: actions/setup-go@v3 - with: - go-version: 1.19 - - - name: Build package - env: - BUILDDIR_YGG: /home/runner/work/yggdrasil-go/yggdrasil-go/yggdrasil - run: cd /home/runner/work/yggdrasil-go/yggdrasil-go/vyatta-yggdrasil && ./build-${{ matrix.pkgarch }} - - - name: Upload artifacts - uses: actions/upload-artifact@v3 - with: - name: Router package (${{ matrix.pkgarch }}) - path: "/home/runner/work/yggdrasil-go/yggdrasil-go/vyatta-yggdrasil/*.deb" - if-no-files-found: error + jobs: ${{ toJSON(needs) }} \ No newline at end of file diff --git a/.github/workflows/pkg.yml b/.github/workflows/pkg.yml new file mode 100644 index 00000000..f282cc21 --- /dev/null +++ b/.github/workflows/pkg.yml @@ -0,0 +1,137 @@ +name: Packages + +on: + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build-packages-debian: + strategy: + fail-fast: false + matrix: + pkgarch: ["amd64", "i386", "mips", "mipsel", "armhf", "armel", "arm64"] + + name: Package (Debian, ${{ matrix.pkgarch }}) + + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: 1.19 + + - name: Build package + env: + PKGARCH: ${{ matrix.pkgarch }} + run: sh contrib/deb/generate.sh + + - name: Upload artifacts + uses: actions/upload-artifact@v3 + with: + name: Debian package (${{ matrix.pkgarch }}) + path: "*.deb" + if-no-files-found: error + + build-packages-macos: + strategy: + fail-fast: false + matrix: + pkgarch: ["amd64", "arm64"] + + name: Package (macOS, ${{ matrix.pkgarch }}) + + runs-on: macos-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: 1.19 + + - name: Build package + env: + PKGARCH: ${{ matrix.pkgarch }} + run: sh contrib/macos/create-pkg.sh + + - name: Upload artifacts + uses: actions/upload-artifact@v3 + with: + name: macOS package (${{ matrix.pkgarch }}) + path: "*.pkg" + if-no-files-found: error + + build-packages-windows: + strategy: + fail-fast: false + matrix: + pkgarch: ["x64", "x86", "arm", "arm64"] + + name: Package (Windows, ${{ matrix.pkgarch }}) + + runs-on: windows-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: 1.19 + + - name: Build package + run: sh contrib/msi/build-msi.sh ${{ matrix.pkgarch }} + + - name: Upload artifacts + uses: actions/upload-artifact@v3 + with: + name: Windows package (${{ matrix.pkgarch }}) + path: "*.msi" + if-no-files-found: error + + build-packages-router: + strategy: + fail-fast: false + matrix: + pkgarch: ["edgerouter-x", "edgerouter-lite", "vyos-amd64", "vyos-i386"] + + name: Package (Router, ${{ matrix.pkgarch }}) + + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + path: yggdrasil + + - uses: actions/checkout@v3 + with: + repository: neilalexander/vyatta-yggdrasil + path: vyatta-yggdrasil + + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: 1.19 + + - name: Build package + env: + BUILDDIR_YGG: /home/runner/work/yggdrasil-go/yggdrasil-go/yggdrasil + run: cd /home/runner/work/yggdrasil-go/yggdrasil-go/vyatta-yggdrasil && ./build-${{ matrix.pkgarch }} + + - name: Upload artifacts + uses: actions/upload-artifact@v3 + with: + name: Router package (${{ matrix.pkgarch }}) + path: "/home/runner/work/yggdrasil-go/yggdrasil-go/vyatta-yggdrasil/*.deb" + if-no-files-found: error From 0abfe78858ae60b2f047ce314503223b21eec07b Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 24 Sep 2022 13:46:22 +0100 Subject: [PATCH 042/293] Silence error when reconnecting to already connected peer --- src/core/link.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/core/link.go b/src/core/link.go index 08e8dfc2..4d5cff57 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -113,17 +113,19 @@ func (l *links) isConnectedTo(info linkInfo) bool { func (l *links) call(u *url.URL, sintf string) error { info := linkInfoFor(u.Scheme, sintf, u.Host) if l.isConnectedTo(info) { - return fmt.Errorf("already connected to this node") + return nil } options := linkOptions{ pinnedEd25519Keys: map[keyArray]struct{}{}, } for _, pubkey := range u.Query()["key"] { - if sigPub, err := hex.DecodeString(pubkey); err == nil { - var sigPubKey keyArray - copy(sigPubKey[:], sigPub) - options.pinnedEd25519Keys[sigPubKey] = struct{}{} + sigPub, err := hex.DecodeString(pubkey) + if err != nil { + return fmt.Errorf("pinned key contains invalid hex characters") } + var sigPubKey keyArray + copy(sigPubKey[:], sigPub) + options.pinnedEd25519Keys[sigPubKey] = struct{}{} } switch info.linkType { case "tcp": From 217ac39e77152949889bbb7461635fba962ebb40 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 24 Sep 2022 14:09:08 +0100 Subject: [PATCH 043/293] Allow setting default config path and `AdminListen` at compile time By providing the following items to `LDFLAGS`: * `-X github.com/yggdrasil-network/yggdrasil-go/src/defaults.defaultConfig=/path/to/config` * '-X github.com/yggdrasil-network/yggdrasil-go/src/defaults.defaultAdminListen=unix://path/to/sock' Closes #818. --- src/defaults/defaults.go | 24 ++++++++++++++++++++---- src/defaults/defaults_darwin.go | 2 +- src/defaults/defaults_freebsd.go | 2 +- src/defaults/defaults_linux.go | 2 +- src/defaults/defaults_openbsd.go | 2 +- src/defaults/defaults_other.go | 2 +- src/defaults/defaults_windows.go | 2 +- 7 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/defaults/defaults.go b/src/defaults/defaults.go index 7912fc76..a7492de1 100644 --- a/src/defaults/defaults.go +++ b/src/defaults/defaults.go @@ -4,6 +4,9 @@ import "github.com/yggdrasil-network/yggdrasil-go/src/config" type MulticastInterfaceConfig = config.MulticastInterfaceConfig +var defaultConfig = "" // LDFLAGS='-X github.com/yggdrasil-network/yggdrasil-go/src/defaults.defaultConfig=/path/to/config +var defaultAdminListen = "" // LDFLAGS='-X github.com/yggdrasil-network/yggdrasil-go/src/defaults.defaultAdminListen=unix://path/to/sock' + // Defines which parameters are expected by default for configuration on a // specific platform. These values are populated in the relevant defaults_*.go // for the platform being targeted. They must be set. @@ -23,21 +26,34 @@ type platformDefaultParameters struct { DefaultIfName string } +func GetDefaults() platformDefaultParameters { + defaults := getDefaults() + if defaultConfig != "" { + defaults.DefaultConfigFile = defaultConfig + } + if defaultAdminListen != "" { + defaults.DefaultAdminListen = defaultAdminListen + } + return defaults +} + // Generates default configuration and returns a pointer to the resulting // NodeConfig. This is used when outputting the -genconf parameter and also when // using -autoconf. func GenerateConfig() *config.NodeConfig { + // Get the defaults for the platform. + defaults := GetDefaults() // Create a node configuration and populate it. cfg := new(config.NodeConfig) cfg.NewKeys() cfg.Listen = []string{} - cfg.AdminListen = GetDefaults().DefaultAdminListen + cfg.AdminListen = defaults.DefaultAdminListen cfg.Peers = []string{} cfg.InterfacePeers = map[string][]string{} cfg.AllowedPublicKeys = []string{} - cfg.MulticastInterfaces = GetDefaults().DefaultMulticastInterfaces - cfg.IfName = GetDefaults().DefaultIfName - cfg.IfMTU = GetDefaults().DefaultIfMTU + cfg.MulticastInterfaces = defaults.DefaultMulticastInterfaces + cfg.IfName = defaults.DefaultIfName + cfg.IfMTU = defaults.DefaultIfMTU cfg.NodeInfoPrivacy = false return cfg diff --git a/src/defaults/defaults_darwin.go b/src/defaults/defaults_darwin.go index 060ce814..a848342d 100644 --- a/src/defaults/defaults_darwin.go +++ b/src/defaults/defaults_darwin.go @@ -5,7 +5,7 @@ package defaults // Sane defaults for the macOS/Darwin platform. The "default" options may be // may be replaced by the running configuration. -func GetDefaults() platformDefaultParameters { +func getDefaults() platformDefaultParameters { return platformDefaultParameters{ // Admin DefaultAdminListen: "unix:///var/run/yggdrasil.sock", diff --git a/src/defaults/defaults_freebsd.go b/src/defaults/defaults_freebsd.go index 84df48ad..c8f918bf 100644 --- a/src/defaults/defaults_freebsd.go +++ b/src/defaults/defaults_freebsd.go @@ -5,7 +5,7 @@ package defaults // Sane defaults for the BSD platforms. The "default" options may be // may be replaced by the running configuration. -func GetDefaults() platformDefaultParameters { +func getDefaults() platformDefaultParameters { return platformDefaultParameters{ // Admin DefaultAdminListen: "unix:///var/run/yggdrasil.sock", diff --git a/src/defaults/defaults_linux.go b/src/defaults/defaults_linux.go index c7f5f119..ea646dbb 100644 --- a/src/defaults/defaults_linux.go +++ b/src/defaults/defaults_linux.go @@ -5,7 +5,7 @@ package defaults // Sane defaults for the Linux platform. The "default" options may be // may be replaced by the running configuration. -func GetDefaults() platformDefaultParameters { +func getDefaults() platformDefaultParameters { return platformDefaultParameters{ // Admin DefaultAdminListen: "unix:///var/run/yggdrasil.sock", diff --git a/src/defaults/defaults_openbsd.go b/src/defaults/defaults_openbsd.go index 0ec877ca..6e9d1743 100644 --- a/src/defaults/defaults_openbsd.go +++ b/src/defaults/defaults_openbsd.go @@ -5,7 +5,7 @@ package defaults // Sane defaults for the BSD platforms. The "default" options may be // may be replaced by the running configuration. -func GetDefaults() platformDefaultParameters { +func getDefaults() platformDefaultParameters { return platformDefaultParameters{ // Admin DefaultAdminListen: "unix:///var/run/yggdrasil.sock", diff --git a/src/defaults/defaults_other.go b/src/defaults/defaults_other.go index 37637425..f42b0875 100644 --- a/src/defaults/defaults_other.go +++ b/src/defaults/defaults_other.go @@ -5,7 +5,7 @@ package defaults // Sane defaults for the other platforms. The "default" options may be // may be replaced by the running configuration. -func GetDefaults() platformDefaultParameters { +func getDefaults() platformDefaultParameters { return platformDefaultParameters{ // Admin DefaultAdminListen: "tcp://localhost:9001", diff --git a/src/defaults/defaults_windows.go b/src/defaults/defaults_windows.go index c1ea9689..90f3b9e6 100644 --- a/src/defaults/defaults_windows.go +++ b/src/defaults/defaults_windows.go @@ -5,7 +5,7 @@ package defaults // Sane defaults for the Windows platform. The "default" options may be // may be replaced by the running configuration. -func GetDefaults() platformDefaultParameters { +func getDefaults() platformDefaultParameters { return platformDefaultParameters{ // Admin DefaultAdminListen: "tcp://localhost:9001", From 01c44a087ba8a8fee3ffad561e0c603f2a4c2d1d Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 24 Sep 2022 14:41:47 +0100 Subject: [PATCH 044/293] Rename `tuntap` package to `tun` We haven't had TAP support in ages. --- cmd/yggdrasil/main.go | 20 ++++++++++---------- cmd/yggdrasilctl/main.go | 4 ++-- src/defaults/defaults.go | 2 +- src/defaults/defaults_darwin.go | 2 +- src/defaults/defaults_freebsd.go | 2 +- src/defaults/defaults_linux.go | 2 +- src/defaults/defaults_openbsd.go | 2 +- src/defaults/defaults_other.go | 2 +- src/defaults/defaults_windows.go | 2 +- src/{tuntap => tun}/admin.go | 2 +- src/{tuntap => tun}/iface.go | 2 +- src/{tuntap => tun}/options.go | 2 +- src/{tuntap => tun}/tun.go | 7 +------ src/{tuntap => tun}/tun_bsd.go | 2 +- src/{tuntap => tun}/tun_darwin.go | 2 +- src/{tuntap => tun}/tun_linux.go | 4 ++-- src/{tuntap => tun}/tun_other.go | 2 +- src/{tuntap => tun}/tun_windows.go | 6 +++--- 18 files changed, 31 insertions(+), 36 deletions(-) rename src/{tuntap => tun}/admin.go (98%) rename src/{tuntap => tun}/iface.go (98%) rename src/{tuntap => tun}/options.go (95%) rename src/{tuntap => tun}/tun.go (96%) rename src/{tuntap => tun}/tun_bsd.go (99%) rename src/{tuntap => tun}/tun_darwin.go (99%) rename src/{tuntap => tun}/tun_linux.go (94%) rename src/{tuntap => tun}/tun_other.go (98%) rename src/{tuntap => tun}/tun_windows.go (97%) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 1ef6738b..abce0cea 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -32,13 +32,13 @@ import ( "github.com/yggdrasil-network/yggdrasil-go/src/core" "github.com/yggdrasil-network/yggdrasil-go/src/multicast" - "github.com/yggdrasil-network/yggdrasil-go/src/tuntap" + "github.com/yggdrasil-network/yggdrasil-go/src/tun" "github.com/yggdrasil-network/yggdrasil-go/src/version" ) type node struct { core *core.Core - tuntap *tuntap.TunAdapter + tun *tun.TunAdapter multicast *multicast.Multicast admin *admin.AdminSocket } @@ -219,7 +219,7 @@ func run(args yggArgs, ctx context.Context, done chan struct{}) { return case args.autoconf: // Use an autoconf-generated config, this will give us random keys and - // port numbers, and will use an automatically selected TUN/TAP interface. + // port numbers, and will use an automatically selected TUN interface. cfg = defaults.GenerateConfig() case args.useconffile != "" || args.useconf: // Read the configuration from either stdin or from the filesystem @@ -348,15 +348,15 @@ func run(args yggArgs, ctx context.Context, done chan struct{}) { // Setup the TUN module. { - options := []tuntap.SetupOption{ - tuntap.InterfaceName(cfg.IfName), - tuntap.InterfaceMTU(cfg.IfMTU), + options := []tun.SetupOption{ + tun.InterfaceName(cfg.IfName), + tun.InterfaceMTU(cfg.IfMTU), } - if n.tuntap, err = tuntap.New(ipv6rwc.NewReadWriteCloser(n.core), logger, options...); err != nil { + if n.tun, err = tun.New(ipv6rwc.NewReadWriteCloser(n.core), logger, options...); err != nil { panic(err) } - if n.admin != nil && n.tuntap != nil { - n.tuntap.SetupAdminHandlers(n.admin) + if n.admin != nil && n.tun != nil { + n.tun.SetupAdminHandlers(n.admin) } } @@ -378,7 +378,7 @@ func run(args yggArgs, ctx context.Context, done chan struct{}) { func (n *node) shutdown() { _ = n.admin.Stop() _ = n.multicast.Stop() - _ = n.tuntap.Stop() + _ = n.tun.Stop() n.core.Stop() } diff --git a/cmd/yggdrasilctl/main.go b/cmd/yggdrasilctl/main.go index 0ec0ccf8..1e93b98f 100644 --- a/cmd/yggdrasilctl/main.go +++ b/cmd/yggdrasilctl/main.go @@ -17,7 +17,7 @@ import ( "github.com/yggdrasil-network/yggdrasil-go/src/admin" "github.com/yggdrasil-network/yggdrasil-go/src/core" "github.com/yggdrasil-network/yggdrasil-go/src/multicast" - "github.com/yggdrasil-network/yggdrasil-go/src/tuntap" + "github.com/yggdrasil-network/yggdrasil-go/src/tun" "github.com/yggdrasil-network/yggdrasil-go/src/version" ) @@ -256,7 +256,7 @@ func run() int { table.Render() case "gettun": - var resp tuntap.GetTUNResponse + var resp tun.GetTUNResponse if err := json.Unmarshal(recv.Response, &resp); err != nil { panic(err) } diff --git a/src/defaults/defaults.go b/src/defaults/defaults.go index a7492de1..6374f4ed 100644 --- a/src/defaults/defaults.go +++ b/src/defaults/defaults.go @@ -20,7 +20,7 @@ type platformDefaultParameters struct { // Multicast interfaces DefaultMulticastInterfaces []MulticastInterfaceConfig - // TUN/TAP + // TUN MaximumIfMTU uint64 DefaultIfMTU uint64 DefaultIfName string diff --git a/src/defaults/defaults_darwin.go b/src/defaults/defaults_darwin.go index a848342d..3ffd9ffb 100644 --- a/src/defaults/defaults_darwin.go +++ b/src/defaults/defaults_darwin.go @@ -19,7 +19,7 @@ func getDefaults() platformDefaultParameters { {Regex: "bridge.*", Beacon: true, Listen: true}, }, - // TUN/TAP + // TUN MaximumIfMTU: 65535, DefaultIfMTU: 65535, DefaultIfName: "auto", diff --git a/src/defaults/defaults_freebsd.go b/src/defaults/defaults_freebsd.go index c8f918bf..43359389 100644 --- a/src/defaults/defaults_freebsd.go +++ b/src/defaults/defaults_freebsd.go @@ -18,7 +18,7 @@ func getDefaults() platformDefaultParameters { {Regex: ".*", Beacon: true, Listen: true}, }, - // TUN/TAP + // TUN MaximumIfMTU: 32767, DefaultIfMTU: 32767, DefaultIfName: "/dev/tun0", diff --git a/src/defaults/defaults_linux.go b/src/defaults/defaults_linux.go index ea646dbb..bad6233e 100644 --- a/src/defaults/defaults_linux.go +++ b/src/defaults/defaults_linux.go @@ -18,7 +18,7 @@ func getDefaults() platformDefaultParameters { {Regex: ".*", Beacon: true, Listen: true}, }, - // TUN/TAP + // TUN MaximumIfMTU: 65535, DefaultIfMTU: 65535, DefaultIfName: "auto", diff --git a/src/defaults/defaults_openbsd.go b/src/defaults/defaults_openbsd.go index 6e9d1743..e5d1fa9a 100644 --- a/src/defaults/defaults_openbsd.go +++ b/src/defaults/defaults_openbsd.go @@ -18,7 +18,7 @@ func getDefaults() platformDefaultParameters { {Regex: ".*", Beacon: true, Listen: true}, }, - // TUN/TAP + // TUN MaximumIfMTU: 16384, DefaultIfMTU: 16384, DefaultIfName: "tun0", diff --git a/src/defaults/defaults_other.go b/src/defaults/defaults_other.go index f42b0875..bb22864e 100644 --- a/src/defaults/defaults_other.go +++ b/src/defaults/defaults_other.go @@ -18,7 +18,7 @@ func getDefaults() platformDefaultParameters { {Regex: ".*", Beacon: true, Listen: true}, }, - // TUN/TAP + // TUN MaximumIfMTU: 65535, DefaultIfMTU: 65535, DefaultIfName: "none", diff --git a/src/defaults/defaults_windows.go b/src/defaults/defaults_windows.go index 90f3b9e6..e2601bf7 100644 --- a/src/defaults/defaults_windows.go +++ b/src/defaults/defaults_windows.go @@ -18,7 +18,7 @@ func getDefaults() platformDefaultParameters { {Regex: ".*", Beacon: true, Listen: true}, }, - // TUN/TAP + // TUN MaximumIfMTU: 65535, DefaultIfMTU: 65535, DefaultIfName: "Yggdrasil", diff --git a/src/tuntap/admin.go b/src/tun/admin.go similarity index 98% rename from src/tuntap/admin.go rename to src/tun/admin.go index 24521fe2..90f712e9 100644 --- a/src/tuntap/admin.go +++ b/src/tun/admin.go @@ -1,4 +1,4 @@ -package tuntap +package tun import ( "encoding/json" diff --git a/src/tuntap/iface.go b/src/tun/iface.go similarity index 98% rename from src/tuntap/iface.go rename to src/tun/iface.go index f629399a..c98b56d6 100644 --- a/src/tuntap/iface.go +++ b/src/tun/iface.go @@ -1,4 +1,4 @@ -package tuntap +package tun const TUN_OFFSET_BYTES = 4 diff --git a/src/tuntap/options.go b/src/tun/options.go similarity index 95% rename from src/tuntap/options.go rename to src/tun/options.go index 10af8d96..7be79211 100644 --- a/src/tuntap/options.go +++ b/src/tun/options.go @@ -1,4 +1,4 @@ -package tuntap +package tun func (m *TunAdapter) _applyOption(opt SetupOption) { switch v := opt.(type) { diff --git a/src/tuntap/tun.go b/src/tun/tun.go similarity index 96% rename from src/tuntap/tun.go rename to src/tun/tun.go index b0c444f4..0f7a70e3 100644 --- a/src/tuntap/tun.go +++ b/src/tun/tun.go @@ -1,10 +1,7 @@ -package tuntap +package tun // This manages the tun driver to send/recv packets to/from applications -// TODO: Crypto-key routing support -// TODO: Set MTU of session properly -// TODO: Reject packets that exceed session MTU with ICMPv6 for PMTU Discovery // TODO: Connection timeouts (call Conn.Close() when we want to time out) // TODO: Don't block in reader on writes that are pending searches @@ -13,8 +10,6 @@ import ( "fmt" "net" - //"sync" - "github.com/Arceliar/phony" "golang.zx2c4.com/wireguard/tun" diff --git a/src/tuntap/tun_bsd.go b/src/tun/tun_bsd.go similarity index 99% rename from src/tuntap/tun_bsd.go rename to src/tun/tun_bsd.go index fe36266b..9a8f70ce 100644 --- a/src/tuntap/tun_bsd.go +++ b/src/tun/tun_bsd.go @@ -1,7 +1,7 @@ //go:build openbsd || freebsd // +build openbsd freebsd -package tuntap +package tun import ( "encoding/binary" diff --git a/src/tuntap/tun_darwin.go b/src/tun/tun_darwin.go similarity index 99% rename from src/tuntap/tun_darwin.go rename to src/tun/tun_darwin.go index 6f6e2528..a6d87a0c 100644 --- a/src/tuntap/tun_darwin.go +++ b/src/tun/tun_darwin.go @@ -1,7 +1,7 @@ //go:build !mobile // +build !mobile -package tuntap +package tun // The darwin platform specific tun parts diff --git a/src/tuntap/tun_linux.go b/src/tun/tun_linux.go similarity index 94% rename from src/tuntap/tun_linux.go rename to src/tun/tun_linux.go index f849c00f..1e42b7b8 100644 --- a/src/tuntap/tun_linux.go +++ b/src/tun/tun_linux.go @@ -1,7 +1,7 @@ //go:build !mobile // +build !mobile -package tuntap +package tun // The linux platform specific tun parts @@ -28,7 +28,7 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error { return tun.setupAddress(addr) } -// Configures the TAP adapter with the correct IPv6 address and MTU. Netlink +// Configures the TUN adapter with the correct IPv6 address and MTU. Netlink // is used to do this, so there is not a hard requirement on "ip" or "ifconfig" // to exist on the system, but this will fail if Netlink is not present in the // kernel (it nearly always is). diff --git a/src/tuntap/tun_other.go b/src/tun/tun_other.go similarity index 98% rename from src/tuntap/tun_other.go rename to src/tun/tun_other.go index 8ce24953..c618d837 100644 --- a/src/tuntap/tun_other.go +++ b/src/tun/tun_other.go @@ -1,7 +1,7 @@ //go:build !linux && !darwin && !windows && !openbsd && !freebsd && !mobile // +build !linux,!darwin,!windows,!openbsd,!freebsd,!mobile -package tuntap +package tun // This is to catch unsupported platforms // If your platform supports tun devices, you could try configuring it manually diff --git a/src/tuntap/tun_windows.go b/src/tun/tun_windows.go similarity index 97% rename from src/tuntap/tun_windows.go rename to src/tun/tun_windows.go index 8dce7274..c3e36596 100644 --- a/src/tuntap/tun_windows.go +++ b/src/tun/tun_windows.go @@ -1,7 +1,7 @@ //go:build windows // +build windows -package tuntap +package tun import ( "bytes" @@ -50,7 +50,7 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error { }) } -// Sets the MTU of the TAP adapter. +// Sets the MTU of the TUN adapter. func (tun *TunAdapter) setupMTU(mtu uint64) error { if tun.iface == nil || tun.Name() == "" { return errors.New("Can't configure MTU as TUN adapter is not present") @@ -77,7 +77,7 @@ func (tun *TunAdapter) setupMTU(mtu uint64) error { return nil } -// Sets the IPv6 address of the TAP adapter. +// Sets the IPv6 address of the TUN adapter. func (tun *TunAdapter) setupAddress(addr string) error { if tun.iface == nil || tun.Name() == "" { return errors.New("Can't configure IPv6 address as TUN adapter is not present") From e165b1fa0ca6bdcbf386410f875d8b0ff49365a6 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 24 Sep 2022 14:44:50 +0100 Subject: [PATCH 045/293] Add quote marks to `InterfacePeers` comment Fixes #945. --- src/config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/config.go b/src/config/config.go index 041147b8..c93699e1 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -28,7 +28,7 @@ import ( type NodeConfig struct { sync.RWMutex `json:"-"` Peers []string `comment:"List of connection strings for outbound peer connections in URI format,\ne.g. tls://a.b.c.d:e or socks://a.b.c.d:e/f.g.h.i:j. These connections\nwill obey the operating system routing table, therefore you should\nuse this section when you may connect via different interfaces."` - InterfacePeers map[string][]string `comment:"List of connection strings for outbound peer connections in URI format,\narranged by source interface, e.g. { \"eth0\": [ tls://a.b.c.d:e ] }.\nNote that SOCKS peerings will NOT be affected by this option and should\ngo in the \"Peers\" section instead."` + InterfacePeers map[string][]string `comment:"List of connection strings for outbound peer connections in URI format,\narranged by source interface, e.g. { \"eth0\": [ \"tls://a.b.c.d:e\" ] }.\nNote that SOCKS peerings will NOT be affected by this option and should\ngo in the \"Peers\" section instead."` Listen []string `comment:"Listen addresses for incoming connections. You will need to add\nlisteners in order to accept incoming peerings from non-local nodes.\nMulticast peer discovery will work regardless of any listeners set\nhere. Each listener should be specified in URI format as above, e.g.\ntls://0.0.0.0:0 or tls://[::]:0 to listen on all interfaces."` 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."` MulticastInterfaces []MulticastInterfaceConfig `comment:"Configuration for which interfaces multicast peer discovery should be\nenabled on. Each entry in the list should be a json object which may\ncontain Regex, Beacon, Listen, and Port. Regex is a regular expression\nwhich is matched against an interface name, and interfaces use the\nfirst configuration that they match gainst. Beacon configures whether\nor not the node should send link-local multicast beacons to advertise\ntheir presence, while listening for incoming connections on Port.\nListen controls whether or not the node listens for multicast beacons\nand opens outgoing connections."` From d24d3fa047c0115fd56a98b076c0b13dc6b47e87 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 24 Sep 2022 16:51:31 +0100 Subject: [PATCH 046/293] Use deadline for link handshake (#949) This uses a 6 second deadline for timeouts instead of using `util.FuncTimeout` at 30 seconds for the read and then again for the write. If the handshake doesn't complete within 6 seconds then it's going to probably collapse when we give the connection to Ironwood and it tries to do a keepalive anyway. --- src/core/link.go | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/src/core/link.go b/src/core/link.go index 4d5cff57..939bb253 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -17,7 +17,6 @@ import ( "github.com/Arceliar/phony" "github.com/yggdrasil-network/yggdrasil-go/src/address" - "github.com/yggdrasil-network/yggdrasil-go/src/util" //"github.com/Arceliar/phony" // TODO? use instead of mutexes ) @@ -234,36 +233,25 @@ func (intf *link) handler() error { delete(intf.links._links, intf.info) }) - // TODO split some of this into shorter functions, so it's easier to read, and for the FIXME duplicate peer issue mentioned later meta := version_getBaseMetadata() meta.key = intf.links.core.public metaBytes := meta.encode() - // TODO timeouts on send/recv (goroutine for send/recv, channel select w/ timer) - var err error - if !util.FuncTimeout(30*time.Second, func() { - var n int - n, err = intf.conn.Write(metaBytes) - if err == nil && n != len(metaBytes) { - err = errors.New("incomplete metadata send") - } - }) { - return errors.New("timeout on metadata send") + if err := intf.conn.SetDeadline(time.Now().Add(time.Second * 6)); err != nil { + return fmt.Errorf("failed to set handshake deadline: %w", err) } - if err != nil { + n, err := intf.conn.Write(metaBytes) + switch { + case err != nil: return fmt.Errorf("write handshake: %w", err) + case err == nil && n != len(metaBytes): + return fmt.Errorf("incomplete handshake send") } - if !util.FuncTimeout(30*time.Second, func() { - var n int - n, err = io.ReadFull(intf.conn, metaBytes) - if err == nil && n != len(metaBytes) { - err = errors.New("incomplete metadata recv") - } - }) { - return errors.New("timeout on metadata recv") - } - if err != nil { + if _, err = io.ReadFull(intf.conn, metaBytes); err != nil { return fmt.Errorf("read handshake: %w", err) } + if err := intf.conn.SetDeadline(time.Time{}); err != nil { + return fmt.Errorf("failed to clear handshake deadline: %w", err) + } meta = version_metadata{} base := version_getBaseMetadata() if !meta.decode(metaBytes) { From d9fe6f72ac9368537f1ee915d9851d6d215b7599 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 24 Sep 2022 17:05:44 +0100 Subject: [PATCH 047/293] Lint tweaks --- src/core/core.go | 4 ++-- src/core/link.go | 15 +++++++-------- src/core/link_tcp.go | 2 +- src/core/link_tls.go | 2 +- src/core/link_unix.go | 2 +- 5 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/core/core.go b/src/core/core.go index 4cc08ad6..5cfcc7a2 100644 --- a/src/core/core.go +++ b/src/core/core.go @@ -127,7 +127,7 @@ func (c *Core) _addPeerLoop() { func (c *Core) Stop() { phony.Block(c, func() { c.log.Infoln("Stopping...") - c._close() + _ = c._close() c.log.Infoln("Stopped") }) } @@ -135,7 +135,7 @@ func (c *Core) Stop() { // This function is unsafe and should only be ran by the core actor. func (c *Core) _close() error { c.cancel() - _ = c.links.shutdown() + c.links.shutdown() err := c.PacketConn.Close() if c.addPeerTimer != nil { c.addPeerTimer.Stop() diff --git a/src/core/link.go b/src/core/link.go index 939bb253..26e44669 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -82,23 +82,22 @@ func (l *links) init(c *Core) error { return nil } -func (l *links) shutdown() error { +func (l *links) shutdown() { phony.Block(l.tcp, func() { for l := range l.tcp._listeners { - l.Close() + _ = l.Close() } }) phony.Block(l.tls, func() { for l := range l.tls._listeners { - l.Close() + _ = l.Close() } }) phony.Block(l.unix, func() { for l := range l.unix._listeners { - l.Close() + _ = l.Close() } }) - return nil } func (l *links) isConnectedTo(info linkInfo) bool { @@ -216,7 +215,7 @@ func (l *links) create(conn net.Conn, name string, info linkInfo, incoming, forc } func (intf *link) handler() error { - defer intf.conn.Close() + defer intf.conn.Close() // nolint:errcheck // Don't connect to this link more than once. if intf.links.isConnectedTo(intf.info) { @@ -249,7 +248,7 @@ func (intf *link) handler() error { if _, err = io.ReadFull(intf.conn, metaBytes); err != nil { return fmt.Errorf("read handshake: %w", err) } - if err := intf.conn.SetDeadline(time.Time{}); err != nil { + if err = intf.conn.SetDeadline(time.Time{}); err != nil { return fmt.Errorf("failed to clear handshake deadline: %w", err) } meta = version_metadata{} @@ -294,7 +293,7 @@ func (intf *link) handler() error { if intf.incoming && !intf.force && !isallowed { intf.links.core.log.Warnf("%s connection from %s forbidden: AllowedEncryptionPublicKeys does not contain key %s", strings.ToUpper(intf.info.linkType), intf.info.remote, hex.EncodeToString(meta.key)) - intf.close() + _ = intf.close() return fmt.Errorf("forbidden connection") } diff --git a/src/core/link_tcp.go b/src/core/link_tcp.go index fcac8b4d..c5a73c9e 100644 --- a/src/core/link_tcp.go +++ b/src/core/link_tcp.go @@ -89,7 +89,7 @@ func (l *linkTCP) listen(url *url.URL, sintf string) (*Listener, error) { l.core.log.Errorln("Failed to create inbound link:", err) } } - listener.Close() + _ = listener.Close() close(entry.closed) l.core.log.Printf("TCP listener stopped on %s", listener.Addr()) }() diff --git a/src/core/link_tls.go b/src/core/link_tls.go index e6ffd5a8..1e932b66 100644 --- a/src/core/link_tls.go +++ b/src/core/link_tls.go @@ -112,7 +112,7 @@ func (l *linkTLS) listen(url *url.URL, sintf string) (*Listener, error) { l.core.log.Errorln("Failed to create inbound link:", err) } } - tlslistener.Close() + _ = tlslistener.Close() close(entry.closed) l.core.log.Printf("TLS listener stopped on %s", listener.Addr()) }() diff --git a/src/core/link_unix.go b/src/core/link_unix.go index d63c1d90..e71f9362 100644 --- a/src/core/link_unix.go +++ b/src/core/link_unix.go @@ -79,7 +79,7 @@ func (l *linkUNIX) listen(url *url.URL, _ string) (*Listener, error) { l.core.log.Errorln("Failed to create inbound link:", err) } } - listener.Close() + _ = listener.Close() close(entry.closed) l.core.log.Printf("UNIX listener stopped on %s", listener.Addr()) }() From 1de587a971ee2f93d7e7d8b6fd894f32458590cd Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 24 Sep 2022 17:06:24 +0100 Subject: [PATCH 048/293] Update to Arceliar/ironwood@ed4b6d4 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index b72bf534..0a0f8fa6 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/yggdrasil-network/yggdrasil-go go 1.17 require ( - github.com/Arceliar/ironwood v0.0.0-20220903132624-ee60c16bcfcf + github.com/Arceliar/ironwood v0.0.0-20220924160422-ed4b6d4750b6 github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 github.com/cheggaaa/pb/v3 v3.0.8 github.com/gologme/log v1.2.0 diff --git a/go.sum b/go.sum index ec5ea18f..e18dafa8 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Arceliar/ironwood v0.0.0-20220903132624-ee60c16bcfcf h1:kjPkmDHUTWUma/4tqDl208bOk3jsUEqOJA6TsMZo5Jk= -github.com/Arceliar/ironwood v0.0.0-20220903132624-ee60c16bcfcf/go.mod h1:RP72rucOFm5udrnEzTmIWLRVGQiV/fSUAQXJ0RST/nk= +github.com/Arceliar/ironwood v0.0.0-20220924160422-ed4b6d4750b6 h1:iwL6nm2ibyuHXYimRNtFof7RJfe8JB+6CPDskV7K7gA= +github.com/Arceliar/ironwood v0.0.0-20220924160422-ed4b6d4750b6/go.mod h1:RP72rucOFm5udrnEzTmIWLRVGQiV/fSUAQXJ0RST/nk= github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 h1:WndgpSW13S32VLQ3ugUxx2EnnWmgba1kCqPkd4Gk1yQ= github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979/go.mod h1:6Lkn+/zJilRMsKmbmG1RPoamiArC6HS73xbwRyp3UyI= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= From c922eba2d82503e6ef187d3d3f06572d4b0bacec Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 24 Sep 2022 21:28:09 +0100 Subject: [PATCH 049/293] Fix sending arguments to the admin socket in `yggdrasilctl` --- cmd/yggdrasilctl/main.go | 14 ++++++++------ src/admin/admin.go | 27 ++++++++++++++------------- src/core/nodeinfo.go | 8 ++++++-- 3 files changed, 28 insertions(+), 21 deletions(-) diff --git a/cmd/yggdrasilctl/main.go b/cmd/yggdrasilctl/main.go index 1e93b98f..f57ab79a 100644 --- a/cmd/yggdrasilctl/main.go +++ b/cmd/yggdrasilctl/main.go @@ -85,7 +85,7 @@ func run() int { encoder := json.NewEncoder(conn) send := &admin.AdminSocketRequest{} recv := &admin.AdminSocketResponse{} - + args := map[string]string{} for c, a := range cmdLineEnv.args { if c == 0 { if strings.HasPrefix(a, "-") { @@ -96,15 +96,17 @@ func run() int { send.Name = a continue } - tokens := strings.SplitN(a, "=", 1) + tokens := strings.SplitN(a, "=", 2) switch { case len(tokens) == 1: - panic("incomplete argument supplied") + logger.Println("Ignoring invalid argument:", a) default: - send.Arguments[tokens[0]] = tokens[1] + args[tokens[0]] = tokens[1] } } - + if send.Arguments, err = json.Marshal(args); err != nil { + panic(err) + } if err := encoder.Encode(&send); err != nil { panic(err) } @@ -139,7 +141,7 @@ func run() int { table.SetNoWhiteSpace(true) table.SetAutoWrapText(false) - switch strings.ToLower(recv.Request.Name) { + switch strings.ToLower(send.Name) { case "list": var resp admin.ListResponse if err := json.Unmarshal(recv.Response, &resp); err != nil { diff --git a/src/admin/admin.go b/src/admin/admin.go index 376f79a6..ca1d95ef 100644 --- a/src/admin/admin.go +++ b/src/admin/admin.go @@ -30,16 +30,16 @@ type AdminSocket struct { } type AdminSocketRequest struct { - Name string `json:"request"` - Arguments map[string]string `json:"arguments,omitempty"` - KeepAlive bool `json:"keepalive,omitempty"` + Name string `json:"request"` + Arguments json.RawMessage `json:"arguments,omitempty"` + KeepAlive bool `json:"keepalive,omitempty"` } type AdminSocketResponse struct { - Status string `json:"status"` - Error string `json:"error,omitempty"` - Request AdminSocketRequest `json:"request"` - Response json.RawMessage `json:"response"` + Status string `json:"status"` + Error string `json:"error,omitempty"` + Request json.RawMessage `json:"request"` + Response json.RawMessage `json:"response"` } type handler struct { @@ -298,25 +298,26 @@ func (a *AdminSocket) handleRequest(conn net.Conn) { for { var err error var buf json.RawMessage + var req AdminSocketRequest var resp AdminSocketResponse if err := func() error { if err = decoder.Decode(&buf); err != nil { return fmt.Errorf("Failed to find request") } - if err = json.Unmarshal(buf, &resp.Request); err != nil { + if err = json.Unmarshal(buf, &req); err != nil { return fmt.Errorf("Failed to unmarshal request") } - if resp.Request.Name == "" { + if req.Name == "" { return fmt.Errorf("No request specified") } - reqname := strings.ToLower(resp.Request.Name) + reqname := strings.ToLower(req.Name) handler, ok := a.handlers[reqname] if !ok { return fmt.Errorf("Unknown action '%s', try 'list' for help", reqname) } - res, err := handler.handler(buf) + res, err := handler.handler(req.Arguments) if err != nil { - return fmt.Errorf("Handler returned error: %w", err) + return err } if resp.Response, err = json.Marshal(res); err != nil { return fmt.Errorf("Failed to marshal response: %w", err) @@ -330,7 +331,7 @@ func (a *AdminSocket) handleRequest(conn net.Conn) { if err = encoder.Encode(resp); err != nil { a.log.Debugln("Encode error:", err) } - if !resp.Request.KeepAlive { + if !req.KeepAlive { break } else { continue diff --git a/src/core/nodeinfo.go b/src/core/nodeinfo.go index a6132ec2..e02c79c7 100644 --- a/src/core/nodeinfo.go +++ b/src/core/nodeinfo.go @@ -4,6 +4,7 @@ import ( "encoding/hex" "encoding/json" "errors" + "fmt" "runtime" "strings" "time" @@ -160,11 +161,14 @@ func (m *nodeinfo) nodeInfoAdminHandler(in json.RawMessage) (interface{}, error) if err := json.Unmarshal(in, &req); err != nil { return nil, err } + if req.Key == "" { + return nil, fmt.Errorf("No remote public key supplied") + } var key keyArray var kbs []byte var err error if kbs, err = hex.DecodeString(req.Key); err != nil { - return nil, err + return nil, fmt.Errorf("Failed to decode public key: %w", err) } copy(key[:], kbs) ch := make(chan []byte, 1) @@ -175,7 +179,7 @@ func (m *nodeinfo) nodeInfoAdminHandler(in json.RawMessage) (interface{}, error) defer timer.Stop() select { case <-timer.C: - return nil, errors.New("timeout") + return nil, errors.New("Timed out waiting for response") case info := <-ch: var msg json.RawMessage if err := msg.UnmarshalJSON(info); err != nil { From 7db934488ea271dea050d8c03c3d7b5ae559118f Mon Sep 17 00:00:00 2001 From: ehmry Date: Sun, 2 Oct 2022 06:35:43 -0500 Subject: [PATCH 050/293] Reimplement AddPeer and RemovePeer for admin socket (#951) * Reimplement AddPeer and RemovePeer for admin socket Fix #950 * Disconnect the peer on `removePeer` Co-authored-by: Neil Alexander --- src/admin/addpeer.go | 12 +++++ src/admin/admin.go | 28 +++++++++++ src/admin/removepeer.go | 12 +++++ src/core/api.go | 102 +++++++++++++++------------------------- src/core/core.go | 4 +- src/core/link.go | 10 ++-- src/core/options.go | 2 +- 7 files changed, 97 insertions(+), 73 deletions(-) create mode 100644 src/admin/addpeer.go create mode 100644 src/admin/removepeer.go diff --git a/src/admin/addpeer.go b/src/admin/addpeer.go new file mode 100644 index 00000000..f5e37ee9 --- /dev/null +++ b/src/admin/addpeer.go @@ -0,0 +1,12 @@ +package admin + +type AddPeerRequest struct { + Uri string `json:"uri"` + Sintf string `json:"interface,omitempty"` +} + +type AddPeerResponse struct{} + +func (a *AdminSocket) addPeerHandler(req *AddPeerRequest, res *AddPeerResponse) error { + return a.core.AddPeer(req.Uri, req.Sintf) +} diff --git a/src/admin/admin.go b/src/admin/admin.go index ca1d95ef..7f5f467e 100644 --- a/src/admin/admin.go +++ b/src/admin/admin.go @@ -174,6 +174,34 @@ func (a *AdminSocket) SetupAdminHandlers() { return res, nil }, ) + _ = a.AddHandler( + "addPeer", "Add a peer to the peer list", []string{"uri", "interface"}, + func(in json.RawMessage) (interface{}, error) { + req := &AddPeerRequest{} + res := &AddPeerResponse{} + if err := json.Unmarshal(in, &req); err != nil { + return nil, err + } + if err := a.addPeerHandler(req, res); err != nil { + return nil, err + } + return res, nil + }, + ) + _ = a.AddHandler( + "removePeer", "Remove a peer from the peer list", []string{"uri", "interface"}, + func(in json.RawMessage) (interface{}, error) { + req := &RemovePeerRequest{} + res := &RemovePeerResponse{} + if err := json.Unmarshal(in, &req); err != nil { + return nil, err + } + if err := a.removePeerHandler(req, res); err != nil { + return nil, err + } + return res, nil + }, + ) //_ = a.AddHandler("getNodeInfo", []string{"key"}, t.proto.nodeinfo.nodeInfoAdminHandler) //_ = a.AddHandler("debug_remoteGetSelf", []string{"key"}, t.proto.getSelfHandler) //_ = a.AddHandler("debug_remoteGetPeers", []string{"key"}, t.proto.getPeersHandler) diff --git a/src/admin/removepeer.go b/src/admin/removepeer.go new file mode 100644 index 00000000..145b8524 --- /dev/null +++ b/src/admin/removepeer.go @@ -0,0 +1,12 @@ +package admin + +type RemovePeerRequest struct { + Uri string `json:"uri"` + Sintf string `json:"interface,omitempty"` +} + +type RemovePeerResponse struct{} + +func (a *AdminSocket) removePeerHandler(req *RemovePeerRequest, res *RemovePeerResponse) error { + return a.core.RemovePeer(req.Uri, req.Sintf) +} diff --git a/src/core/api.go b/src/core/api.go index a67ecf3f..75d9d7b3 100644 --- a/src/core/api.go +++ b/src/core/api.go @@ -181,78 +181,49 @@ func (c *Core) SetLogger(log util.Logger) { } // AddPeer adds a peer. This should be specified in the peer URI format, e.g.: -// tcp://a.b.c.d:e -// socks://a.b.c.d:e/f.g.h.i:j +// +// tcp://a.b.c.d:e +// socks://a.b.c.d:e/f.g.h.i:j +// // This adds the peer to the peer list, so that they will be called again if the // connection drops. -/* -func (c *Core) AddPeer(addr string, sintf string) error { - if err := c.CallPeer(addr, sintf); err != nil { - // TODO: We maybe want this to write the peer to the persistent - // configuration even if a connection attempt fails, but first we'll need to - // move the code to check the peer URI so that we don't deliberately save a - // peer with a known bad URI. Loading peers from config should really do the - // same thing too but I don't think that happens today +func (c *Core) AddPeer(uri string, sourceInterface string) error { + u, err := url.Parse(uri) + if err != nil { return err } - c.config.Mutex.Lock() - defer c.config.Mutex.Unlock() - if sintf == "" { - for _, peer := range c.config.Current.Peers { - if peer == addr { - return errors.New("peer already added") - } - } - c.config.Current.Peers = append(c.config.Current.Peers, addr) - } else { - if _, ok := c.config.Current.InterfacePeers[sintf]; ok { - for _, peer := range c.config.Current.InterfacePeers[sintf] { - if peer == addr { - return errors.New("peer already added") - } - } - } - if _, ok := c.config.Current.InterfacePeers[sintf]; !ok { - c.config.Current.InterfacePeers[sintf] = []string{addr} - } else { - c.config.Current.InterfacePeers[sintf] = append(c.config.Current.InterfacePeers[sintf], addr) - } + info, err := c.links.call(u, sourceInterface) + if err != nil { + return err } - return nil -} -*/ - -/* -func (c *Core) RemovePeer(addr string, sintf string) error { - if sintf == "" { - for i, peer := range c.config.Current.Peers { - if peer == addr { - c.config.Current.Peers = append(c.config.Current.Peers[:i], c.config.Current.Peers[i+1:]...) - break - } - } - } else if _, ok := c.config.Current.InterfacePeers[sintf]; ok { - for i, peer := range c.config.Current.InterfacePeers[sintf] { - if peer == addr { - c.config.Current.InterfacePeers[sintf] = append(c.config.Current.InterfacePeers[sintf][:i], c.config.Current.InterfacePeers[sintf][i+1:]...) - break - } - } - } - - panic("TODO") // Get the net.Conn to this peer (if any) and close it - c.peers.Act(nil, func() { - ports := c.peers.ports - for _, peer := range ports { - if addr == peer.intf.name() { - c.peers._removePeer(peer) - } - } + phony.Block(c, func() { + c.config._peers[Peer{uri, sourceInterface}] = &info }) - return nil } -*/ + +// RemovePeer removes a peer. The peer should be specified in URI format, see AddPeer. +// The peer is not disconnected immediately. +func (c *Core) RemovePeer(uri string, sourceInterface string) error { + var err error + phony.Block(c, func() { + peer := Peer{uri, sourceInterface} + linkInfo, ok := c.config._peers[peer] + if !ok { + err = fmt.Errorf("peer not configured") + return + } + if ok && linkInfo != nil { + c.links.Act(nil, func() { + if link := c.links._links[*linkInfo]; link != nil { + _ = link.close() + } + }) + } + delete(c.config._peers, peer) + }) + return err +} // CallPeer calls a peer once. This should be specified in the peer URI format, // e.g.: @@ -263,7 +234,8 @@ func (c *Core) RemovePeer(addr string, sintf string) error { // This does not add the peer to the peer list, so if the connection drops, the // peer will not be called again automatically. func (c *Core) CallPeer(u *url.URL, sintf string) error { - return c.links.call(u, sintf) + _, err := c.links.call(u, sintf) + return err } func (c *Core) PublicKey() ed25519.PublicKey { diff --git a/src/core/core.go b/src/core/core.go index 5cfcc7a2..b8315416 100644 --- a/src/core/core.go +++ b/src/core/core.go @@ -36,7 +36,7 @@ type Core struct { log util.Logger addPeerTimer *time.Timer config struct { - _peers map[Peer]struct{} // configurable after startup + _peers map[Peer]*linkInfo // configurable after startup _listeners map[ListenAddress]struct{} // configurable after startup nodeinfo NodeInfo // immutable after startup nodeinfoPrivacy NodeInfoPrivacy // immutable after startup @@ -66,7 +66,7 @@ func New(secret ed25519.PrivateKey, logger util.Logger, opts ...SetupOption) (*C if c.PacketConn, err = iwe.NewPacketConn(c.secret); err != nil { return nil, fmt.Errorf("error creating encryption: %w", err) } - c.config._peers = map[Peer]struct{}{} + c.config._peers = map[Peer]*linkInfo{} c.config._listeners = map[ListenAddress]struct{}{} c.config._allowedPublicKeys = map[[32]byte]struct{}{} for _, opt := range opts { diff --git a/src/core/link.go b/src/core/link.go index 26e44669..963963a4 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -108,10 +108,10 @@ func (l *links) isConnectedTo(info linkInfo) bool { return isConnected } -func (l *links) call(u *url.URL, sintf string) error { +func (l *links) call(u *url.URL, sintf string) (linkInfo, error) { info := linkInfoFor(u.Scheme, sintf, u.Host) if l.isConnectedTo(info) { - return nil + return info, nil } options := linkOptions{ pinnedEd25519Keys: map[keyArray]struct{}{}, @@ -119,7 +119,7 @@ func (l *links) call(u *url.URL, sintf string) error { for _, pubkey := range u.Query()["key"] { sigPub, err := hex.DecodeString(pubkey) if err != nil { - return fmt.Errorf("pinned key contains invalid hex characters") + return info, fmt.Errorf("pinned key contains invalid hex characters") } var sigPubKey keyArray copy(sigPubKey[:], sigPub) @@ -172,9 +172,9 @@ func (l *links) call(u *url.URL, sintf string) error { }() default: - return errors.New("unknown call scheme: " + u.Scheme) + return info, errors.New("unknown call scheme: " + u.Scheme) } - return nil + return info, nil } func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { diff --git a/src/core/options.go b/src/core/options.go index 8009aa3b..66aa16ce 100644 --- a/src/core/options.go +++ b/src/core/options.go @@ -7,7 +7,7 @@ import ( func (c *Core) _applyOption(opt SetupOption) { switch v := opt.(type) { case Peer: - c.config._peers[v] = struct{}{} + c.config._peers[v] = nil case ListenAddress: c.config._listeners[v] = struct{}{} case NodeInfo: From 8cf76f841d3f613febdbd697f349ae2f5e31cd70 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 2 Oct 2022 12:36:51 +0100 Subject: [PATCH 051/293] Silence `already connected to this node` --- src/core/link.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/link.go b/src/core/link.go index 963963a4..860c3b83 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -219,7 +219,7 @@ func (intf *link) handler() error { // Don't connect to this link more than once. if intf.links.isConnectedTo(intf.info) { - return fmt.Errorf("already connected to this node") + return nil } // Mark the connection as in progress. From 428d2375da391b15ac9e2b525ad65351f5128d0c Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 2 Oct 2022 12:39:18 +0100 Subject: [PATCH 052/293] Don't allow configuring the same peer more than once --- src/core/api.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/core/api.go b/src/core/api.go index 75d9d7b3..dfc309d3 100644 --- a/src/core/api.go +++ b/src/core/api.go @@ -188,6 +188,13 @@ func (c *Core) SetLogger(log util.Logger) { // This adds the peer to the peer list, so that they will be called again if the // connection drops. func (c *Core) AddPeer(uri string, sourceInterface string) error { + var known bool + phony.Block(c, func() { + _, known = c.config._peers[Peer{uri, sourceInterface}] + }) + if known { + return fmt.Errorf("peer already configured") + } u, err := url.Parse(uri) if err != nil { return err From 962665189c7d77096b90ef5ea7b3c58293146d9e Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 2 Oct 2022 13:15:11 +0100 Subject: [PATCH 053/293] Tweaks to `yggdrasilctl` --- cmd/yggdrasilctl/main.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cmd/yggdrasilctl/main.go b/cmd/yggdrasilctl/main.go index f57ab79a..324550bd 100644 --- a/cmd/yggdrasilctl/main.go +++ b/cmd/yggdrasilctl/main.go @@ -174,16 +174,16 @@ func run() int { if err := json.Unmarshal(recv.Response, &resp); err != nil { panic(err) } - table.SetHeader([]string{"Port", "Public Key", "IP Address", "Peering URI", "Uptime", "RX", "TX"}) + table.SetHeader([]string{"Port", "Public Key", "IP Address", "Uptime", "RX", "TX", "URI"}) for _, peer := range resp.Peers { table.Append([]string{ fmt.Sprintf("%d", peer.Port), peer.PublicKey, peer.IPAddress, - peer.Remote, (time.Duration(peer.Uptime) * time.Second).String(), peer.RXBytes.String(), peer.TXBytes.String(), + peer.Remote, }) } table.Render() @@ -269,6 +269,8 @@ func run() int { } table.Render() + case "addpeer", "removepeer": + default: fmt.Println(string(recv.Response)) } From 69632bacb516e8fd7ded1fbb6860d3f224429f08 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 2 Oct 2022 13:20:39 +0100 Subject: [PATCH 054/293] Tidy up --- src/admin/admin.go | 5 ++--- src/config/config.go | 2 -- src/core/api.go | 18 ++++------------ src/core/core.go | 19 +++++++++++++---- src/core/debug.go | 1 - src/core/link.go | 7 +----- src/core/nodeinfo.go | 3 --- src/tun/tun.go | 6 +++--- src/util/util.go | 51 -------------------------------------------- 9 files changed, 25 insertions(+), 87 deletions(-) delete mode 100644 src/util/util.go diff --git a/src/admin/admin.go b/src/admin/admin.go index 7f5f467e..b24bf0de 100644 --- a/src/admin/admin.go +++ b/src/admin/admin.go @@ -13,14 +13,13 @@ import ( "time" "github.com/yggdrasil-network/yggdrasil-go/src/core" - "github.com/yggdrasil-network/yggdrasil-go/src/util" ) // TODO: Add authentication type AdminSocket struct { core *core.Core - log util.Logger + log core.Logger listener net.Listener handlers map[string]handler done chan struct{} @@ -72,7 +71,7 @@ func (a *AdminSocket) AddHandler(name, desc string, args []string, handlerfunc c } // Init runs the initial admin setup. -func New(c *core.Core, log util.Logger, opts ...SetupOption) (*AdminSocket, error) { +func New(c *core.Core, log core.Logger, opts ...SetupOption) (*AdminSocket, error) { a := &AdminSocket{ core: c, log: log, diff --git a/src/config/config.go b/src/config/config.go index c93699e1..5bdeec4b 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -19,14 +19,12 @@ package config import ( "crypto/ed25519" "encoding/hex" - "sync" ) // NodeConfig is the main configuration structure, containing configuration // options that are necessary for an Yggdrasil node to run. You will need to // supply one of these structs to the Yggdrasil core when starting a node. type NodeConfig struct { - sync.RWMutex `json:"-"` Peers []string `comment:"List of connection strings for outbound peer connections in URI format,\ne.g. tls://a.b.c.d:e or socks://a.b.c.d:e/f.g.h.i:j. These connections\nwill obey the operating system routing table, therefore you should\nuse this section when you may connect via different interfaces."` InterfacePeers map[string][]string `comment:"List of connection strings for outbound peer connections in URI format,\narranged by source interface, e.g. { \"eth0\": [ \"tls://a.b.c.d:e\" ] }.\nNote that SOCKS peerings will NOT be affected by this option and should\ngo in the \"Peers\" section instead."` Listen []string `comment:"Listen addresses for incoming connections. You will need to add\nlisteners in order to accept incoming peerings from non-local nodes.\nMulticast peer discovery will work regardless of any listeners set\nhere. Each listener should be specified in URI format as above, e.g.\ntls://0.0.0.0:0 or tls://[::]:0 to listen on all interfaces."` diff --git a/src/core/api.go b/src/core/api.go index dfc309d3..0fa6dd33 100644 --- a/src/core/api.go +++ b/src/core/api.go @@ -2,25 +2,15 @@ package core import ( "crypto/ed25519" + "encoding/json" "fmt" + "net" + "net/url" "sync/atomic" "time" - //"encoding/hex" - "encoding/json" - //"errors" - //"fmt" - "net" - "net/url" - - //"sort" - //"time" - "github.com/Arceliar/phony" "github.com/yggdrasil-network/yggdrasil-go/src/address" - "github.com/yggdrasil-network/yggdrasil-go/src/util" - //"github.com/yggdrasil-network/yggdrasil-go/src/crypto" - //"github.com/Arceliar/phony" ) type SelfInfo struct { @@ -176,7 +166,7 @@ func (c *Core) Subnet() net.IPNet { // may be useful if you want to redirect the output later. Note that this // expects a Logger from the github.com/gologme/log package and not from Go's // built-in log package. -func (c *Core) SetLogger(log util.Logger) { +func (c *Core) SetLogger(log Logger) { c.log = log } diff --git a/src/core/core.go b/src/core/core.go index b8315416..67f927a6 100644 --- a/src/core/core.go +++ b/src/core/core.go @@ -14,9 +14,7 @@ import ( "github.com/Arceliar/phony" "github.com/gologme/log" - "github.com/yggdrasil-network/yggdrasil-go/src/util" "github.com/yggdrasil-network/yggdrasil-go/src/version" - //"github.com/yggdrasil-network/yggdrasil-go/src/crypto" ) // The Core object represents the Yggdrasil node. You should create a Core @@ -33,7 +31,7 @@ type Core struct { public ed25519.PublicKey links links proto protoHandler - log util.Logger + log Logger addPeerTimer *time.Timer config struct { _peers map[Peer]*linkInfo // configurable after startup @@ -44,7 +42,7 @@ type Core struct { } } -func New(secret ed25519.PrivateKey, logger util.Logger, opts ...SetupOption) (*Core, error) { +func New(secret ed25519.PrivateKey, logger Logger, opts ...SetupOption) (*Core, error) { c := &Core{ log: logger, } @@ -193,3 +191,16 @@ func (c *Core) WriteTo(p []byte, addr net.Addr) (n int, err error) { } return } + +type Logger interface { + Printf(string, ...interface{}) + Println(...interface{}) + Infof(string, ...interface{}) + Infoln(...interface{}) + Warnf(string, ...interface{}) + Warnln(...interface{}) + Errorf(string, ...interface{}) + Errorln(...interface{}) + Debugf(string, ...interface{}) + Debugln(...interface{}) +} diff --git a/src/core/debug.go b/src/core/debug.go index ee1f1ed8..6085ceeb 100644 --- a/src/core/debug.go +++ b/src/core/debug.go @@ -5,7 +5,6 @@ package core import ( "fmt" - "net/http" _ "net/http/pprof" "os" diff --git a/src/core/link.go b/src/core/link.go index 860c3b83..b4515276 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -9,15 +9,11 @@ import ( "net" "net/url" "strings" - - //"sync/atomic" - "time" - "sync/atomic" + "time" "github.com/Arceliar/phony" "github.com/yggdrasil-network/yggdrasil-go/src/address" - //"github.com/Arceliar/phony" // TODO? use instead of mutexes ) type links struct { @@ -28,7 +24,6 @@ type links struct { unix *linkUNIX // UNIX interface support socks *linkSOCKS // SOCKS interface support _links map[linkInfo]*link // *link is nil if connection in progress - // TODO timeout (to remove from switch), read from config.ReadTimeout } // linkInfo is used as a map key diff --git a/src/core/nodeinfo.go b/src/core/nodeinfo.go index e02c79c7..bac0935b 100644 --- a/src/core/nodeinfo.go +++ b/src/core/nodeinfo.go @@ -11,9 +11,6 @@ import ( iwt "github.com/Arceliar/ironwood/types" "github.com/Arceliar/phony" - - //"github.com/yggdrasil-network/yggdrasil-go/src/crypto" - "github.com/yggdrasil-network/yggdrasil-go/src/version" ) diff --git a/src/tun/tun.go b/src/tun/tun.go index 0f7a70e3..ce1bd169 100644 --- a/src/tun/tun.go +++ b/src/tun/tun.go @@ -14,9 +14,9 @@ import ( "golang.zx2c4.com/wireguard/tun" "github.com/yggdrasil-network/yggdrasil-go/src/address" + "github.com/yggdrasil-network/yggdrasil-go/src/core" "github.com/yggdrasil-network/yggdrasil-go/src/defaults" "github.com/yggdrasil-network/yggdrasil-go/src/ipv6rwc" - "github.com/yggdrasil-network/yggdrasil-go/src/util" ) type MTU uint16 @@ -27,7 +27,7 @@ type MTU uint16 // calling yggdrasil.Start(). type TunAdapter struct { rwc *ipv6rwc.ReadWriteCloser - log util.Logger + log core.Logger addr address.Address subnet address.Subnet mtu uint64 @@ -90,7 +90,7 @@ func MaximumMTU() uint64 { // Init initialises the TUN module. You must have acquired a Listener from // the Yggdrasil core before this point and it must not be in use elsewhere. -func New(rwc *ipv6rwc.ReadWriteCloser, log util.Logger, opts ...SetupOption) (*TunAdapter, error) { +func New(rwc *ipv6rwc.ReadWriteCloser, log core.Logger, opts ...SetupOption) (*TunAdapter, error) { tun := &TunAdapter{ rwc: rwc, log: log, diff --git a/src/util/util.go b/src/util/util.go deleted file mode 100644 index e2e21464..00000000 --- a/src/util/util.go +++ /dev/null @@ -1,51 +0,0 @@ -// Package util contains miscellaneous utilities used by yggdrasil. -// In particular, this includes a crypto worker pool, Cancellation machinery, and a sync.Pool used to reuse []byte. -package util - -// These are misc. utility functions that didn't really fit anywhere else - -import ( - "time" -) - -// Any logger that satisfies this interface is suitable for Yggdrasil. -type Logger interface { - Printf(string, ...interface{}) - Println(...interface{}) - Infof(string, ...interface{}) - Infoln(...interface{}) - Warnf(string, ...interface{}) - Warnln(...interface{}) - Errorf(string, ...interface{}) - Errorln(...interface{}) - Debugf(string, ...interface{}) - Debugln(...interface{}) -} - -// TimerStop stops a timer and makes sure the channel is drained, returns true if the timer was stopped before firing. -func TimerStop(t *time.Timer) bool { - stopped := t.Stop() - select { - case <-t.C: - default: - } - return stopped -} - -// FuncTimeout runs the provided function in a separate goroutine, and returns true if the function finishes executing before the timeout passes, or false if the timeout passes. -// It includes no mechanism to stop the function if the timeout fires, so the user is expected to do so on their own (such as with a Cancellation or a context). -func FuncTimeout(timeout time.Duration, f func()) 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 - } -} From ee21c56e432dca0e439f3675ac58eda50ca4538e Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 15 Oct 2022 15:42:52 +0100 Subject: [PATCH 055/293] Fix setting nodeinfo (closes #954) --- cmd/yggdrasil/main.go | 5 +++- src/core/nodeinfo.go | 64 ++++++++++++++++--------------------------- 2 files changed, 28 insertions(+), 41 deletions(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index abce0cea..11bda9f2 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -290,7 +290,10 @@ func run(args yggArgs, ctx context.Context, done chan struct{}) { if err != nil { panic(err) } - options := []core.SetupOption{} + options := []core.SetupOption{ + core.NodeInfo(cfg.NodeInfo), + core.NodeInfoPrivacy(cfg.NodeInfoPrivacy), + } for _, addr := range cfg.Listen { options = append(options, core.ListenAddress(addr)) } diff --git a/src/core/nodeinfo.go b/src/core/nodeinfo.go index bac0935b..6f1f4be8 100644 --- a/src/core/nodeinfo.go +++ b/src/core/nodeinfo.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "runtime" - "strings" "time" iwt "github.com/Arceliar/ironwood/types" @@ -14,18 +13,15 @@ import ( "github.com/yggdrasil-network/yggdrasil-go/src/version" ) -// NodeInfoPayload represents a RequestNodeInfo response, in bytes. -type NodeInfoPayload []byte - type nodeinfo struct { phony.Inbox proto *protoHandler - myNodeInfo NodeInfoPayload + myNodeInfo json.RawMessage callbacks map[keyArray]nodeinfoCallback } type nodeinfoCallback struct { - call func(nodeinfo NodeInfoPayload) + call func(nodeinfo json.RawMessage) created time.Time } @@ -54,7 +50,7 @@ func (m *nodeinfo) _cleanup() { }) } -func (m *nodeinfo) _addCallback(sender keyArray, call func(nodeinfo NodeInfoPayload)) { +func (m *nodeinfo) _addCallback(sender keyArray, call func(nodeinfo json.RawMessage)) { m.callbacks[sender] = nodeinfoCallback{ created: time.Now(), call: call, @@ -62,67 +58,55 @@ func (m *nodeinfo) _addCallback(sender keyArray, call func(nodeinfo NodeInfoPayl } // Handles the callback, if there is one -func (m *nodeinfo) _callback(sender keyArray, nodeinfo NodeInfoPayload) { +func (m *nodeinfo) _callback(sender keyArray, nodeinfo json.RawMessage) { if callback, ok := m.callbacks[sender]; ok { callback.call(nodeinfo) delete(m.callbacks, sender) } } -func (m *nodeinfo) _getNodeInfo() NodeInfoPayload { +func (m *nodeinfo) _getNodeInfo() json.RawMessage { return m.myNodeInfo } // Set the current node's nodeinfo -func (m *nodeinfo) setNodeInfo(given interface{}, privacy bool) (err error) { +func (m *nodeinfo) setNodeInfo(given map[string]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(), - "buildplatform": runtime.GOOS, - "buildarch": runtime.GOARCH, +func (m *nodeinfo) _setNodeInfo(given map[string]interface{}, privacy bool) error { + newnodeinfo := make(map[string]interface{}, len(given)) + for k, v := range given { + newnodeinfo[k] = v } - newnodeinfo := make(map[string]interface{}) if !privacy { - for k, v := range defaults { - newnodeinfo[k] = v - } - } - if nodeinfomap, ok := given.(map[string]interface{}); ok { - for key, value := range nodeinfomap { - if _, ok := defaults[key]; ok { - if strvalue, strok := value.(string); strok && strings.EqualFold(strvalue, "null") || value == nil { - delete(newnodeinfo, key) - } - continue - } - newnodeinfo[key] = value - } + newnodeinfo["buildname"] = version.BuildName() + newnodeinfo["buildversion"] = version.BuildVersion() + newnodeinfo["buildplatform"] = runtime.GOOS + newnodeinfo["buildarch"] = runtime.GOARCH } newjson, err := json.Marshal(newnodeinfo) - if err == nil { - if len(newjson) > 16384 { - return errors.New("NodeInfo exceeds max length of 16384 bytes") - } + switch { + case err != nil: + return fmt.Errorf("NodeInfo marshalling failed: %w", err) + case len(newjson) > 16384: + return fmt.Errorf("NodeInfo exceeds max length of 16384 bytes") + default: m.myNodeInfo = newjson return nil } - return err } -func (m *nodeinfo) sendReq(from phony.Actor, key keyArray, callback func(nodeinfo NodeInfoPayload)) { +func (m *nodeinfo) sendReq(from phony.Actor, key keyArray, callback func(nodeinfo json.RawMessage)) { m.Act(from, func() { m._sendReq(key, callback) }) } -func (m *nodeinfo) _sendReq(key keyArray, callback func(nodeinfo NodeInfoPayload)) { +func (m *nodeinfo) _sendReq(key keyArray, callback func(nodeinfo json.RawMessage)) { if callback != nil { m._addCallback(key, callback) } @@ -135,7 +119,7 @@ func (m *nodeinfo) handleReq(from phony.Actor, key keyArray) { }) } -func (m *nodeinfo) handleRes(from phony.Actor, key keyArray, info NodeInfoPayload) { +func (m *nodeinfo) handleRes(from phony.Actor, key keyArray, info json.RawMessage) { m.Act(from, func() { m._callback(key, info) }) @@ -169,7 +153,7 @@ func (m *nodeinfo) nodeInfoAdminHandler(in json.RawMessage) (interface{}, error) } copy(key[:], kbs) ch := make(chan []byte, 1) - m.sendReq(nil, key, func(info NodeInfoPayload) { + m.sendReq(nil, key, func(info json.RawMessage) { ch <- info }) timer := time.NewTimer(6 * time.Second) From 69782ad87bff8838e934c6e0235fd6aef431b3a3 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 15 Oct 2022 16:07:32 +0100 Subject: [PATCH 056/293] Improve shutdown behaviour (fixes #891) --- cmd/yggdrasil/main.go | 50 ++++++++++++++++++------------------------- 1 file changed, 21 insertions(+), 29 deletions(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 11bda9f2..3d5eab97 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -14,6 +14,7 @@ import ( "os/signal" "regexp" "strings" + "sync" "syscall" "golang.org/x/text/encoding/unicode" @@ -183,8 +184,7 @@ func getArgs() yggArgs { } // The main function is responsible for configuring and starting Yggdrasil. -func run(args yggArgs, ctx context.Context, done chan struct{}) { - defer close(done) +func run(args yggArgs, ctx context.Context) { // Create a new logger that logs output to stdout. var logger *log.Logger switch args.logto { @@ -371,14 +371,11 @@ func run(args yggArgs, ctx context.Context, done chan struct{}) { logger.Infof("Your public key is %s", hex.EncodeToString(public[:])) 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. - <-ctx.Done() - // Capture the service being stopped on Windows. - minwinsvc.SetOnExit(n.shutdown) - n.shutdown() -} -func (n *node) shutdown() { + // Block until we are told to shut down. + <-ctx.Done() + + // Shut down the node. _ = n.admin.Stop() _ = n.multicast.Stop() _ = n.tun.Stop() @@ -387,24 +384,19 @@ func (n *node) shutdown() { func main() { args := getArgs() - hup := make(chan os.Signal, 1) - //signal.Notify(hup, os.Interrupt, syscall.SIGHUP) - term := make(chan os.Signal, 1) - signal.Notify(term, os.Interrupt, syscall.SIGTERM) - for { - done := make(chan struct{}) - ctx, cancel := context.WithCancel(context.Background()) - go run(args, ctx, done) - select { - case <-hup: - cancel() - <-done - case <-term: - cancel() - <-done - return - case <-done: - return - } - } + + // Catch interrupts from the operating system to exit gracefully. + ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) + + // Capture the service being stopped on Windows. + minwinsvc.SetOnExit(cancel) + + // Start the node, block and then wait for it to shut down. + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + run(args, ctx) + }() + wg.Wait() } From 8ce7c86383f619222134aa420e1713f49e13ec42 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 15 Oct 2022 17:45:41 +0100 Subject: [PATCH 057/293] Update some dependencies --- go.mod | 12 ++++++------ go.sum | 15 +++++++++++++++ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 0a0f8fa6..4e73edea 100644 --- a/go.mod +++ b/go.mod @@ -9,13 +9,13 @@ require ( github.com/gologme/log v1.2.0 github.com/hashicorp/go-syslog v1.0.0 github.com/hjson/hjson-go v3.1.0+incompatible - github.com/kardianos/minwinsvc v1.0.0 + github.com/kardianos/minwinsvc v1.0.2 github.com/mitchellh/mapstructure v1.4.1 github.com/vishvananda/netlink v1.1.0 - golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105 - golang.org/x/net v0.0.0-20220722155237-a158d28d115b - golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f - golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b + golang.org/x/mobile v0.0.0-20221012134814-c746ac228303 + golang.org/x/net v0.0.0-20221014081412-f15817d10f9b + golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43 + golang.org/x/text v0.3.8 golang.zx2c4.com/wireguard v0.0.0-20211017052713-f87e87af0d9a golang.zx2c4.com/wireguard/windows v0.4.12 ) @@ -23,7 +23,7 @@ require ( require ( github.com/mattn/go-colorable v0.1.8 // indirect github.com/rivo/uniseg v0.2.0 // indirect - golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect + golang.org/x/crypto v0.0.0-20221012134737-56aed061732a // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect golang.org/x/tools v0.1.12 // indirect ) diff --git a/go.sum b/go.sum index e18dafa8..9e0b7458 100644 --- a/go.sum +++ b/go.sum @@ -19,6 +19,8 @@ github.com/hjson/hjson-go v3.1.0+incompatible h1:DY/9yE8ey8Zv22bY+mHV1uk2yRy0h8t github.com/hjson/hjson-go v3.1.0+incompatible/go.mod h1:qsetwF8NlsTsOTwZTApNlTCerV+b2GjYRRcIk4JMFio= github.com/kardianos/minwinsvc v1.0.0 h1:+JfAi8IBJna0jY2dJGZqi7o15z13JelFIklJCAENALA= github.com/kardianos/minwinsvc v1.0.0/go.mod h1:Bgd0oc+D0Qo3bBytmNtyRKVlp85dAloLKhfxanPFFRc= +github.com/kardianos/minwinsvc v1.0.2 h1:JmZKFJQrmTGa/WiW+vkJXKmfzdjabuEW4Tirj5lLdR0= +github.com/kardianos/minwinsvc v1.0.2/go.mod h1:LUZNYhNmxujx2tR7FbdxqYJ9XDDoCd3MQcl1o//FWl4= github.com/lxn/walk v0.0.0-20210112085537-c389da54e794/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ= github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk= github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= @@ -50,12 +52,16 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20221012134737-56aed061732a h1:NmSIgad6KjE6VvHciPZuNRTKxGhlPfD6OA87W/PLkqg= +golang.org/x/crypto v0.0.0-20221012134737-56aed061732a/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105 h1:3vUV5x5+3LfQbgk7paCM6INOaJG9xXQbn79xoNkwfIk= golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ= +golang.org/x/mobile v0.0.0-20221012134814-c746ac228303 h1:K4fp1rDuJBz0FCPAWzIJwnzwNEM7S6yobdZzMrZ/Zws= +golang.org/x/mobile v0.0.0-20221012134814-c746ac228303/go.mod h1:M32cGdzp91A8Ex9qQtyZinr19EYxzkFqDjW2oyHzTDQ= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= @@ -69,9 +75,12 @@ golang.org/x/net v0.0.0-20210927181540-4e4d966f7476/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211011170408-caeb26a5c8c0/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20221014081412-f15817d10f9b h1:tvrvnPFcdzp294diPnrdZZZ8XUt2Tyj7svb7X52iDuU= +golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -90,6 +99,10 @@ golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43 h1:OK7RB6t2WQX54srQQYSXMW8dF5C6/8+oA/s5QBmmto4= +golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -98,6 +111,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b h1:NXqSWXSRUSCaFuvitrWtU169I3876zRTalMRbfd6LL0= golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0= +golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= From b8a2d9f1258635f17405ecd5d7786bcf1617bd8a Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 18 Oct 2022 23:04:06 +0100 Subject: [PATCH 058/293] Version 0.4.5 (#957) * Version 0.4.5 changelog * Update changelog --- CHANGELOG.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 34001548..fc965e99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,26 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - in case of vulnerabilities. --> +## [0.4.5] - 2022-10-15 + +### Added + +- Support for peering over UNIX sockets is now available, by configuring `Listen` and peering URIs in the `unix:///path/to/socket.sock` format + +### Changed + +- `yggdrasilctl` has been refactored and now has cleaner output +- It is now possible to `addPeer` and `removePeer` using the admin socket again +- The `getSessions` admin socket call reports number of bytes received and transmitted again +- The link setup code has been refactored, making it easier to support new peering types in the future +- Yggdrasil now maintains configuration internally, rather than relying on a shared and potentially mutable structure + +### Fixed + +- Tracking information about expired root nodes has been fixed, which should hopefully resolve issues with reparenting and connection failures when the root node disappears +- A bug in the mobile framework code which caused a crash on Android when multicast failed to set up has been fixed +- Yggdrasil should now shut down gracefully and clean up correctly when running as a Windows service + ## [0.4.4] - 2022-07-07 ### Fixed From 81839ad50d11ea0cf6d44f2eb5841022a38a1aa6 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Fri, 21 Oct 2022 19:49:15 +0100 Subject: [PATCH 059/293] Fix `InterfacePeers` --- src/core/link_tcp.go | 13 +++++-------- src/core/link_tls.go | 3 +-- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/core/link_tcp.go b/src/core/link_tcp.go index c5a73c9e..30749983 100644 --- a/src/core/link_tcp.go +++ b/src/core/link_tcp.go @@ -39,8 +39,7 @@ func (l *linkTCP) dial(url *url.URL, options linkOptions, sintf string) error { if err != nil { return err } - addr.Zone = sintf - dialer, err := l.dialerFor(addr.String(), sintf) + dialer, err := l.dialerFor(addr, sintf) if err != nil { return err } @@ -121,13 +120,11 @@ func (l *linkTCP) getAddr() *net.TCPAddr { return addr } -func (l *linkTCP) dialerFor(saddr, sintf string) (*net.Dialer, error) { - dst, err := net.ResolveTCPAddr("tcp", saddr) - if err != nil { - return nil, err - } +func (l *linkTCP) dialerFor(dst *net.TCPAddr, sintf string) (*net.Dialer, error) { if dst.IP.IsLinkLocalUnicast() { - dst.Zone = sintf + if sintf != "" { + dst.Zone = sintf + } if dst.Zone == "" { return nil, fmt.Errorf("link-local address requires a zone") } diff --git a/src/core/link_tls.go b/src/core/link_tls.go index 1e932b66..2dc2daf1 100644 --- a/src/core/link_tls.go +++ b/src/core/link_tls.go @@ -55,8 +55,7 @@ func (l *linkTLS) dial(url *url.URL, options linkOptions, sintf, sni string) err if err != nil { return err } - addr.Zone = sintf - dialer, err := l.tcp.dialerFor(addr.String(), sintf) + dialer, err := l.tcp.dialerFor(addr, sintf) if err != nil { return err } From 22caddef63b1e46a4fc6e8c9a93e425b391fa723 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Fri, 21 Oct 2022 19:49:49 +0100 Subject: [PATCH 060/293] Don't log `duplicate connection attempt` --- src/core/link_socks.go | 2 +- src/core/link_tcp.go | 2 +- src/core/link_tls.go | 2 +- src/core/link_unix.go | 3 +-- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/core/link_socks.go b/src/core/link_socks.go index ad5b8c98..036de992 100644 --- a/src/core/link_socks.go +++ b/src/core/link_socks.go @@ -23,7 +23,7 @@ func (l *links) newLinkSOCKS() *linkSOCKS { func (l *linkSOCKS) dial(url *url.URL, options linkOptions) error { info := linkInfoFor("socks", "", url.Path) if l.links.isConnectedTo(info) { - return fmt.Errorf("duplicate connection attempt") + return nil } proxyAuth := &proxy.Auth{} proxyAuth.User = url.User.Username() diff --git a/src/core/link_tcp.go b/src/core/link_tcp.go index 30749983..6a04bb35 100644 --- a/src/core/link_tcp.go +++ b/src/core/link_tcp.go @@ -33,7 +33,7 @@ func (l *links) newLinkTCP() *linkTCP { func (l *linkTCP) dial(url *url.URL, options linkOptions, sintf string) error { info := linkInfoFor("tcp", sintf, strings.SplitN(url.Host, "%", 2)[0]) if l.links.isConnectedTo(info) { - return fmt.Errorf("duplicate connection attempt") + return nil } addr, err := net.ResolveTCPAddr("tcp", url.Host) if err != nil { diff --git a/src/core/link_tls.go b/src/core/link_tls.go index 2dc2daf1..bc39b6c0 100644 --- a/src/core/link_tls.go +++ b/src/core/link_tls.go @@ -49,7 +49,7 @@ func (l *links) newLinkTLS(tcp *linkTCP) *linkTLS { func (l *linkTLS) dial(url *url.URL, options linkOptions, sintf, sni string) error { info := linkInfoFor("tls", sintf, strings.SplitN(url.Host, "%", 2)[0]) if l.links.isConnectedTo(info) { - return fmt.Errorf("duplicate connection attempt") + return nil } addr, err := net.ResolveTCPAddr("tcp", url.Host) if err != nil { diff --git a/src/core/link_unix.go b/src/core/link_unix.go index e71f9362..2d1b9398 100644 --- a/src/core/link_unix.go +++ b/src/core/link_unix.go @@ -2,7 +2,6 @@ package core import ( "context" - "fmt" "net" "net/url" "time" @@ -36,7 +35,7 @@ func (l *links) newLinkUNIX() *linkUNIX { func (l *linkUNIX) dial(url *url.URL, options linkOptions, _ string) error { info := linkInfoFor("unix", "", url.Path) if l.links.isConnectedTo(info) { - return fmt.Errorf("duplicate connection attempt") + return nil } addr, err := net.ResolveUnixAddr("unix", url.Path) if err != nil { From c55611a478bc725a462db241c56758eb613cd4cf Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 22 Oct 2022 14:56:11 +0100 Subject: [PATCH 061/293] Tweak logging for connections --- src/core/link.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/core/link.go b/src/core/link.go index b4515276..f8e5be22 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -286,7 +286,7 @@ func (intf *link) handler() error { } } if intf.incoming && !intf.force && !isallowed { - intf.links.core.log.Warnf("%s connection from %s forbidden: AllowedEncryptionPublicKeys does not contain key %s", + intf.links.core.log.Warnf("%s connection from %s forbidden: AllowedPublicKeys does not contain key %s", strings.ToUpper(intf.info.linkType), intf.info.remote, hex.EncodeToString(meta.key)) _ = intf.close() return fmt.Errorf("forbidden connection") @@ -302,15 +302,15 @@ func (intf *link) handler() error { intf.links.core.log.Infof("Connected %s: %s, source %s", strings.ToUpper(intf.info.linkType), remoteStr, localStr) - // TODO don't report an error if it's just a 'use of closed network connection' - if err = intf.links.core.HandleConn(meta.key, intf.conn); err != nil && err != io.EOF { - intf.links.core.log.Infof("Disconnected %s: %s, source %s; error: %s", - strings.ToUpper(intf.info.linkType), remoteStr, localStr, err) - } else { + err = intf.links.core.HandleConn(meta.key, intf.conn) + switch err { + case io.EOF, net.ErrClosed, nil: intf.links.core.log.Infof("Disconnected %s: %s, source %s", strings.ToUpper(intf.info.linkType), remoteStr, localStr) + default: + intf.links.core.log.Infof("Disconnected %s: %s, source %s; error: %s", + strings.ToUpper(intf.info.linkType), remoteStr, localStr, err) } - return nil } From 0a1a155e66ace9ee30841348a3eb1cdbafca2264 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 22 Oct 2022 14:56:29 +0100 Subject: [PATCH 062/293] Use `SO_REUSEADDR` instead of `SO_REUSEPORT` on Linux --- src/multicast/multicast_unix.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/multicast/multicast_unix.go b/src/multicast/multicast_unix.go index c59d876b..08230735 100644 --- a/src/multicast/multicast_unix.go +++ b/src/multicast/multicast_unix.go @@ -15,15 +15,19 @@ func (m *Multicast) _multicastStarted() { func (m *Multicast) multicastReuse(network string, address string, c syscall.RawConn) error { var control error - var reuseport error + var reuseaddr error control = c.Control(func(fd uintptr) { - reuseport = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1) + // Previously we used SO_REUSEPORT here, but that meant that machines running + // Yggdrasil nodes as different users would inevitably fail with EADDRINUSE. + // The behaviour for multicast is similar with both, so we'll use SO_REUSEADDR + // instead. + reuseaddr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1) }) switch { - case reuseport != nil: - return reuseport + case reuseaddr != nil: + return reuseaddr default: return control } From 63c4cb5c211aeca37bbe62534bb3675db03a96f6 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 22 Oct 2022 15:47:09 +0100 Subject: [PATCH 063/293] Fix reporting name for TCP --- src/core/link_tcp.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/link_tcp.go b/src/core/link_tcp.go index 6a04bb35..a388fcdb 100644 --- a/src/core/link_tcp.go +++ b/src/core/link_tcp.go @@ -82,7 +82,7 @@ func (l *linkTCP) listen(url *url.URL, sintf string) (*Listener, error) { break } addr := conn.RemoteAddr().(*net.TCPAddr) - name := fmt.Sprintf("tls://%s", addr) + name := fmt.Sprintf("tcp://%s", addr) info := linkInfoFor("tcp", sintf, strings.SplitN(addr.IP.String(), "%", 2)[0]) if err = l.handler(name, info, conn, linkOptions{}, true); err != nil { l.core.log.Errorln("Failed to create inbound link:", err) From d66b3ffb7ae67b7996033acf5e54a9174e8856bd Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 22 Oct 2022 16:23:25 +0100 Subject: [PATCH 064/293] Always allow link-local peerings again --- src/core/link.go | 27 ++++++++++++--------------- src/core/link_tcp.go | 8 ++++---- src/core/link_tls.go | 8 ++++---- 3 files changed, 20 insertions(+), 23 deletions(-) diff --git a/src/core/link.go b/src/core/link.go index f8e5be22..e822acaf 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -272,8 +272,7 @@ func (intf *link) handler() error { var key keyArray copy(key[:], meta.key) if _, allowed := pinned[key]; !allowed { - intf.links.core.log.Errorf("Failed to connect to node: %q sent ed25519 key that does not match pinned keys", intf.name()) - return fmt.Errorf("failed to connect: host sent ed25519 key that does not match pinned keys") + return fmt.Errorf("node public key that does not match pinned keys") } } // Check if we're authorized to connect to this key / IP @@ -286,30 +285,32 @@ func (intf *link) handler() error { } } if intf.incoming && !intf.force && !isallowed { - intf.links.core.log.Warnf("%s connection from %s forbidden: AllowedPublicKeys does not contain key %s", - strings.ToUpper(intf.info.linkType), intf.info.remote, hex.EncodeToString(meta.key)) _ = intf.close() - return fmt.Errorf("forbidden connection") + return fmt.Errorf("node public key %q is not in AllowedPublicKeys", hex.EncodeToString(meta.key)) } phony.Block(intf.links, func() { intf.links._links[intf.info] = intf }) + dir := "outbound" + if intf.incoming { + dir = "inbound" + } remoteAddr := net.IP(address.AddrForKey(meta.key)[:]).String() remoteStr := fmt.Sprintf("%s@%s", remoteAddr, intf.info.remote) localStr := intf.conn.LocalAddr() - intf.links.core.log.Infof("Connected %s: %s, source %s", - strings.ToUpper(intf.info.linkType), remoteStr, localStr) + intf.links.core.log.Infof("Connected %s %s: %s, source %s", + dir, strings.ToUpper(intf.info.linkType), remoteStr, localStr) err = intf.links.core.HandleConn(meta.key, intf.conn) switch err { case io.EOF, net.ErrClosed, nil: - intf.links.core.log.Infof("Disconnected %s: %s, source %s", - strings.ToUpper(intf.info.linkType), remoteStr, localStr) + intf.links.core.log.Infof("Disconnected %s %s: %s, source %s", + dir, strings.ToUpper(intf.info.linkType), remoteStr, localStr) default: - intf.links.core.log.Infof("Disconnected %s: %s, source %s; error: %s", - strings.ToUpper(intf.info.linkType), remoteStr, localStr, err) + intf.links.core.log.Infof("Disconnected %s %s: %s, source %s; error: %s", + dir, strings.ToUpper(intf.info.linkType), remoteStr, localStr, err) } return nil } @@ -318,10 +319,6 @@ func (intf *link) close() error { return intf.conn.Close() } -func (intf *link) name() string { - return intf.lname -} - func linkInfoFor(linkType, sintf, remote string) linkInfo { if h, _, err := net.SplitHostPort(remote); err == nil { remote = h diff --git a/src/core/link_tcp.go b/src/core/link_tcp.go index a388fcdb..986eda30 100644 --- a/src/core/link_tcp.go +++ b/src/core/link_tcp.go @@ -47,7 +47,7 @@ func (l *linkTCP) dial(url *url.URL, options linkOptions, sintf string) error { if err != nil { return err } - return l.handler(url.String(), info, conn, options, false) + return l.handler(url.String(), info, conn, options, false, false) } func (l *linkTCP) listen(url *url.URL, sintf string) (*Listener, error) { @@ -84,7 +84,7 @@ func (l *linkTCP) listen(url *url.URL, sintf string) (*Listener, error) { addr := conn.RemoteAddr().(*net.TCPAddr) name := fmt.Sprintf("tcp://%s", addr) info := linkInfoFor("tcp", sintf, strings.SplitN(addr.IP.String(), "%", 2)[0]) - if err = l.handler(name, info, conn, linkOptions{}, true); err != nil { + if err = l.handler(name, info, conn, linkOptions{}, true, addr.IP.IsLinkLocalUnicast()); err != nil { l.core.log.Errorln("Failed to create inbound link:", err) } } @@ -95,13 +95,13 @@ func (l *linkTCP) listen(url *url.URL, sintf string) (*Listener, error) { return entry, nil } -func (l *linkTCP) handler(name string, info linkInfo, conn net.Conn, options linkOptions, incoming bool) error { +func (l *linkTCP) handler(name string, info linkInfo, conn net.Conn, options linkOptions, incoming, force bool) error { return l.links.create( conn, // connection name, // connection name info, // connection info incoming, // not incoming - false, // not forced + force, // not forced options, // connection options ) } diff --git a/src/core/link_tls.go b/src/core/link_tls.go index bc39b6c0..9e7dda9b 100644 --- a/src/core/link_tls.go +++ b/src/core/link_tls.go @@ -69,7 +69,7 @@ func (l *linkTLS) dial(url *url.URL, options linkOptions, sintf, sni string) err if err != nil { return err } - return l.handler(url.String(), info, conn, options, false) + return l.handler(url.String(), info, conn, options, false, false) } func (l *linkTLS) listen(url *url.URL, sintf string) (*Listener, error) { @@ -107,7 +107,7 @@ func (l *linkTLS) listen(url *url.URL, sintf string) (*Listener, error) { addr := conn.RemoteAddr().(*net.TCPAddr) name := fmt.Sprintf("tls://%s", addr) info := linkInfoFor("tls", sintf, strings.SplitN(addr.IP.String(), "%", 2)[0]) - if err = l.handler(name, info, conn, linkOptions{}, true); err != nil { + if err = l.handler(name, info, conn, linkOptions{}, true, addr.IP.IsLinkLocalUnicast()); err != nil { l.core.log.Errorln("Failed to create inbound link:", err) } } @@ -165,6 +165,6 @@ func (l *linkTLS) generateConfig() (*tls.Config, error) { }, nil } -func (l *linkTLS) handler(name string, info linkInfo, conn net.Conn, options linkOptions, incoming bool) error { - return l.tcp.handler(name, info, conn, options, incoming) +func (l *linkTLS) handler(name string, info linkInfo, conn net.Conn, options linkOptions, incoming, force bool) error { + return l.tcp.handler(name, info, conn, options, incoming, force) } From 8fe1c41295e568df75d89cf2226f0260f32f7e9c Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 22 Oct 2022 16:59:25 +0100 Subject: [PATCH 065/293] Don't reject multiple genuine links from the same host --- src/core/link.go | 3 --- src/core/link_tcp.go | 13 +++++++------ src/core/link_tls.go | 13 +++++++------ 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/core/link.go b/src/core/link.go index e822acaf..67184625 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -320,9 +320,6 @@ func (intf *link) close() error { } func linkInfoFor(linkType, sintf, remote string) linkInfo { - if h, _, err := net.SplitHostPort(remote); err == nil { - remote = h - } return linkInfo{ linkType: linkType, local: sintf, diff --git a/src/core/link_tcp.go b/src/core/link_tcp.go index 986eda30..ee0dd001 100644 --- a/src/core/link_tcp.go +++ b/src/core/link_tcp.go @@ -31,14 +31,14 @@ func (l *links) newLinkTCP() *linkTCP { } func (l *linkTCP) dial(url *url.URL, options linkOptions, sintf string) error { - info := linkInfoFor("tcp", sintf, strings.SplitN(url.Host, "%", 2)[0]) - if l.links.isConnectedTo(info) { - return nil - } addr, err := net.ResolveTCPAddr("tcp", url.Host) if err != nil { return err } + info := linkInfoFor("tcp", sintf, addr.String()) + if l.links.isConnectedTo(info) { + return nil + } dialer, err := l.dialerFor(addr, sintf) if err != nil { return err @@ -47,7 +47,8 @@ func (l *linkTCP) dial(url *url.URL, options linkOptions, sintf string) error { if err != nil { return err } - return l.handler(url.String(), info, conn, options, false, false) + uri := strings.TrimRight(strings.SplitN(url.String(), "?", 2)[0], "/") + return l.handler(uri, info, conn, options, false, false) } func (l *linkTCP) listen(url *url.URL, sintf string) (*Listener, error) { @@ -83,7 +84,7 @@ func (l *linkTCP) listen(url *url.URL, sintf string) (*Listener, error) { } addr := conn.RemoteAddr().(*net.TCPAddr) name := fmt.Sprintf("tcp://%s", addr) - info := linkInfoFor("tcp", sintf, strings.SplitN(addr.IP.String(), "%", 2)[0]) + info := linkInfoFor("tcp", sintf, addr.String()) if err = l.handler(name, info, conn, linkOptions{}, true, addr.IP.IsLinkLocalUnicast()); err != nil { l.core.log.Errorln("Failed to create inbound link:", err) } diff --git a/src/core/link_tls.go b/src/core/link_tls.go index 9e7dda9b..ee3363ec 100644 --- a/src/core/link_tls.go +++ b/src/core/link_tls.go @@ -47,14 +47,14 @@ func (l *links) newLinkTLS(tcp *linkTCP) *linkTLS { } func (l *linkTLS) dial(url *url.URL, options linkOptions, sintf, sni string) error { - info := linkInfoFor("tls", sintf, strings.SplitN(url.Host, "%", 2)[0]) - if l.links.isConnectedTo(info) { - return nil - } addr, err := net.ResolveTCPAddr("tcp", url.Host) if err != nil { return err } + info := linkInfoFor("tls", sintf, addr.String()) + if l.links.isConnectedTo(info) { + return nil + } dialer, err := l.tcp.dialerFor(addr, sintf) if err != nil { return err @@ -69,7 +69,8 @@ func (l *linkTLS) dial(url *url.URL, options linkOptions, sintf, sni string) err if err != nil { return err } - return l.handler(url.String(), info, conn, options, false, false) + uri := strings.TrimRight(strings.SplitN(url.String(), "?", 2)[0], "/") + return l.handler(uri, info, conn, options, false, false) } func (l *linkTLS) listen(url *url.URL, sintf string) (*Listener, error) { @@ -106,7 +107,7 @@ func (l *linkTLS) listen(url *url.URL, sintf string) (*Listener, error) { } addr := conn.RemoteAddr().(*net.TCPAddr) name := fmt.Sprintf("tls://%s", addr) - info := linkInfoFor("tls", sintf, strings.SplitN(addr.IP.String(), "%", 2)[0]) + info := linkInfoFor("tls", sintf, addr.String()) if err = l.handler(name, info, conn, linkOptions{}, true, addr.IP.IsLinkLocalUnicast()); err != nil { l.core.log.Errorln("Failed to create inbound link:", err) } From 35ea66d651d3f186d8c33ede4568fd28a18bdc58 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 22 Oct 2022 17:45:09 +0100 Subject: [PATCH 066/293] Varying connection check strictness based on scope --- src/core/link_tcp.go | 30 ++++++++++++++++++++++-------- src/core/link_tls.go | 17 +++++++++-------- 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/src/core/link_tcp.go b/src/core/link_tcp.go index ee0dd001..a8f437e9 100644 --- a/src/core/link_tcp.go +++ b/src/core/link_tcp.go @@ -35,14 +35,14 @@ func (l *linkTCP) dial(url *url.URL, options linkOptions, sintf string) error { if err != nil { return err } - info := linkInfoFor("tcp", sintf, addr.String()) - if l.links.isConnectedTo(info) { - return nil - } dialer, err := l.dialerFor(addr, sintf) if err != nil { return err } + info := linkInfoFor("tcp", sintf, tcpIDFor(dialer.LocalAddr, addr)) + if l.links.isConnectedTo(info) { + return nil + } conn, err := dialer.DialContext(l.core.ctx, "tcp", addr.String()) if err != nil { return err @@ -82,10 +82,11 @@ func (l *linkTCP) listen(url *url.URL, sintf string) (*Listener, error) { cancel() break } - addr := conn.RemoteAddr().(*net.TCPAddr) - name := fmt.Sprintf("tcp://%s", addr) - info := linkInfoFor("tcp", sintf, addr.String()) - if err = l.handler(name, info, conn, linkOptions{}, true, addr.IP.IsLinkLocalUnicast()); err != nil { + laddr := conn.LocalAddr().(*net.TCPAddr) + raddr := conn.RemoteAddr().(*net.TCPAddr) + name := fmt.Sprintf("tcp://%s", raddr) + info := linkInfoFor("tcp", sintf, tcpIDFor(laddr, raddr)) + if err = l.handler(name, info, conn, linkOptions{}, true, raddr.IP.IsLinkLocalUnicast()); err != nil { l.core.log.Errorln("Failed to create inbound link:", err) } } @@ -179,3 +180,16 @@ func (l *linkTCP) dialerFor(dst *net.TCPAddr, sintf string) (*net.Dialer, error) } return dialer, nil } + +func tcpIDFor(local net.Addr, remoteAddr *net.TCPAddr) string { + if localAddr, ok := local.(*net.TCPAddr); ok && localAddr.IP.Equal(remoteAddr.IP) { + // Nodes running on the same host — include both the IP and port. + return remoteAddr.String() + } + if remoteAddr.IP.IsLinkLocalUnicast() { + // Nodes discovered via multicast — include the IP only. + return remoteAddr.IP.String() + } + // Nodes connected remotely — include both the IP and port. + return remoteAddr.String() +} diff --git a/src/core/link_tls.go b/src/core/link_tls.go index ee3363ec..3af8fe2b 100644 --- a/src/core/link_tls.go +++ b/src/core/link_tls.go @@ -51,14 +51,14 @@ func (l *linkTLS) dial(url *url.URL, options linkOptions, sintf, sni string) err if err != nil { return err } - info := linkInfoFor("tls", sintf, addr.String()) - if l.links.isConnectedTo(info) { - return nil - } dialer, err := l.tcp.dialerFor(addr, sintf) if err != nil { return err } + info := linkInfoFor("tls", sintf, tcpIDFor(dialer.LocalAddr, addr)) + if l.links.isConnectedTo(info) { + return nil + } tlsconfig := l.config.Clone() tlsconfig.ServerName = sni tlsdialer := &tls.Dialer{ @@ -105,10 +105,11 @@ func (l *linkTLS) listen(url *url.URL, sintf string) (*Listener, error) { cancel() break } - addr := conn.RemoteAddr().(*net.TCPAddr) - name := fmt.Sprintf("tls://%s", addr) - info := linkInfoFor("tls", sintf, addr.String()) - if err = l.handler(name, info, conn, linkOptions{}, true, addr.IP.IsLinkLocalUnicast()); err != nil { + laddr := conn.LocalAddr().(*net.TCPAddr) + raddr := conn.RemoteAddr().(*net.TCPAddr) + name := fmt.Sprintf("tls://%s", raddr) + info := linkInfoFor("tls", sintf, tcpIDFor(laddr, raddr)) + if err = l.handler(name, info, conn, linkOptions{}, true, raddr.IP.IsLinkLocalUnicast()); err != nil { l.core.log.Errorln("Failed to create inbound link:", err) } } From 65e350153eeba16ab47386963a8e705a22dd2cc8 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 22 Oct 2022 18:05:14 +0100 Subject: [PATCH 067/293] Don't start multicast module if all `Beacon` and `Listen` are disabled --- src/multicast/multicast.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/multicast/multicast.go b/src/multicast/multicast.go index d40bcfc0..84d5933a 100644 --- a/src/multicast/multicast.go +++ b/src/multicast/multicast.go @@ -77,7 +77,11 @@ func (m *Multicast) _start() error { if m._isOpen { return fmt.Errorf("multicast module is already started") } - if len(m.config._interfaces) == 0 { + var anyEnabled bool + for intf := range m.config._interfaces { + anyEnabled = anyEnabled || intf.Beacon || intf.Listen + } + if !anyEnabled { return nil } m.log.Debugln("Starting multicast module") From 9a9452dcc814931503a1267bc0ef52b6ab9837ba Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 25 Oct 2022 18:58:52 +0100 Subject: [PATCH 068/293] Fix panic in `GetPeers` that may happen mid-link setup --- src/core/api.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/api.go b/src/core/api.go index 0fa6dd33..8e9186ac 100644 --- a/src/core/api.go +++ b/src/core/api.go @@ -62,6 +62,9 @@ func (c *Core) GetPeers() []PeerInfo { names := make(map[net.Conn]string) phony.Block(&c.links, func() { for _, info := range c.links._links { + if info == nil { + continue + } names[info.conn] = info.lname } }) From f08dec822a80bd487649273cd4195cdce4ed619f Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 26 Oct 2022 09:24:24 +0100 Subject: [PATCH 069/293] Priority support (#964) * Allow setting link priorities * Fix a bug * Allow setting priority on listeners and multicast interfaces * Update `yggdrasilctl` * Update to Arceliar/ironwood#5 --- cmd/yggdrasil/main.go | 9 +++++---- cmd/yggdrasilctl/main.go | 3 ++- contrib/mobile/mobile.go | 9 +++++---- go.mod | 2 +- go.sum | 15 +++------------ src/admin/getpeers.go | 5 +++++ src/config/config.go | 9 +++++---- src/core/api.go | 18 ++++++++++-------- src/core/link.go | 28 +++++++++++++++++++++++----- src/core/link_tcp.go | 2 +- src/core/link_tls.go | 2 +- src/core/link_unix.go | 2 +- src/multicast/multicast.go | 22 ++++++++++++---------- src/multicast/options.go | 9 +++++---- 14 files changed, 79 insertions(+), 56 deletions(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 3d5eab97..8185dee0 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -335,10 +335,11 @@ func run(args yggArgs, ctx context.Context) { options := []multicast.SetupOption{} for _, intf := range cfg.MulticastInterfaces { options = append(options, multicast.MulticastInterface{ - Regex: regexp.MustCompile(intf.Regex), - Beacon: intf.Beacon, - Listen: intf.Listen, - Port: intf.Port, + Regex: regexp.MustCompile(intf.Regex), + Beacon: intf.Beacon, + Listen: intf.Listen, + Port: intf.Port, + Priority: intf.Priority, }) } if n.multicast, err = multicast.New(n.core, logger, options...); err != nil { diff --git a/cmd/yggdrasilctl/main.go b/cmd/yggdrasilctl/main.go index 324550bd..c9b1522a 100644 --- a/cmd/yggdrasilctl/main.go +++ b/cmd/yggdrasilctl/main.go @@ -174,7 +174,7 @@ func run() int { if err := json.Unmarshal(recv.Response, &resp); err != nil { panic(err) } - table.SetHeader([]string{"Port", "Public Key", "IP Address", "Uptime", "RX", "TX", "URI"}) + table.SetHeader([]string{"Port", "Public Key", "IP Address", "Uptime", "RX", "TX", "Pr", "URI"}) for _, peer := range resp.Peers { table.Append([]string{ fmt.Sprintf("%d", peer.Port), @@ -183,6 +183,7 @@ func run() int { (time.Duration(peer.Uptime) * time.Second).String(), peer.RXBytes.String(), peer.TXBytes.String(), + fmt.Sprintf("%d", peer.Priority), peer.Remote, }) } diff --git a/contrib/mobile/mobile.go b/contrib/mobile/mobile.go index 0cf87180..78a3f506 100644 --- a/contrib/mobile/mobile.go +++ b/contrib/mobile/mobile.go @@ -83,10 +83,11 @@ func (m *Yggdrasil) StartJSON(configjson []byte) error { options := []multicast.SetupOption{} for _, intf := range m.config.MulticastInterfaces { options = append(options, multicast.MulticastInterface{ - Regex: regexp.MustCompile(intf.Regex), - Beacon: intf.Beacon, - Listen: intf.Listen, - Port: intf.Port, + Regex: regexp.MustCompile(intf.Regex), + Beacon: intf.Beacon, + Listen: intf.Listen, + Port: intf.Port, + Priority: intf.Priority, }) } m.multicast, err = multicast.New(m.core, logger, options...) diff --git a/go.mod b/go.mod index 4e73edea..156b42ce 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/yggdrasil-network/yggdrasil-go go 1.17 require ( - github.com/Arceliar/ironwood v0.0.0-20220924160422-ed4b6d4750b6 + github.com/Arceliar/ironwood v0.0.0-20221025225125-45b4281814c2 github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 github.com/cheggaaa/pb/v3 v3.0.8 github.com/gologme/log v1.2.0 diff --git a/go.sum b/go.sum index 9e0b7458..b6655087 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Arceliar/ironwood v0.0.0-20220924160422-ed4b6d4750b6 h1:iwL6nm2ibyuHXYimRNtFof7RJfe8JB+6CPDskV7K7gA= -github.com/Arceliar/ironwood v0.0.0-20220924160422-ed4b6d4750b6/go.mod h1:RP72rucOFm5udrnEzTmIWLRVGQiV/fSUAQXJ0RST/nk= +github.com/Arceliar/ironwood v0.0.0-20221025225125-45b4281814c2 h1:Usab30pNT2i/vZvpXcN9uOr5IO1RZPcUqoGH0DIAPnU= +github.com/Arceliar/ironwood v0.0.0-20221025225125-45b4281814c2/go.mod h1:RP72rucOFm5udrnEzTmIWLRVGQiV/fSUAQXJ0RST/nk= github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 h1:WndgpSW13S32VLQ3ugUxx2EnnWmgba1kCqPkd4Gk1yQ= github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979/go.mod h1:6Lkn+/zJilRMsKmbmG1RPoamiArC6HS73xbwRyp3UyI= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= @@ -17,8 +17,6 @@ github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwM github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hjson/hjson-go v3.1.0+incompatible h1:DY/9yE8ey8Zv22bY+mHV1uk2yRy0h8tKhZ77hEdi0Aw= github.com/hjson/hjson-go v3.1.0+incompatible/go.mod h1:qsetwF8NlsTsOTwZTApNlTCerV+b2GjYRRcIk4JMFio= -github.com/kardianos/minwinsvc v1.0.0 h1:+JfAi8IBJna0jY2dJGZqi7o15z13JelFIklJCAENALA= -github.com/kardianos/minwinsvc v1.0.0/go.mod h1:Bgd0oc+D0Qo3bBytmNtyRKVlp85dAloLKhfxanPFFRc= github.com/kardianos/minwinsvc v1.0.2 h1:JmZKFJQrmTGa/WiW+vkJXKmfzdjabuEW4Tirj5lLdR0= github.com/kardianos/minwinsvc v1.0.2/go.mod h1:LUZNYhNmxujx2tR7FbdxqYJ9XDDoCd3MQcl1o//FWl4= github.com/lxn/walk v0.0.0-20210112085537-c389da54e794/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ= @@ -50,7 +48,6 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20221012134737-56aed061732a h1:NmSIgad6KjE6VvHciPZuNRTKxGhlPfD6OA87W/PLkqg= golang.org/x/crypto v0.0.0-20221012134737-56aed061732a/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= @@ -58,8 +55,6 @@ golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9t golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105 h1:3vUV5x5+3LfQbgk7paCM6INOaJG9xXQbn79xoNkwfIk= -golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ= golang.org/x/mobile v0.0.0-20221012134814-c746ac228303 h1:K4fp1rDuJBz0FCPAWzIJwnzwNEM7S6yobdZzMrZ/Zws= golang.org/x/mobile v0.0.0-20221012134814-c746ac228303/go.mod h1:M32cGdzp91A8Ex9qQtyZinr19EYxzkFqDjW2oyHzTDQ= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= @@ -73,7 +68,7 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210927181540-4e4d966f7476/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211011170408-caeb26a5c8c0/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20221014081412-f15817d10f9b h1:tvrvnPFcdzp294diPnrdZZZ8XUt2Tyj7svb7X52iDuU= golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= @@ -87,7 +82,6 @@ golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -97,9 +91,7 @@ golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43 h1:OK7RB6t2WQX54srQQYSXMW8dF5C6/8+oA/s5QBmmto4= golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -109,7 +101,6 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b h1:NXqSWXSRUSCaFuvitrWtU169I3876zRTalMRbfd6LL0= golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0= golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= diff --git a/src/admin/getpeers.go b/src/admin/getpeers.go index 61d0937f..c1c9a6f9 100644 --- a/src/admin/getpeers.go +++ b/src/admin/getpeers.go @@ -19,6 +19,7 @@ type PeerEntry struct { IPAddress string `json:"address"` PublicKey string `json:"key"` Port uint64 `json:"port"` + Priority uint8 `json:"priority"` Coords []uint64 `json:"coords"` Remote string `json:"remote"` RXBytes DataUnit `json:"bytes_recvd"` @@ -35,6 +36,7 @@ func (a *AdminSocket) getPeersHandler(req *GetPeersRequest, res *GetPeersRespons IPAddress: net.IP(addr[:]).String(), PublicKey: hex.EncodeToString(p.Key), Port: p.Port, + Priority: p.Priority, Coords: p.Coords, Remote: p.Remote, RXBytes: DataUnit(p.RXBytes), @@ -43,6 +45,9 @@ func (a *AdminSocket) getPeersHandler(req *GetPeersRequest, res *GetPeersRespons }) } sort.Slice(res.Peers, func(i, j int) bool { + if res.Peers[i].Port == res.Peers[j].Port { + return res.Peers[i].Priority < res.Peers[j].Priority + } return res.Peers[i].Port < res.Peers[j].Port }) return nil diff --git a/src/config/config.go b/src/config/config.go index 5bdeec4b..3fc9c4ed 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -40,10 +40,11 @@ type NodeConfig struct { } type MulticastInterfaceConfig struct { - Regex string - Beacon bool - Listen bool - Port uint16 + Regex string + Beacon bool + Listen bool + Port uint16 + Priority uint8 } // NewSigningKeys replaces the signing keypair in the NodeConfig with a new diff --git a/src/core/api.go b/src/core/api.go index 8e9186ac..ae783156 100644 --- a/src/core/api.go +++ b/src/core/api.go @@ -20,14 +20,15 @@ type SelfInfo struct { } type PeerInfo struct { - Key ed25519.PublicKey - Root ed25519.PublicKey - Coords []uint64 - Port uint64 - Remote string - RXBytes uint64 - TXBytes uint64 - Uptime time.Duration + Key ed25519.PublicKey + Root ed25519.PublicKey + Coords []uint64 + Port uint64 + Priority uint8 + Remote string + RXBytes uint64 + TXBytes uint64 + Uptime time.Duration } type DHTEntryInfo struct { @@ -75,6 +76,7 @@ func (c *Core) GetPeers() []PeerInfo { info.Root = p.Root info.Coords = p.Coords info.Port = p.Port + info.Priority = p.Priority info.Remote = p.Conn.RemoteAddr().String() if name := names[p.Conn]; name != "" { info.Remote = name diff --git a/src/core/link.go b/src/core/link.go index 67184625..550d7ec2 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -8,6 +8,7 @@ import ( "io" "net" "net/url" + "strconv" "strings" "sync/atomic" "time" @@ -45,6 +46,7 @@ type link struct { type linkOptions struct { pinnedEd25519Keys map[keyArray]struct{} + priority uint8 } type Listener struct { @@ -120,17 +122,24 @@ func (l *links) call(u *url.URL, sintf string) (linkInfo, error) { copy(sigPubKey[:], sigPub) options.pinnedEd25519Keys[sigPubKey] = struct{}{} } + if p := u.Query().Get("priority"); p != "" { + pi, err := strconv.ParseUint(p, 10, 8) + if err != nil { + return info, fmt.Errorf("priority invalid: %w", err) + } + options.priority = uint8(pi) + } switch info.linkType { case "tcp": go func() { - if err := l.tcp.dial(u, options, sintf); err != nil { + if err := l.tcp.dial(u, options, sintf); err != nil && err != io.EOF { l.core.log.Warnf("Failed to dial TCP %s: %s\n", u.Host, err) } }() case "socks": go func() { - if err := l.socks.dial(u, options); err != nil { + if err := l.socks.dial(u, options); err != nil && err != io.EOF { l.core.log.Warnf("Failed to dial SOCKS %s: %s\n", u.Host, err) } }() @@ -154,14 +163,14 @@ func (l *links) call(u *url.URL, sintf string) (linkInfo, error) { } } go func() { - if err := l.tls.dial(u, options, sintf, tlsSNI); err != nil { + if err := l.tls.dial(u, options, sintf, tlsSNI); err != nil && err != io.EOF { l.core.log.Warnf("Failed to dial TLS %s: %s\n", u.Host, err) } }() case "unix": go func() { - if err := l.unix.dial(u, options, sintf); err != nil { + if err := l.unix.dial(u, options, sintf); err != nil && err != io.EOF { l.core.log.Warnf("Failed to dial UNIX %s: %s\n", u.Host, err) } }() @@ -303,7 +312,7 @@ func (intf *link) handler() error { intf.links.core.log.Infof("Connected %s %s: %s, source %s", dir, strings.ToUpper(intf.info.linkType), remoteStr, localStr) - err = intf.links.core.HandleConn(meta.key, intf.conn) + err = intf.links.core.HandleConn(meta.key, intf.conn, intf.options.priority) switch err { case io.EOF, net.ErrClosed, nil: intf.links.core.log.Infof("Disconnected %s %s: %s, source %s", @@ -347,3 +356,12 @@ func (c *linkConn) Write(p []byte) (n int, err error) { atomic.AddUint64(&c.tx, uint64(n)) return } + +func linkOptionsForListener(u *url.URL) (l linkOptions) { + if p := u.Query().Get("priority"); p != "" { + if pi, err := strconv.ParseUint(p, 10, 8); err == nil { + l.priority = uint8(pi) + } + } + return +} diff --git a/src/core/link_tcp.go b/src/core/link_tcp.go index a8f437e9..9c3c3290 100644 --- a/src/core/link_tcp.go +++ b/src/core/link_tcp.go @@ -86,7 +86,7 @@ func (l *linkTCP) listen(url *url.URL, sintf string) (*Listener, error) { raddr := conn.RemoteAddr().(*net.TCPAddr) name := fmt.Sprintf("tcp://%s", raddr) info := linkInfoFor("tcp", sintf, tcpIDFor(laddr, raddr)) - if err = l.handler(name, info, conn, linkOptions{}, true, raddr.IP.IsLinkLocalUnicast()); err != nil { + if err = l.handler(name, info, conn, linkOptionsForListener(url), true, raddr.IP.IsLinkLocalUnicast()); err != nil { l.core.log.Errorln("Failed to create inbound link:", err) } } diff --git a/src/core/link_tls.go b/src/core/link_tls.go index 3af8fe2b..4eeb8710 100644 --- a/src/core/link_tls.go +++ b/src/core/link_tls.go @@ -109,7 +109,7 @@ func (l *linkTLS) listen(url *url.URL, sintf string) (*Listener, error) { raddr := conn.RemoteAddr().(*net.TCPAddr) name := fmt.Sprintf("tls://%s", raddr) info := linkInfoFor("tls", sintf, tcpIDFor(laddr, raddr)) - if err = l.handler(name, info, conn, linkOptions{}, true, raddr.IP.IsLinkLocalUnicast()); err != nil { + if err = l.handler(name, info, conn, linkOptionsForListener(url), true, raddr.IP.IsLinkLocalUnicast()); err != nil { l.core.log.Errorln("Failed to create inbound link:", err) } } diff --git a/src/core/link_unix.go b/src/core/link_unix.go index 2d1b9398..50183a25 100644 --- a/src/core/link_unix.go +++ b/src/core/link_unix.go @@ -74,7 +74,7 @@ func (l *linkUNIX) listen(url *url.URL, _ string) (*Listener, error) { break } info := linkInfoFor("unix", "", url.String()) - if err = l.handler(url.String(), info, conn, linkOptions{}, true); err != nil { + if err = l.handler(url.String(), info, conn, linkOptionsForListener(url), true); err != nil { l.core.log.Errorln("Failed to create inbound link:", err) } } diff --git a/src/multicast/multicast.go b/src/multicast/multicast.go index 84d5933a..8d7fbb7d 100644 --- a/src/multicast/multicast.go +++ b/src/multicast/multicast.go @@ -37,11 +37,12 @@ type Multicast struct { } type interfaceInfo struct { - iface net.Interface - addrs []net.Addr - beacon bool - listen bool - port uint16 + iface net.Interface + addrs []net.Addr + beacon bool + listen bool + port uint16 + priority uint8 } type listenerInfo struct { @@ -194,10 +195,11 @@ func (m *Multicast) _getAllowedInterfaces() map[string]*interfaceInfo { continue } interfaces[iface.Name] = &interfaceInfo{ - iface: iface, - beacon: ifcfg.Beacon, - listen: ifcfg.Listen, - port: ifcfg.Port, + iface: iface, + beacon: ifcfg.Beacon, + listen: ifcfg.Listen, + port: ifcfg.Port, + priority: ifcfg.Priority, } break } @@ -387,7 +389,7 @@ func (m *Multicast) listen() { }) if info, ok := interfaces[from.Zone]; ok && info.listen { addr.Zone = "" - pin := fmt.Sprintf("/?key=%s", hex.EncodeToString(key)) + pin := fmt.Sprintf("/?key=%s&priority=%d", hex.EncodeToString(key), info.priority) u, err := url.Parse("tls://" + addr.String() + pin) if err != nil { m.log.Debugln("Call from multicast failed, parse error:", addr.String(), err) diff --git a/src/multicast/options.go b/src/multicast/options.go index a03b0677..f36284ed 100644 --- a/src/multicast/options.go +++ b/src/multicast/options.go @@ -16,10 +16,11 @@ type SetupOption interface { } type MulticastInterface struct { - Regex *regexp.Regexp - Beacon bool - Listen bool - Port uint16 + Regex *regexp.Regexp + Beacon bool + Listen bool + Port uint16 + Priority uint8 } type GroupAddress string From 4c66a13b93cb3e53402869df54d3ed5f0bc5af7d Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 26 Oct 2022 18:25:48 +0100 Subject: [PATCH 070/293] Version 0.4.6 --- CHANGELOG.md | 19 ++++++++++ cmd/yggdrasil/main.go | 9 ++--- cmd/yggdrasilctl/main.go | 3 +- contrib/mobile/mobile.go | 9 ++--- go.mod | 2 +- go.sum | 15 ++------ src/admin/getpeers.go | 5 +++ src/config/config.go | 9 ++--- src/core/api.go | 21 ++++++----- src/core/link.go | 64 +++++++++++++++++++-------------- src/core/link_socks.go | 2 +- src/core/link_tcp.go | 50 ++++++++++++++++---------- src/core/link_tls.go | 27 +++++++------- src/core/link_unix.go | 5 ++- src/multicast/multicast.go | 28 +++++++++------ src/multicast/multicast_unix.go | 12 ++++--- src/multicast/options.go | 9 ++--- 17 files changed, 174 insertions(+), 115 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc965e99..af827b21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,25 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - in case of vulnerabilities. --> +## [0.4.6] - 2022-10-25 + +### Added + +- Support for prioritising multiple peerings to the same node has been added, useful for nodes with multiple network interfaces + - The priority can be configured by specifying `?priority=X` in a `Peers` or `Listen` URI, or by specifying `Priority` within a `MulticastInterfaces` configuration entry + - Priorities are values between 0 and 254 (default is 0), lower numbers are prioritised and nodes will automatically negotiate the higher of the two values + +### Changed + +- On Linux, `SO_REUSEADDR` is now used on the multicast port instead of `SO_REUSEPORT`, which should allow processes running under different users to run simultaneously + +### Fixed + +- Adding peers using the `InterfacePeers` configuration option should now work correctly again +- Multiple connections from the same remote IP address will no longer be incorrectly dropped +- The admin socket will no longer incorrectly claim TCP connections as TLS +- A panic that could occur when calling `GetPeers` while a peering link is being set up has been fixed + ## [0.4.5] - 2022-10-15 ### Added diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 3d5eab97..8185dee0 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -335,10 +335,11 @@ func run(args yggArgs, ctx context.Context) { options := []multicast.SetupOption{} for _, intf := range cfg.MulticastInterfaces { options = append(options, multicast.MulticastInterface{ - Regex: regexp.MustCompile(intf.Regex), - Beacon: intf.Beacon, - Listen: intf.Listen, - Port: intf.Port, + Regex: regexp.MustCompile(intf.Regex), + Beacon: intf.Beacon, + Listen: intf.Listen, + Port: intf.Port, + Priority: intf.Priority, }) } if n.multicast, err = multicast.New(n.core, logger, options...); err != nil { diff --git a/cmd/yggdrasilctl/main.go b/cmd/yggdrasilctl/main.go index 324550bd..c9b1522a 100644 --- a/cmd/yggdrasilctl/main.go +++ b/cmd/yggdrasilctl/main.go @@ -174,7 +174,7 @@ func run() int { if err := json.Unmarshal(recv.Response, &resp); err != nil { panic(err) } - table.SetHeader([]string{"Port", "Public Key", "IP Address", "Uptime", "RX", "TX", "URI"}) + table.SetHeader([]string{"Port", "Public Key", "IP Address", "Uptime", "RX", "TX", "Pr", "URI"}) for _, peer := range resp.Peers { table.Append([]string{ fmt.Sprintf("%d", peer.Port), @@ -183,6 +183,7 @@ func run() int { (time.Duration(peer.Uptime) * time.Second).String(), peer.RXBytes.String(), peer.TXBytes.String(), + fmt.Sprintf("%d", peer.Priority), peer.Remote, }) } diff --git a/contrib/mobile/mobile.go b/contrib/mobile/mobile.go index 0cf87180..78a3f506 100644 --- a/contrib/mobile/mobile.go +++ b/contrib/mobile/mobile.go @@ -83,10 +83,11 @@ func (m *Yggdrasil) StartJSON(configjson []byte) error { options := []multicast.SetupOption{} for _, intf := range m.config.MulticastInterfaces { options = append(options, multicast.MulticastInterface{ - Regex: regexp.MustCompile(intf.Regex), - Beacon: intf.Beacon, - Listen: intf.Listen, - Port: intf.Port, + Regex: regexp.MustCompile(intf.Regex), + Beacon: intf.Beacon, + Listen: intf.Listen, + Port: intf.Port, + Priority: intf.Priority, }) } m.multicast, err = multicast.New(m.core, logger, options...) diff --git a/go.mod b/go.mod index 4e73edea..156b42ce 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/yggdrasil-network/yggdrasil-go go 1.17 require ( - github.com/Arceliar/ironwood v0.0.0-20220924160422-ed4b6d4750b6 + github.com/Arceliar/ironwood v0.0.0-20221025225125-45b4281814c2 github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 github.com/cheggaaa/pb/v3 v3.0.8 github.com/gologme/log v1.2.0 diff --git a/go.sum b/go.sum index 9e0b7458..b6655087 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Arceliar/ironwood v0.0.0-20220924160422-ed4b6d4750b6 h1:iwL6nm2ibyuHXYimRNtFof7RJfe8JB+6CPDskV7K7gA= -github.com/Arceliar/ironwood v0.0.0-20220924160422-ed4b6d4750b6/go.mod h1:RP72rucOFm5udrnEzTmIWLRVGQiV/fSUAQXJ0RST/nk= +github.com/Arceliar/ironwood v0.0.0-20221025225125-45b4281814c2 h1:Usab30pNT2i/vZvpXcN9uOr5IO1RZPcUqoGH0DIAPnU= +github.com/Arceliar/ironwood v0.0.0-20221025225125-45b4281814c2/go.mod h1:RP72rucOFm5udrnEzTmIWLRVGQiV/fSUAQXJ0RST/nk= github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 h1:WndgpSW13S32VLQ3ugUxx2EnnWmgba1kCqPkd4Gk1yQ= github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979/go.mod h1:6Lkn+/zJilRMsKmbmG1RPoamiArC6HS73xbwRyp3UyI= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= @@ -17,8 +17,6 @@ github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwM github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hjson/hjson-go v3.1.0+incompatible h1:DY/9yE8ey8Zv22bY+mHV1uk2yRy0h8tKhZ77hEdi0Aw= github.com/hjson/hjson-go v3.1.0+incompatible/go.mod h1:qsetwF8NlsTsOTwZTApNlTCerV+b2GjYRRcIk4JMFio= -github.com/kardianos/minwinsvc v1.0.0 h1:+JfAi8IBJna0jY2dJGZqi7o15z13JelFIklJCAENALA= -github.com/kardianos/minwinsvc v1.0.0/go.mod h1:Bgd0oc+D0Qo3bBytmNtyRKVlp85dAloLKhfxanPFFRc= github.com/kardianos/minwinsvc v1.0.2 h1:JmZKFJQrmTGa/WiW+vkJXKmfzdjabuEW4Tirj5lLdR0= github.com/kardianos/minwinsvc v1.0.2/go.mod h1:LUZNYhNmxujx2tR7FbdxqYJ9XDDoCd3MQcl1o//FWl4= github.com/lxn/walk v0.0.0-20210112085537-c389da54e794/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ= @@ -50,7 +48,6 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20221012134737-56aed061732a h1:NmSIgad6KjE6VvHciPZuNRTKxGhlPfD6OA87W/PLkqg= golang.org/x/crypto v0.0.0-20221012134737-56aed061732a/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= @@ -58,8 +55,6 @@ golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9t golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105 h1:3vUV5x5+3LfQbgk7paCM6INOaJG9xXQbn79xoNkwfIk= -golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ= golang.org/x/mobile v0.0.0-20221012134814-c746ac228303 h1:K4fp1rDuJBz0FCPAWzIJwnzwNEM7S6yobdZzMrZ/Zws= golang.org/x/mobile v0.0.0-20221012134814-c746ac228303/go.mod h1:M32cGdzp91A8Ex9qQtyZinr19EYxzkFqDjW2oyHzTDQ= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= @@ -73,7 +68,7 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210927181540-4e4d966f7476/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211011170408-caeb26a5c8c0/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20221014081412-f15817d10f9b h1:tvrvnPFcdzp294diPnrdZZZ8XUt2Tyj7svb7X52iDuU= golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= @@ -87,7 +82,6 @@ golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -97,9 +91,7 @@ golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43 h1:OK7RB6t2WQX54srQQYSXMW8dF5C6/8+oA/s5QBmmto4= golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -109,7 +101,6 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b h1:NXqSWXSRUSCaFuvitrWtU169I3876zRTalMRbfd6LL0= golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0= golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= diff --git a/src/admin/getpeers.go b/src/admin/getpeers.go index 61d0937f..c1c9a6f9 100644 --- a/src/admin/getpeers.go +++ b/src/admin/getpeers.go @@ -19,6 +19,7 @@ type PeerEntry struct { IPAddress string `json:"address"` PublicKey string `json:"key"` Port uint64 `json:"port"` + Priority uint8 `json:"priority"` Coords []uint64 `json:"coords"` Remote string `json:"remote"` RXBytes DataUnit `json:"bytes_recvd"` @@ -35,6 +36,7 @@ func (a *AdminSocket) getPeersHandler(req *GetPeersRequest, res *GetPeersRespons IPAddress: net.IP(addr[:]).String(), PublicKey: hex.EncodeToString(p.Key), Port: p.Port, + Priority: p.Priority, Coords: p.Coords, Remote: p.Remote, RXBytes: DataUnit(p.RXBytes), @@ -43,6 +45,9 @@ func (a *AdminSocket) getPeersHandler(req *GetPeersRequest, res *GetPeersRespons }) } sort.Slice(res.Peers, func(i, j int) bool { + if res.Peers[i].Port == res.Peers[j].Port { + return res.Peers[i].Priority < res.Peers[j].Priority + } return res.Peers[i].Port < res.Peers[j].Port }) return nil diff --git a/src/config/config.go b/src/config/config.go index 5bdeec4b..3fc9c4ed 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -40,10 +40,11 @@ type NodeConfig struct { } type MulticastInterfaceConfig struct { - Regex string - Beacon bool - Listen bool - Port uint16 + Regex string + Beacon bool + Listen bool + Port uint16 + Priority uint8 } // NewSigningKeys replaces the signing keypair in the NodeConfig with a new diff --git a/src/core/api.go b/src/core/api.go index 0fa6dd33..ae783156 100644 --- a/src/core/api.go +++ b/src/core/api.go @@ -20,14 +20,15 @@ type SelfInfo struct { } type PeerInfo struct { - Key ed25519.PublicKey - Root ed25519.PublicKey - Coords []uint64 - Port uint64 - Remote string - RXBytes uint64 - TXBytes uint64 - Uptime time.Duration + Key ed25519.PublicKey + Root ed25519.PublicKey + Coords []uint64 + Port uint64 + Priority uint8 + Remote string + RXBytes uint64 + TXBytes uint64 + Uptime time.Duration } type DHTEntryInfo struct { @@ -62,6 +63,9 @@ func (c *Core) GetPeers() []PeerInfo { names := make(map[net.Conn]string) phony.Block(&c.links, func() { for _, info := range c.links._links { + if info == nil { + continue + } names[info.conn] = info.lname } }) @@ -72,6 +76,7 @@ func (c *Core) GetPeers() []PeerInfo { info.Root = p.Root info.Coords = p.Coords info.Port = p.Port + info.Priority = p.Priority info.Remote = p.Conn.RemoteAddr().String() if name := names[p.Conn]; name != "" { info.Remote = name diff --git a/src/core/link.go b/src/core/link.go index b4515276..550d7ec2 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -8,6 +8,7 @@ import ( "io" "net" "net/url" + "strconv" "strings" "sync/atomic" "time" @@ -45,6 +46,7 @@ type link struct { type linkOptions struct { pinnedEd25519Keys map[keyArray]struct{} + priority uint8 } type Listener struct { @@ -120,17 +122,24 @@ func (l *links) call(u *url.URL, sintf string) (linkInfo, error) { copy(sigPubKey[:], sigPub) options.pinnedEd25519Keys[sigPubKey] = struct{}{} } + if p := u.Query().Get("priority"); p != "" { + pi, err := strconv.ParseUint(p, 10, 8) + if err != nil { + return info, fmt.Errorf("priority invalid: %w", err) + } + options.priority = uint8(pi) + } switch info.linkType { case "tcp": go func() { - if err := l.tcp.dial(u, options, sintf); err != nil { + if err := l.tcp.dial(u, options, sintf); err != nil && err != io.EOF { l.core.log.Warnf("Failed to dial TCP %s: %s\n", u.Host, err) } }() case "socks": go func() { - if err := l.socks.dial(u, options); err != nil { + if err := l.socks.dial(u, options); err != nil && err != io.EOF { l.core.log.Warnf("Failed to dial SOCKS %s: %s\n", u.Host, err) } }() @@ -154,14 +163,14 @@ func (l *links) call(u *url.URL, sintf string) (linkInfo, error) { } } go func() { - if err := l.tls.dial(u, options, sintf, tlsSNI); err != nil { + if err := l.tls.dial(u, options, sintf, tlsSNI); err != nil && err != io.EOF { l.core.log.Warnf("Failed to dial TLS %s: %s\n", u.Host, err) } }() case "unix": go func() { - if err := l.unix.dial(u, options, sintf); err != nil { + if err := l.unix.dial(u, options, sintf); err != nil && err != io.EOF { l.core.log.Warnf("Failed to dial UNIX %s: %s\n", u.Host, err) } }() @@ -272,8 +281,7 @@ func (intf *link) handler() error { var key keyArray copy(key[:], meta.key) if _, allowed := pinned[key]; !allowed { - intf.links.core.log.Errorf("Failed to connect to node: %q sent ed25519 key that does not match pinned keys", intf.name()) - return fmt.Errorf("failed to connect: host sent ed25519 key that does not match pinned keys") + return fmt.Errorf("node public key that does not match pinned keys") } } // Check if we're authorized to connect to this key / IP @@ -286,31 +294,33 @@ func (intf *link) handler() error { } } if intf.incoming && !intf.force && !isallowed { - intf.links.core.log.Warnf("%s connection from %s forbidden: AllowedEncryptionPublicKeys does not contain key %s", - strings.ToUpper(intf.info.linkType), intf.info.remote, hex.EncodeToString(meta.key)) _ = intf.close() - return fmt.Errorf("forbidden connection") + return fmt.Errorf("node public key %q is not in AllowedPublicKeys", hex.EncodeToString(meta.key)) } phony.Block(intf.links, func() { intf.links._links[intf.info] = intf }) + dir := "outbound" + if intf.incoming { + dir = "inbound" + } remoteAddr := net.IP(address.AddrForKey(meta.key)[:]).String() remoteStr := fmt.Sprintf("%s@%s", remoteAddr, intf.info.remote) localStr := intf.conn.LocalAddr() - intf.links.core.log.Infof("Connected %s: %s, source %s", - strings.ToUpper(intf.info.linkType), remoteStr, localStr) + intf.links.core.log.Infof("Connected %s %s: %s, source %s", + dir, strings.ToUpper(intf.info.linkType), remoteStr, localStr) - // TODO don't report an error if it's just a 'use of closed network connection' - if err = intf.links.core.HandleConn(meta.key, intf.conn); err != nil && err != io.EOF { - intf.links.core.log.Infof("Disconnected %s: %s, source %s; error: %s", - strings.ToUpper(intf.info.linkType), remoteStr, localStr, err) - } else { - intf.links.core.log.Infof("Disconnected %s: %s, source %s", - strings.ToUpper(intf.info.linkType), remoteStr, localStr) + err = intf.links.core.HandleConn(meta.key, intf.conn, intf.options.priority) + switch err { + case io.EOF, net.ErrClosed, nil: + intf.links.core.log.Infof("Disconnected %s %s: %s, source %s", + dir, strings.ToUpper(intf.info.linkType), remoteStr, localStr) + default: + intf.links.core.log.Infof("Disconnected %s %s: %s, source %s; error: %s", + dir, strings.ToUpper(intf.info.linkType), remoteStr, localStr, err) } - return nil } @@ -318,14 +328,7 @@ func (intf *link) close() error { return intf.conn.Close() } -func (intf *link) name() string { - return intf.lname -} - func linkInfoFor(linkType, sintf, remote string) linkInfo { - if h, _, err := net.SplitHostPort(remote); err == nil { - remote = h - } return linkInfo{ linkType: linkType, local: sintf, @@ -353,3 +356,12 @@ func (c *linkConn) Write(p []byte) (n int, err error) { atomic.AddUint64(&c.tx, uint64(n)) return } + +func linkOptionsForListener(u *url.URL) (l linkOptions) { + if p := u.Query().Get("priority"); p != "" { + if pi, err := strconv.ParseUint(p, 10, 8); err == nil { + l.priority = uint8(pi) + } + } + return +} diff --git a/src/core/link_socks.go b/src/core/link_socks.go index ad5b8c98..036de992 100644 --- a/src/core/link_socks.go +++ b/src/core/link_socks.go @@ -23,7 +23,7 @@ func (l *links) newLinkSOCKS() *linkSOCKS { func (l *linkSOCKS) dial(url *url.URL, options linkOptions) error { info := linkInfoFor("socks", "", url.Path) if l.links.isConnectedTo(info) { - return fmt.Errorf("duplicate connection attempt") + return nil } proxyAuth := &proxy.Auth{} proxyAuth.User = url.User.Username() diff --git a/src/core/link_tcp.go b/src/core/link_tcp.go index c5a73c9e..9c3c3290 100644 --- a/src/core/link_tcp.go +++ b/src/core/link_tcp.go @@ -31,24 +31,24 @@ func (l *links) newLinkTCP() *linkTCP { } func (l *linkTCP) dial(url *url.URL, options linkOptions, sintf string) error { - info := linkInfoFor("tcp", sintf, strings.SplitN(url.Host, "%", 2)[0]) - if l.links.isConnectedTo(info) { - return fmt.Errorf("duplicate connection attempt") - } addr, err := net.ResolveTCPAddr("tcp", url.Host) if err != nil { return err } - addr.Zone = sintf - dialer, err := l.dialerFor(addr.String(), sintf) + dialer, err := l.dialerFor(addr, sintf) if err != nil { return err } + info := linkInfoFor("tcp", sintf, tcpIDFor(dialer.LocalAddr, addr)) + if l.links.isConnectedTo(info) { + return nil + } conn, err := dialer.DialContext(l.core.ctx, "tcp", addr.String()) if err != nil { return err } - return l.handler(url.String(), info, conn, options, false) + uri := strings.TrimRight(strings.SplitN(url.String(), "?", 2)[0], "/") + return l.handler(uri, info, conn, options, false, false) } func (l *linkTCP) listen(url *url.URL, sintf string) (*Listener, error) { @@ -82,10 +82,11 @@ func (l *linkTCP) listen(url *url.URL, sintf string) (*Listener, error) { cancel() break } - addr := conn.RemoteAddr().(*net.TCPAddr) - name := fmt.Sprintf("tls://%s", addr) - info := linkInfoFor("tcp", sintf, strings.SplitN(addr.IP.String(), "%", 2)[0]) - if err = l.handler(name, info, conn, linkOptions{}, true); err != nil { + laddr := conn.LocalAddr().(*net.TCPAddr) + raddr := conn.RemoteAddr().(*net.TCPAddr) + name := fmt.Sprintf("tcp://%s", raddr) + info := linkInfoFor("tcp", sintf, tcpIDFor(laddr, raddr)) + if err = l.handler(name, info, conn, linkOptionsForListener(url), true, raddr.IP.IsLinkLocalUnicast()); err != nil { l.core.log.Errorln("Failed to create inbound link:", err) } } @@ -96,13 +97,13 @@ func (l *linkTCP) listen(url *url.URL, sintf string) (*Listener, error) { return entry, nil } -func (l *linkTCP) handler(name string, info linkInfo, conn net.Conn, options linkOptions, incoming bool) error { +func (l *linkTCP) handler(name string, info linkInfo, conn net.Conn, options linkOptions, incoming, force bool) error { return l.links.create( conn, // connection name, // connection name info, // connection info incoming, // not incoming - false, // not forced + force, // not forced options, // connection options ) } @@ -121,13 +122,11 @@ func (l *linkTCP) getAddr() *net.TCPAddr { return addr } -func (l *linkTCP) dialerFor(saddr, sintf string) (*net.Dialer, error) { - dst, err := net.ResolveTCPAddr("tcp", saddr) - if err != nil { - return nil, err - } +func (l *linkTCP) dialerFor(dst *net.TCPAddr, sintf string) (*net.Dialer, error) { if dst.IP.IsLinkLocalUnicast() { - dst.Zone = sintf + if sintf != "" { + dst.Zone = sintf + } if dst.Zone == "" { return nil, fmt.Errorf("link-local address requires a zone") } @@ -181,3 +180,16 @@ func (l *linkTCP) dialerFor(saddr, sintf string) (*net.Dialer, error) { } return dialer, nil } + +func tcpIDFor(local net.Addr, remoteAddr *net.TCPAddr) string { + if localAddr, ok := local.(*net.TCPAddr); ok && localAddr.IP.Equal(remoteAddr.IP) { + // Nodes running on the same host — include both the IP and port. + return remoteAddr.String() + } + if remoteAddr.IP.IsLinkLocalUnicast() { + // Nodes discovered via multicast — include the IP only. + return remoteAddr.IP.String() + } + // Nodes connected remotely — include both the IP and port. + return remoteAddr.String() +} diff --git a/src/core/link_tls.go b/src/core/link_tls.go index 1e932b66..4eeb8710 100644 --- a/src/core/link_tls.go +++ b/src/core/link_tls.go @@ -47,19 +47,18 @@ func (l *links) newLinkTLS(tcp *linkTCP) *linkTLS { } func (l *linkTLS) dial(url *url.URL, options linkOptions, sintf, sni string) error { - info := linkInfoFor("tls", sintf, strings.SplitN(url.Host, "%", 2)[0]) - if l.links.isConnectedTo(info) { - return fmt.Errorf("duplicate connection attempt") - } addr, err := net.ResolveTCPAddr("tcp", url.Host) if err != nil { return err } - addr.Zone = sintf - dialer, err := l.tcp.dialerFor(addr.String(), sintf) + dialer, err := l.tcp.dialerFor(addr, sintf) if err != nil { return err } + info := linkInfoFor("tls", sintf, tcpIDFor(dialer.LocalAddr, addr)) + if l.links.isConnectedTo(info) { + return nil + } tlsconfig := l.config.Clone() tlsconfig.ServerName = sni tlsdialer := &tls.Dialer{ @@ -70,7 +69,8 @@ func (l *linkTLS) dial(url *url.URL, options linkOptions, sintf, sni string) err if err != nil { return err } - return l.handler(url.String(), info, conn, options, false) + uri := strings.TrimRight(strings.SplitN(url.String(), "?", 2)[0], "/") + return l.handler(uri, info, conn, options, false, false) } func (l *linkTLS) listen(url *url.URL, sintf string) (*Listener, error) { @@ -105,10 +105,11 @@ func (l *linkTLS) listen(url *url.URL, sintf string) (*Listener, error) { cancel() break } - addr := conn.RemoteAddr().(*net.TCPAddr) - name := fmt.Sprintf("tls://%s", addr) - info := linkInfoFor("tls", sintf, strings.SplitN(addr.IP.String(), "%", 2)[0]) - if err = l.handler(name, info, conn, linkOptions{}, true); err != nil { + laddr := conn.LocalAddr().(*net.TCPAddr) + raddr := conn.RemoteAddr().(*net.TCPAddr) + name := fmt.Sprintf("tls://%s", raddr) + info := linkInfoFor("tls", sintf, tcpIDFor(laddr, raddr)) + if err = l.handler(name, info, conn, linkOptionsForListener(url), true, raddr.IP.IsLinkLocalUnicast()); err != nil { l.core.log.Errorln("Failed to create inbound link:", err) } } @@ -166,6 +167,6 @@ func (l *linkTLS) generateConfig() (*tls.Config, error) { }, nil } -func (l *linkTLS) handler(name string, info linkInfo, conn net.Conn, options linkOptions, incoming bool) error { - return l.tcp.handler(name, info, conn, options, incoming) +func (l *linkTLS) handler(name string, info linkInfo, conn net.Conn, options linkOptions, incoming, force bool) error { + return l.tcp.handler(name, info, conn, options, incoming, force) } diff --git a/src/core/link_unix.go b/src/core/link_unix.go index e71f9362..50183a25 100644 --- a/src/core/link_unix.go +++ b/src/core/link_unix.go @@ -2,7 +2,6 @@ package core import ( "context" - "fmt" "net" "net/url" "time" @@ -36,7 +35,7 @@ func (l *links) newLinkUNIX() *linkUNIX { func (l *linkUNIX) dial(url *url.URL, options linkOptions, _ string) error { info := linkInfoFor("unix", "", url.Path) if l.links.isConnectedTo(info) { - return fmt.Errorf("duplicate connection attempt") + return nil } addr, err := net.ResolveUnixAddr("unix", url.Path) if err != nil { @@ -75,7 +74,7 @@ func (l *linkUNIX) listen(url *url.URL, _ string) (*Listener, error) { break } info := linkInfoFor("unix", "", url.String()) - if err = l.handler(url.String(), info, conn, linkOptions{}, true); err != nil { + if err = l.handler(url.String(), info, conn, linkOptionsForListener(url), true); err != nil { l.core.log.Errorln("Failed to create inbound link:", err) } } diff --git a/src/multicast/multicast.go b/src/multicast/multicast.go index d40bcfc0..8d7fbb7d 100644 --- a/src/multicast/multicast.go +++ b/src/multicast/multicast.go @@ -37,11 +37,12 @@ type Multicast struct { } type interfaceInfo struct { - iface net.Interface - addrs []net.Addr - beacon bool - listen bool - port uint16 + iface net.Interface + addrs []net.Addr + beacon bool + listen bool + port uint16 + priority uint8 } type listenerInfo struct { @@ -77,7 +78,11 @@ func (m *Multicast) _start() error { if m._isOpen { return fmt.Errorf("multicast module is already started") } - if len(m.config._interfaces) == 0 { + var anyEnabled bool + for intf := range m.config._interfaces { + anyEnabled = anyEnabled || intf.Beacon || intf.Listen + } + if !anyEnabled { return nil } m.log.Debugln("Starting multicast module") @@ -190,10 +195,11 @@ func (m *Multicast) _getAllowedInterfaces() map[string]*interfaceInfo { continue } interfaces[iface.Name] = &interfaceInfo{ - iface: iface, - beacon: ifcfg.Beacon, - listen: ifcfg.Listen, - port: ifcfg.Port, + iface: iface, + beacon: ifcfg.Beacon, + listen: ifcfg.Listen, + port: ifcfg.Port, + priority: ifcfg.Priority, } break } @@ -383,7 +389,7 @@ func (m *Multicast) listen() { }) if info, ok := interfaces[from.Zone]; ok && info.listen { addr.Zone = "" - pin := fmt.Sprintf("/?key=%s", hex.EncodeToString(key)) + pin := fmt.Sprintf("/?key=%s&priority=%d", hex.EncodeToString(key), info.priority) u, err := url.Parse("tls://" + addr.String() + pin) if err != nil { m.log.Debugln("Call from multicast failed, parse error:", addr.String(), err) diff --git a/src/multicast/multicast_unix.go b/src/multicast/multicast_unix.go index c59d876b..08230735 100644 --- a/src/multicast/multicast_unix.go +++ b/src/multicast/multicast_unix.go @@ -15,15 +15,19 @@ func (m *Multicast) _multicastStarted() { func (m *Multicast) multicastReuse(network string, address string, c syscall.RawConn) error { var control error - var reuseport error + var reuseaddr error control = c.Control(func(fd uintptr) { - reuseport = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1) + // Previously we used SO_REUSEPORT here, but that meant that machines running + // Yggdrasil nodes as different users would inevitably fail with EADDRINUSE. + // The behaviour for multicast is similar with both, so we'll use SO_REUSEADDR + // instead. + reuseaddr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1) }) switch { - case reuseport != nil: - return reuseport + case reuseaddr != nil: + return reuseaddr default: return control } diff --git a/src/multicast/options.go b/src/multicast/options.go index a03b0677..f36284ed 100644 --- a/src/multicast/options.go +++ b/src/multicast/options.go @@ -16,10 +16,11 @@ type SetupOption interface { } type MulticastInterface struct { - Regex *regexp.Regexp - Beacon bool - Listen bool - Port uint16 + Regex *regexp.Regexp + Beacon bool + Listen bool + Port uint16 + Priority uint8 } type GroupAddress string From cfa293d189fae9af9c025bf40a19e9ac69f8f839 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 26 Oct 2022 22:29:19 +0100 Subject: [PATCH 071/293] Fix bug in admin socket where requests fail unless `"arguments":{}` is specified in the JSON --- src/admin/admin.go | 1 + 1 file changed, 1 insertion(+) diff --git a/src/admin/admin.go b/src/admin/admin.go index b24bf0de..9dbcfdca 100644 --- a/src/admin/admin.go +++ b/src/admin/admin.go @@ -327,6 +327,7 @@ func (a *AdminSocket) handleRequest(conn net.Conn) { var buf json.RawMessage var req AdminSocketRequest var resp AdminSocketResponse + req.Arguments = []byte("{}") if err := func() error { if err = decoder.Decode(&buf); err != nil { return fmt.Errorf("Failed to find request") From ee33bd248f19945d83d1b84db65d97030739eb3e Mon Sep 17 00:00:00 2001 From: Revertron <105154+Revertron@users.noreply.github.com> Date: Tue, 1 Nov 2022 13:10:50 +0100 Subject: [PATCH 072/293] Added two new methods to `mobile` package (#974) * Added two new methods In order to implement https://github.com/yggdrasil-network/yggdrasil-android/issues/25 we need these new methods. * Renamed methods, changed comments --- contrib/mobile/mobile.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/contrib/mobile/mobile.go b/contrib/mobile/mobile.go index 78a3f506..9b146492 100644 --- a/contrib/mobile/mobile.go +++ b/contrib/mobile/mobile.go @@ -115,6 +115,18 @@ func (m *Yggdrasil) Send(p []byte) error { return nil } +// Send sends a packet from given buffer to Yggdrasil. From first byte up to length. +func (m *Yggdrasil) SendBuffer(p []byte, length int) error { + if m.iprwc == nil { + return nil + } + if len(p) < length { + return nil + } + _, _ = m.iprwc.Write(p[:length]) + return nil +} + // Recv waits for and reads a packet coming from Yggdrasil. It // will be a fully formed IPv6 packet func (m *Yggdrasil) Recv() ([]byte, error) { @@ -126,6 +138,15 @@ func (m *Yggdrasil) Recv() ([]byte, error) { return buf[:n], nil } +// Recv waits for and reads a packet coming from Yggdrasil to given buffer, returning size of packet +func (m *Yggdrasil) RecvBuffer(buf []byte) (int, error) { + if m.iprwc == nil { + return 0, nil + } + n, _ := m.iprwc.Read(buf) + return n, nil +} + // Stop the mobile Yggdrasil instance func (m *Yggdrasil) Stop() error { logger := log.New(m.log, "", 0) From 590d83aa9c90b151de471d13571c331075506481 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 1 Nov 2022 17:42:52 +0000 Subject: [PATCH 073/293] Fix #975 by not exporting `uint8` --- contrib/mobile/mobile.go | 4 ++-- go.mod | 2 +- go.sum | 5 ++--- src/admin/getpeers.go | 4 ++-- src/config/config.go | 2 +- 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/contrib/mobile/mobile.go b/contrib/mobile/mobile.go index 9b146492..ff22b9d7 100644 --- a/contrib/mobile/mobile.go +++ b/contrib/mobile/mobile.go @@ -87,7 +87,7 @@ func (m *Yggdrasil) StartJSON(configjson []byte) error { Beacon: intf.Beacon, Listen: intf.Listen, Port: intf.Port, - Priority: intf.Priority, + Priority: uint8(intf.Priority), }) } m.multicast, err = multicast.New(m.core, logger, options...) @@ -138,7 +138,7 @@ func (m *Yggdrasil) Recv() ([]byte, error) { return buf[:n], nil } -// Recv waits for and reads a packet coming from Yggdrasil to given buffer, returning size of packet +// Recv waits for and reads a packet coming from Yggdrasil to given buffer, returning size of packet func (m *Yggdrasil) RecvBuffer(buf []byte) (int, error) { if m.iprwc == nil { return 0, nil diff --git a/go.mod b/go.mod index 156b42ce..adaf7cf8 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/kardianos/minwinsvc v1.0.2 github.com/mitchellh/mapstructure v1.4.1 github.com/vishvananda/netlink v1.1.0 - golang.org/x/mobile v0.0.0-20221012134814-c746ac228303 + golang.org/x/mobile v0.0.0-20221020085226-b36e6246172e golang.org/x/net v0.0.0-20221014081412-f15817d10f9b golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43 golang.org/x/text v0.3.8 diff --git a/go.sum b/go.sum index b6655087..e398aca6 100644 --- a/go.sum +++ b/go.sum @@ -55,8 +55,8 @@ golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9t golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20221012134814-c746ac228303 h1:K4fp1rDuJBz0FCPAWzIJwnzwNEM7S6yobdZzMrZ/Zws= -golang.org/x/mobile v0.0.0-20221012134814-c746ac228303/go.mod h1:M32cGdzp91A8Ex9qQtyZinr19EYxzkFqDjW2oyHzTDQ= +golang.org/x/mobile v0.0.0-20221020085226-b36e6246172e h1:zSgtO19fpg781xknwqiQPmOHaASr6E7ZVlTseLd9Fx4= +golang.org/x/mobile v0.0.0-20221020085226-b36e6246172e/go.mod h1:aAjjkJNdrh3PMckS4B10TGS2nag27cbKR1y2BpUxsiY= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= @@ -108,7 +108,6 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= -golang.org/x/tools v0.1.8-0.20211022200916-316ba0b74098/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/src/admin/getpeers.go b/src/admin/getpeers.go index c1c9a6f9..d51b184d 100644 --- a/src/admin/getpeers.go +++ b/src/admin/getpeers.go @@ -19,7 +19,7 @@ type PeerEntry struct { IPAddress string `json:"address"` PublicKey string `json:"key"` Port uint64 `json:"port"` - Priority uint8 `json:"priority"` + Priority uint64 `json:"priority"` Coords []uint64 `json:"coords"` Remote string `json:"remote"` RXBytes DataUnit `json:"bytes_recvd"` @@ -36,7 +36,7 @@ func (a *AdminSocket) getPeersHandler(req *GetPeersRequest, res *GetPeersRespons IPAddress: net.IP(addr[:]).String(), PublicKey: hex.EncodeToString(p.Key), Port: p.Port, - Priority: p.Priority, + Priority: uint64(p.Priority), // can't be uint8 thanks to gobind Coords: p.Coords, Remote: p.Remote, RXBytes: DataUnit(p.RXBytes), diff --git a/src/config/config.go b/src/config/config.go index 3fc9c4ed..f7f0f6ba 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -44,7 +44,7 @@ type MulticastInterfaceConfig struct { Beacon bool Listen bool Port uint16 - Priority uint8 + Priority uint64 // really uint8, but gobind won't export it } // NewSigningKeys replaces the signing keypair in the NodeConfig with a new From 6112c9cf18929547229e55c9d364efec6f775f88 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 1 Nov 2022 18:34:49 +0000 Subject: [PATCH 074/293] Fix build --- cmd/yggdrasil/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 8185dee0..2d1da6b9 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -339,7 +339,7 @@ func run(args yggArgs, ctx context.Context) { Beacon: intf.Beacon, Listen: intf.Listen, Port: intf.Port, - Priority: intf.Priority, + Priority: uint8(intf.Priority), }) } if n.multicast, err = multicast.New(n.core, logger, options...); err != nil { From 110613b234025078990143749cd30f0b8a964e92 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 8 Nov 2022 21:59:13 +0000 Subject: [PATCH 075/293] Try all addresses when connecting to a DNS name Fixes #980 --- src/core/link_tcp.go | 56 +++++++++++++++++++++++--------------------- src/core/link_tls.go | 55 +++++++++++++++++++++++++++---------------- 2 files changed, 64 insertions(+), 47 deletions(-) diff --git a/src/core/link_tcp.go b/src/core/link_tcp.go index 9c3c3290..4392fb73 100644 --- a/src/core/link_tcp.go +++ b/src/core/link_tcp.go @@ -5,6 +5,7 @@ import ( "fmt" "net" "net/url" + "strconv" "strings" "time" @@ -31,24 +32,39 @@ func (l *links) newLinkTCP() *linkTCP { } func (l *linkTCP) dial(url *url.URL, options linkOptions, sintf string) error { - addr, err := net.ResolveTCPAddr("tcp", url.Host) - if err != nil { - return err - } - dialer, err := l.dialerFor(addr, sintf) - if err != nil { - return err - } - info := linkInfoFor("tcp", sintf, tcpIDFor(dialer.LocalAddr, addr)) + info := linkInfoFor("tcp", sintf, url.Host) if l.links.isConnectedTo(info) { return nil } - conn, err := dialer.DialContext(l.core.ctx, "tcp", addr.String()) + host, p, err := net.SplitHostPort(url.Host) if err != nil { return err } - uri := strings.TrimRight(strings.SplitN(url.String(), "?", 2)[0], "/") - return l.handler(uri, info, conn, options, false, false) + port, err := strconv.Atoi(p) + if err != nil { + return err + } + ips, err := net.LookupIP(host) + if err != nil { + return err + } + for _, ip := range ips { + addr := &net.TCPAddr{ + IP: ip, + Port: port, + } + dialer, err := l.dialerFor(addr, sintf) + if err != nil { + continue + } + conn, err := dialer.DialContext(l.core.ctx, "tcp", addr.String()) + if err != nil { + continue + } + uri := strings.TrimRight(strings.SplitN(url.String(), "?", 2)[0], "/") + return l.handler(uri, info, conn, options, false, false) + } + return fmt.Errorf("failed to connect via %d addresses", len(ips)) } func (l *linkTCP) listen(url *url.URL, sintf string) (*Listener, error) { @@ -82,10 +98,9 @@ func (l *linkTCP) listen(url *url.URL, sintf string) (*Listener, error) { cancel() break } - laddr := conn.LocalAddr().(*net.TCPAddr) raddr := conn.RemoteAddr().(*net.TCPAddr) name := fmt.Sprintf("tcp://%s", raddr) - info := linkInfoFor("tcp", sintf, tcpIDFor(laddr, raddr)) + info := linkInfoFor("tcp", sintf, raddr.String()) if err = l.handler(name, info, conn, linkOptionsForListener(url), true, raddr.IP.IsLinkLocalUnicast()); err != nil { l.core.log.Errorln("Failed to create inbound link:", err) } @@ -180,16 +195,3 @@ func (l *linkTCP) dialerFor(dst *net.TCPAddr, sintf string) (*net.Dialer, error) } return dialer, nil } - -func tcpIDFor(local net.Addr, remoteAddr *net.TCPAddr) string { - if localAddr, ok := local.(*net.TCPAddr); ok && localAddr.IP.Equal(remoteAddr.IP) { - // Nodes running on the same host — include both the IP and port. - return remoteAddr.String() - } - if remoteAddr.IP.IsLinkLocalUnicast() { - // Nodes discovered via multicast — include the IP only. - return remoteAddr.IP.String() - } - // Nodes connected remotely — include both the IP and port. - return remoteAddr.String() -} diff --git a/src/core/link_tls.go b/src/core/link_tls.go index 4eeb8710..8e7f870a 100644 --- a/src/core/link_tls.go +++ b/src/core/link_tls.go @@ -13,6 +13,7 @@ import ( "math/big" "net" "net/url" + "strconv" "strings" "time" @@ -47,30 +48,45 @@ func (l *links) newLinkTLS(tcp *linkTCP) *linkTLS { } func (l *linkTLS) dial(url *url.URL, options linkOptions, sintf, sni string) error { - addr, err := net.ResolveTCPAddr("tcp", url.Host) - if err != nil { - return err - } - dialer, err := l.tcp.dialerFor(addr, sintf) - if err != nil { - return err - } - info := linkInfoFor("tls", sintf, tcpIDFor(dialer.LocalAddr, addr)) + info := linkInfoFor("tls", sintf, url.Host) if l.links.isConnectedTo(info) { return nil } - tlsconfig := l.config.Clone() - tlsconfig.ServerName = sni - tlsdialer := &tls.Dialer{ - NetDialer: dialer, - Config: tlsconfig, - } - conn, err := tlsdialer.DialContext(l.core.ctx, "tcp", addr.String()) + host, p, err := net.SplitHostPort(url.Host) if err != nil { return err } - uri := strings.TrimRight(strings.SplitN(url.String(), "?", 2)[0], "/") - return l.handler(uri, info, conn, options, false, false) + port, err := strconv.Atoi(p) + if err != nil { + return err + } + ips, err := net.LookupIP(host) + if err != nil { + return err + } + for _, ip := range ips { + addr := &net.TCPAddr{ + IP: ip, + Port: port, + } + dialer, err := l.tcp.dialerFor(addr, sintf) + if err != nil { + continue + } + tlsconfig := l.config.Clone() + tlsconfig.ServerName = sni + tlsdialer := &tls.Dialer{ + NetDialer: dialer, + Config: tlsconfig, + } + conn, err := tlsdialer.DialContext(l.core.ctx, "tcp", addr.String()) + if err != nil { + continue + } + uri := strings.TrimRight(strings.SplitN(url.String(), "?", 2)[0], "/") + return l.handler(uri, info, conn, options, false, false) + } + return fmt.Errorf("failed to connect via %d addresses", len(ips)) } func (l *linkTLS) listen(url *url.URL, sintf string) (*Listener, error) { @@ -105,10 +121,9 @@ func (l *linkTLS) listen(url *url.URL, sintf string) (*Listener, error) { cancel() break } - laddr := conn.LocalAddr().(*net.TCPAddr) raddr := conn.RemoteAddr().(*net.TCPAddr) name := fmt.Sprintf("tls://%s", raddr) - info := linkInfoFor("tls", sintf, tcpIDFor(laddr, raddr)) + info := linkInfoFor("tls", sintf, raddr.String()) if err = l.handler(name, info, conn, linkOptionsForListener(url), true, raddr.IP.IsLinkLocalUnicast()); err != nil { l.core.log.Errorln("Failed to create inbound link:", err) } From 6fed2a75d749c90471da8a8f10154af3de4e1772 Mon Sep 17 00:00:00 2001 From: majestrate Date: Tue, 8 Nov 2022 17:11:22 -0500 Subject: [PATCH 076/293] Make TLS certs never expire (#977) According to RFC5280 we can make TLS certs never expire by setting their `NotAfter` date to a value that is basically the end of time. Fixes #976. --- src/core/link_tls.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/core/link_tls.go b/src/core/link_tls.go index 4eeb8710..fbc6172e 100644 --- a/src/core/link_tls.go +++ b/src/core/link_tls.go @@ -120,20 +120,18 @@ func (l *linkTLS) listen(url *url.URL, sintf string) (*Listener, error) { return entry, nil } +// RFC5280 section 4.1.2.5 +var notAfterNeverExpires = time.Date(9999, time.December, 31, 23, 59, 59, 0, time.UTC) + func (l *linkTLS) generateConfig() (*tls.Config, error) { certBuf := &bytes.Buffer{} - - // TODO: because NotAfter is finite, we should add some mechanism to - // regenerate the certificate and restart the listeners periodically - // for nodes with very high uptimes. Perhaps regenerate certs and restart - // listeners every few months or so. cert := x509.Certificate{ SerialNumber: big.NewInt(1), Subject: pkix.Name{ CommonName: hex.EncodeToString(l.links.core.public[:]), }, NotBefore: time.Now(), - NotAfter: time.Now().Add(time.Hour * 24 * 365), + NotAfter: notAfterNeverExpires, KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, BasicConstraintsValid: true, From 0da871f5287addb555138c65cad93183411d8d84 Mon Sep 17 00:00:00 2001 From: solanav Date: Tue, 8 Nov 2022 23:19:43 +0100 Subject: [PATCH 077/293] Fix #884 (#916) * Fixed #884 * Remove yggdrasil and yggdrasilctl * Fixed #884 Co-authored-by: asolana Co-authored-by: solanav Co-authored-by: Neil Alexander --- cmd/yggdrasil/main.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 2d1da6b9..f85525d6 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -247,7 +247,12 @@ func run(args yggArgs, ctx context.Context) { return default: // No flags were provided, therefore print the list of flags to stdout. + fmt.Println("Usage:") flag.PrintDefaults() + + if args.getaddr || args.getsnet { + fmt.Println("\nError: You need to specify some config data using -useconf or -useconffile.") + } } // Have we got a working configuration? If we don't then it probably means // that neither -autoconf, -useconf or -useconffile were set above. Stop From 7efd66932f469c2e8ff6f695995d4008ac23c158 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 12 Nov 2022 11:30:03 +0000 Subject: [PATCH 078/293] Redial failed connections if possible (#983) --- contrib/mobile/mobile.go | 5 +++ src/core/api.go | 4 +-- src/core/core.go | 7 ++++ src/core/link.go | 67 +++++++++++++++++++++++++++++++++++--- src/core/link_socks.go | 20 +++++++----- src/core/link_tcp.go | 13 +++++--- src/core/link_tls.go | 14 +++++--- src/core/link_unix.go | 10 ++++-- src/multicast/multicast.go | 12 ++++++- 9 files changed, 124 insertions(+), 28 deletions(-) diff --git a/contrib/mobile/mobile.go b/contrib/mobile/mobile.go index ff22b9d7..3b3227bd 100644 --- a/contrib/mobile/mobile.go +++ b/contrib/mobile/mobile.go @@ -159,6 +159,11 @@ func (m *Yggdrasil) Stop() error { return nil } +// Retry resets the peer connection timer and tries to dial them immediately. +func (m *Yggdrasil) RetryPeersNow() { + m.core.RetryPeersNow() +} + // GenerateConfigJSON generates mobile-friendly configuration in JSON format func GenerateConfigJSON() []byte { nc := defaults.GenerateConfig() diff --git a/src/core/api.go b/src/core/api.go index ae783156..5accdeee 100644 --- a/src/core/api.go +++ b/src/core/api.go @@ -194,7 +194,7 @@ func (c *Core) AddPeer(uri string, sourceInterface string) error { if err != nil { return err } - info, err := c.links.call(u, sourceInterface) + info, err := c.links.call(u, sourceInterface, nil) if err != nil { return err } @@ -236,7 +236,7 @@ func (c *Core) RemovePeer(uri string, sourceInterface string) error { // This does not add the peer to the peer list, so if the connection drops, the // peer will not be called again automatically. func (c *Core) CallPeer(u *url.URL, sintf string) error { - _, err := c.links.call(u, sintf) + _, err := c.links.call(u, sintf, nil) return err } diff --git a/src/core/core.go b/src/core/core.go index 67f927a6..b096d1d7 100644 --- a/src/core/core.go +++ b/src/core/core.go @@ -121,6 +121,13 @@ func (c *Core) _addPeerLoop() { }) } +func (c *Core) RetryPeersNow() { + if c.addPeerTimer != nil && !c.addPeerTimer.Stop() { + <-c.addPeerTimer.C + } + c.Act(nil, c._addPeerLoop) +} + // Stop shuts down the Yggdrasil node. func (c *Core) Stop() { phony.Block(c, func() { diff --git a/src/core/link.go b/src/core/link.go index 550d7ec2..fa2e3325 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -34,6 +34,11 @@ type linkInfo struct { remote string // Remote name or address } +type linkDial struct { + url *url.URL + sintf string +} + type link struct { lname string links *links @@ -105,9 +110,10 @@ func (l *links) isConnectedTo(info linkInfo) bool { return isConnected } -func (l *links) call(u *url.URL, sintf string) (linkInfo, error) { - info := linkInfoFor(u.Scheme, sintf, u.Host) +func (l *links) call(u *url.URL, sintf string, errch chan<- error) (info linkInfo, err error) { + info = linkInfoFor(u.Scheme, sintf, u.Host) if l.isConnectedTo(info) { + close(errch) // already connected, no error return info, nil } options := linkOptions{ @@ -116,6 +122,7 @@ func (l *links) call(u *url.URL, sintf string) (linkInfo, error) { for _, pubkey := range u.Query()["key"] { sigPub, err := hex.DecodeString(pubkey) if err != nil { + close(errch) return info, fmt.Errorf("pinned key contains invalid hex characters") } var sigPubKey keyArray @@ -125,6 +132,7 @@ func (l *links) call(u *url.URL, sintf string) (linkInfo, error) { if p := u.Query().Get("priority"); p != "" { pi, err := strconv.ParseUint(p, 10, 8) if err != nil { + close(errch) return info, fmt.Errorf("priority invalid: %w", err) } options.priority = uint8(pi) @@ -132,15 +140,27 @@ func (l *links) call(u *url.URL, sintf string) (linkInfo, error) { switch info.linkType { case "tcp": go func() { + if errch != nil { + defer close(errch) + } if err := l.tcp.dial(u, options, sintf); err != nil && err != io.EOF { l.core.log.Warnf("Failed to dial TCP %s: %s\n", u.Host, err) + if errch != nil { + errch <- err + } } }() case "socks": go func() { + if errch != nil { + defer close(errch) + } if err := l.socks.dial(u, options); err != nil && err != io.EOF { l.core.log.Warnf("Failed to dial SOCKS %s: %s\n", u.Host, err) + if errch != nil { + errch <- err + } } }() @@ -163,19 +183,32 @@ func (l *links) call(u *url.URL, sintf string) (linkInfo, error) { } } go func() { + if errch != nil { + defer close(errch) + } if err := l.tls.dial(u, options, sintf, tlsSNI); err != nil && err != io.EOF { l.core.log.Warnf("Failed to dial TLS %s: %s\n", u.Host, err) + if errch != nil { + errch <- err + } } }() case "unix": go func() { + if errch != nil { + defer close(errch) + } if err := l.unix.dial(u, options, sintf); err != nil && err != io.EOF { l.core.log.Warnf("Failed to dial UNIX %s: %s\n", u.Host, err) + if errch != nil { + errch <- err + } } }() default: + close(errch) return info, errors.New("unknown call scheme: " + u.Scheme) } return info, nil @@ -197,7 +230,7 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { return listener, err } -func (l *links) create(conn net.Conn, name string, info linkInfo, incoming, force bool, options linkOptions) error { +func (l *links) create(conn net.Conn, dial *linkDial, name string, info linkInfo, incoming, force bool, options linkOptions) error { intf := link{ conn: &linkConn{ Conn: conn, @@ -211,14 +244,14 @@ func (l *links) create(conn net.Conn, name string, info linkInfo, incoming, forc force: force, } go func() { - if err := intf.handler(); err != nil { + if err := intf.handler(dial); err != nil { l.core.log.Errorf("Link handler %s error (%s): %s", name, conn.RemoteAddr(), err) } }() return nil } -func (intf *link) handler() error { +func (intf *link) handler(dial *linkDial) error { defer intf.conn.Close() // nolint:errcheck // Don't connect to this link more than once. @@ -321,6 +354,30 @@ func (intf *link) handler() error { intf.links.core.log.Infof("Disconnected %s %s: %s, source %s; error: %s", dir, strings.ToUpper(intf.info.linkType), remoteStr, localStr, err) } + + if !intf.incoming && dial != nil { + // The connection was one that we dialled, so wait a second and try to + // dial it again. + var retry func(attempt int) + retry = func(attempt int) { + // intf.links.core.log.Infof("Retrying %s (attempt %d of 5)...", dial.url.String(), attempt) + errch := make(chan error, 1) + if _, err := intf.links.call(dial.url, dial.sintf, errch); err != nil { + return + } + if err := <-errch; err != nil { + if attempt < 3 { + time.AfterFunc(time.Second, func() { + retry(attempt + 1) + }) + } + } + } + time.AfterFunc(time.Second, func() { + retry(1) + }) + } + return nil } diff --git a/src/core/link_socks.go b/src/core/link_socks.go index 036de992..4cdffa51 100644 --- a/src/core/link_socks.go +++ b/src/core/link_socks.go @@ -37,16 +37,20 @@ func (l *linkSOCKS) dial(url *url.URL, options linkOptions) error { if err != nil { return err } - return l.handler(url.String(), info, conn, options, false) + dial := &linkDial{ + url: url, + } + return l.handler(dial, info, conn, options, false) } -func (l *linkSOCKS) handler(name string, info linkInfo, conn net.Conn, options linkOptions, incoming bool) error { +func (l *linkSOCKS) handler(dial *linkDial, info linkInfo, conn net.Conn, options linkOptions, incoming bool) error { return l.links.create( - conn, // connection - name, // connection name - info, // connection info - incoming, // not incoming - false, // not forced - options, // connection options + conn, // connection + dial, // connection URL + dial.url.String(), // connection name + info, // connection info + incoming, // not incoming + false, // not forced + options, // connection options ) } diff --git a/src/core/link_tcp.go b/src/core/link_tcp.go index 9c3c3290..714ea9d9 100644 --- a/src/core/link_tcp.go +++ b/src/core/link_tcp.go @@ -47,8 +47,12 @@ func (l *linkTCP) dial(url *url.URL, options linkOptions, sintf string) error { if err != nil { return err } - uri := strings.TrimRight(strings.SplitN(url.String(), "?", 2)[0], "/") - return l.handler(uri, info, conn, options, false, false) + name := strings.TrimRight(strings.SplitN(url.String(), "?", 2)[0], "/") + dial := &linkDial{ + url: url, + sintf: sintf, + } + return l.handler(dial, name, info, conn, options, false, false) } func (l *linkTCP) listen(url *url.URL, sintf string) (*Listener, error) { @@ -86,7 +90,7 @@ func (l *linkTCP) listen(url *url.URL, sintf string) (*Listener, error) { raddr := conn.RemoteAddr().(*net.TCPAddr) name := fmt.Sprintf("tcp://%s", raddr) info := linkInfoFor("tcp", sintf, tcpIDFor(laddr, raddr)) - if err = l.handler(name, info, conn, linkOptionsForListener(url), true, raddr.IP.IsLinkLocalUnicast()); err != nil { + if err = l.handler(nil, name, info, conn, linkOptionsForListener(url), true, raddr.IP.IsLinkLocalUnicast()); err != nil { l.core.log.Errorln("Failed to create inbound link:", err) } } @@ -97,9 +101,10 @@ func (l *linkTCP) listen(url *url.URL, sintf string) (*Listener, error) { return entry, nil } -func (l *linkTCP) handler(name string, info linkInfo, conn net.Conn, options linkOptions, incoming, force bool) error { +func (l *linkTCP) handler(dial *linkDial, name string, info linkInfo, conn net.Conn, options linkOptions, incoming, force bool) error { return l.links.create( conn, // connection + dial, // connection URL name, // connection name info, // connection info incoming, // not incoming diff --git a/src/core/link_tls.go b/src/core/link_tls.go index fbc6172e..dda0e2fe 100644 --- a/src/core/link_tls.go +++ b/src/core/link_tls.go @@ -69,8 +69,12 @@ func (l *linkTLS) dial(url *url.URL, options linkOptions, sintf, sni string) err if err != nil { return err } - uri := strings.TrimRight(strings.SplitN(url.String(), "?", 2)[0], "/") - return l.handler(uri, info, conn, options, false, false) + name := strings.TrimRight(strings.SplitN(url.String(), "?", 2)[0], "/") + dial := &linkDial{ + url: url, + sintf: sintf, + } + return l.handler(dial, name, info, conn, options, false, false) } func (l *linkTLS) listen(url *url.URL, sintf string) (*Listener, error) { @@ -109,7 +113,7 @@ func (l *linkTLS) listen(url *url.URL, sintf string) (*Listener, error) { raddr := conn.RemoteAddr().(*net.TCPAddr) name := fmt.Sprintf("tls://%s", raddr) info := linkInfoFor("tls", sintf, tcpIDFor(laddr, raddr)) - if err = l.handler(name, info, conn, linkOptionsForListener(url), true, raddr.IP.IsLinkLocalUnicast()); err != nil { + if err = l.handler(nil, name, info, conn, linkOptionsForListener(url), true, raddr.IP.IsLinkLocalUnicast()); err != nil { l.core.log.Errorln("Failed to create inbound link:", err) } } @@ -165,6 +169,6 @@ func (l *linkTLS) generateConfig() (*tls.Config, error) { }, nil } -func (l *linkTLS) handler(name string, info linkInfo, conn net.Conn, options linkOptions, incoming, force bool) error { - return l.tcp.handler(name, info, conn, options, incoming, force) +func (l *linkTLS) handler(dial *linkDial, name string, info linkInfo, conn net.Conn, options linkOptions, incoming, force bool) error { + return l.tcp.handler(dial, name, info, conn, options, incoming, force) } diff --git a/src/core/link_unix.go b/src/core/link_unix.go index 50183a25..7f782575 100644 --- a/src/core/link_unix.go +++ b/src/core/link_unix.go @@ -45,7 +45,10 @@ func (l *linkUNIX) dial(url *url.URL, options linkOptions, _ string) error { if err != nil { return err } - return l.handler(url.String(), info, conn, options, false) + dial := &linkDial{ + url: url, + } + return l.handler(dial, url.String(), info, conn, options, false) } func (l *linkUNIX) listen(url *url.URL, _ string) (*Listener, error) { @@ -74,7 +77,7 @@ func (l *linkUNIX) listen(url *url.URL, _ string) (*Listener, error) { break } info := linkInfoFor("unix", "", url.String()) - if err = l.handler(url.String(), info, conn, linkOptionsForListener(url), true); err != nil { + if err = l.handler(nil, url.String(), info, conn, linkOptionsForListener(url), true); err != nil { l.core.log.Errorln("Failed to create inbound link:", err) } } @@ -85,9 +88,10 @@ func (l *linkUNIX) listen(url *url.URL, _ string) (*Listener, error) { return entry, nil } -func (l *linkUNIX) handler(name string, info linkInfo, conn net.Conn, options linkOptions, incoming bool) error { +func (l *linkUNIX) handler(dial *linkDial, name string, info linkInfo, conn net.Conn, options linkOptions, incoming bool) error { return l.links.create( conn, // connection + dial, // connection URL name, // connection name info, // connection info incoming, // not incoming diff --git a/src/multicast/multicast.go b/src/multicast/multicast.go index 8d7fbb7d..ec145230 100644 --- a/src/multicast/multicast.go +++ b/src/multicast/multicast.go @@ -30,6 +30,7 @@ type Multicast struct { _isOpen bool _listeners map[string]*listenerInfo _interfaces map[string]*interfaceInfo + _timer *time.Timer config struct { _groupAddr GroupAddress _interfaces map[MulticastInterface]struct{} @@ -207,6 +208,15 @@ func (m *Multicast) _getAllowedInterfaces() map[string]*interfaceInfo { return interfaces } +func (m *Multicast) AnnounceNow() { + phony.Block(m, func() { + if m._timer != nil && !m._timer.Stop() { + <-m._timer.C + } + m.Act(nil, m._announce) + }) +} + func (m *Multicast) _announce() { if !m._isOpen { return @@ -329,7 +339,7 @@ func (m *Multicast) _announce() { break } } - time.AfterFunc(time.Second, func() { + m._timer = time.AfterFunc(time.Second, func() { m.Act(nil, m._announce) }) } From e824c73e21bd06756189765ee5e39532a82a2a16 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 12 Nov 2022 11:56:50 +0000 Subject: [PATCH 079/293] Fix crash --- src/core/link.go | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/core/link.go b/src/core/link.go index fa2e3325..933e3983 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -113,7 +113,9 @@ func (l *links) isConnectedTo(info linkInfo) bool { func (l *links) call(u *url.URL, sintf string, errch chan<- error) (info linkInfo, err error) { info = linkInfoFor(u.Scheme, sintf, u.Host) if l.isConnectedTo(info) { - close(errch) // already connected, no error + if errch != nil { + close(errch) // already connected, no error + } return info, nil } options := linkOptions{ @@ -122,7 +124,9 @@ func (l *links) call(u *url.URL, sintf string, errch chan<- error) (info linkInf for _, pubkey := range u.Query()["key"] { sigPub, err := hex.DecodeString(pubkey) if err != nil { - close(errch) + if errch != nil { + close(errch) + } return info, fmt.Errorf("pinned key contains invalid hex characters") } var sigPubKey keyArray @@ -132,7 +136,9 @@ func (l *links) call(u *url.URL, sintf string, errch chan<- error) (info linkInf if p := u.Query().Get("priority"); p != "" { pi, err := strconv.ParseUint(p, 10, 8) if err != nil { - close(errch) + if errch != nil { + close(errch) + } return info, fmt.Errorf("priority invalid: %w", err) } options.priority = uint8(pi) @@ -208,7 +214,9 @@ func (l *links) call(u *url.URL, sintf string, errch chan<- error) (info linkInf }() default: - close(errch) + if errch != nil { + close(errch) + } return info, errors.New("unknown call scheme: " + u.Scheme) } return info, nil From 9df3bc0066d626d43d66588c70d14999e58f2d79 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 12 Nov 2022 15:26:43 +0000 Subject: [PATCH 080/293] Update to Arceliar/ironwood@846a97f5e5bb7b4002638c1c33790707a7060e8f --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index adaf7cf8..7c7da09c 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/yggdrasil-network/yggdrasil-go go 1.17 require ( - github.com/Arceliar/ironwood v0.0.0-20221025225125-45b4281814c2 + github.com/Arceliar/ironwood v0.0.0-20221112140720-846a97f5e5bb github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 github.com/cheggaaa/pb/v3 v3.0.8 github.com/gologme/log v1.2.0 @@ -12,7 +12,7 @@ require ( github.com/kardianos/minwinsvc v1.0.2 github.com/mitchellh/mapstructure v1.4.1 github.com/vishvananda/netlink v1.1.0 - golang.org/x/mobile v0.0.0-20221020085226-b36e6246172e + golang.org/x/mobile v0.0.0-20221110043201-43a038452099 golang.org/x/net v0.0.0-20221014081412-f15817d10f9b golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43 golang.org/x/text v0.3.8 diff --git a/go.sum b/go.sum index e398aca6..69558e3a 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Arceliar/ironwood v0.0.0-20221025225125-45b4281814c2 h1:Usab30pNT2i/vZvpXcN9uOr5IO1RZPcUqoGH0DIAPnU= -github.com/Arceliar/ironwood v0.0.0-20221025225125-45b4281814c2/go.mod h1:RP72rucOFm5udrnEzTmIWLRVGQiV/fSUAQXJ0RST/nk= +github.com/Arceliar/ironwood v0.0.0-20221112140720-846a97f5e5bb h1:O/+9TuKlOuaVagYOApNdOgf+PUbgM+mZ+bpo0L0wddM= +github.com/Arceliar/ironwood v0.0.0-20221112140720-846a97f5e5bb/go.mod h1:RP72rucOFm5udrnEzTmIWLRVGQiV/fSUAQXJ0RST/nk= github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 h1:WndgpSW13S32VLQ3ugUxx2EnnWmgba1kCqPkd4Gk1yQ= github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979/go.mod h1:6Lkn+/zJilRMsKmbmG1RPoamiArC6HS73xbwRyp3UyI= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= @@ -55,8 +55,8 @@ golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9t golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20221020085226-b36e6246172e h1:zSgtO19fpg781xknwqiQPmOHaASr6E7ZVlTseLd9Fx4= -golang.org/x/mobile v0.0.0-20221020085226-b36e6246172e/go.mod h1:aAjjkJNdrh3PMckS4B10TGS2nag27cbKR1y2BpUxsiY= +golang.org/x/mobile v0.0.0-20221110043201-43a038452099 h1:aIu0lKmfdgtn2uTj7JI2oN4TUrQvgB+wzTPO23bCKt8= +golang.org/x/mobile v0.0.0-20221110043201-43a038452099/go.mod h1:aAjjkJNdrh3PMckS4B10TGS2nag27cbKR1y2BpUxsiY= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= From cba667f28da202ec1d1bbbe24d98b50eb0680afe Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 12 Nov 2022 16:47:20 +0000 Subject: [PATCH 081/293] Fix race conditions (update to Arceliar/ironwood@2c0740b) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 7c7da09c..a8517360 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/yggdrasil-network/yggdrasil-go go 1.17 require ( - github.com/Arceliar/ironwood v0.0.0-20221112140720-846a97f5e5bb + github.com/Arceliar/ironwood v0.0.0-20221112164410-2c0740b8ceca github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 github.com/cheggaaa/pb/v3 v3.0.8 github.com/gologme/log v1.2.0 diff --git a/go.sum b/go.sum index 69558e3a..04819a68 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Arceliar/ironwood v0.0.0-20221112140720-846a97f5e5bb h1:O/+9TuKlOuaVagYOApNdOgf+PUbgM+mZ+bpo0L0wddM= -github.com/Arceliar/ironwood v0.0.0-20221112140720-846a97f5e5bb/go.mod h1:RP72rucOFm5udrnEzTmIWLRVGQiV/fSUAQXJ0RST/nk= +github.com/Arceliar/ironwood v0.0.0-20221112164410-2c0740b8ceca h1:tWBRn22W6q4aEb+jPUZZ3vaJjZGbJ/6ON6QVmzJFRlE= +github.com/Arceliar/ironwood v0.0.0-20221112164410-2c0740b8ceca/go.mod h1:RP72rucOFm5udrnEzTmIWLRVGQiV/fSUAQXJ0RST/nk= github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 h1:WndgpSW13S32VLQ3ugUxx2EnnWmgba1kCqPkd4Gk1yQ= github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979/go.mod h1:6Lkn+/zJilRMsKmbmG1RPoamiArC6HS73xbwRyp3UyI= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= From ae24f5de38885bc17adb63f5726459beb3fcaa1c Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 12 Nov 2022 16:55:23 +0000 Subject: [PATCH 082/293] Less aggressive key ratcheting (update to Arceliar/ironwood@bf5f12a) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index a8517360..74b88aa4 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/yggdrasil-network/yggdrasil-go go 1.17 require ( - github.com/Arceliar/ironwood v0.0.0-20221112164410-2c0740b8ceca + github.com/Arceliar/ironwood v0.0.0-20221112165445-bf5f12ab2b26 github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 github.com/cheggaaa/pb/v3 v3.0.8 github.com/gologme/log v1.2.0 diff --git a/go.sum b/go.sum index 04819a68..f3904fc0 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Arceliar/ironwood v0.0.0-20221112164410-2c0740b8ceca h1:tWBRn22W6q4aEb+jPUZZ3vaJjZGbJ/6ON6QVmzJFRlE= -github.com/Arceliar/ironwood v0.0.0-20221112164410-2c0740b8ceca/go.mod h1:RP72rucOFm5udrnEzTmIWLRVGQiV/fSUAQXJ0RST/nk= +github.com/Arceliar/ironwood v0.0.0-20221112165445-bf5f12ab2b26 h1:RJIZ+FIOx4v3SYaR236RwmBOyK6FJNA/+6Sq1CCf9M4= +github.com/Arceliar/ironwood v0.0.0-20221112165445-bf5f12ab2b26/go.mod h1:RP72rucOFm5udrnEzTmIWLRVGQiV/fSUAQXJ0RST/nk= github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 h1:WndgpSW13S32VLQ3ugUxx2EnnWmgba1kCqPkd4Gk1yQ= github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979/go.mod h1:6Lkn+/zJilRMsKmbmG1RPoamiArC6HS73xbwRyp3UyI= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= From 596f16aa6cf8e2cba07747ea2971c3ed4973caf8 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 15 Nov 2022 12:46:08 +0000 Subject: [PATCH 083/293] Reduce allocations in `encrypted` package (update to Arceliar/ironwood@ec61cea) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 74b88aa4..7d6b3bd5 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/yggdrasil-network/yggdrasil-go go 1.17 require ( - github.com/Arceliar/ironwood v0.0.0-20221112165445-bf5f12ab2b26 + github.com/Arceliar/ironwood v0.0.0-20221115123222-ec61cea2f439 github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 github.com/cheggaaa/pb/v3 v3.0.8 github.com/gologme/log v1.2.0 diff --git a/go.sum b/go.sum index f3904fc0..3c2f7310 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Arceliar/ironwood v0.0.0-20221112165445-bf5f12ab2b26 h1:RJIZ+FIOx4v3SYaR236RwmBOyK6FJNA/+6Sq1CCf9M4= -github.com/Arceliar/ironwood v0.0.0-20221112165445-bf5f12ab2b26/go.mod h1:RP72rucOFm5udrnEzTmIWLRVGQiV/fSUAQXJ0RST/nk= +github.com/Arceliar/ironwood v0.0.0-20221115123222-ec61cea2f439 h1:eOW6/XIs06TnUn9GPCnfv71CQZw8edP3u3mH3lZt6iM= +github.com/Arceliar/ironwood v0.0.0-20221115123222-ec61cea2f439/go.mod h1:RP72rucOFm5udrnEzTmIWLRVGQiV/fSUAQXJ0RST/nk= github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 h1:WndgpSW13S32VLQ3ugUxx2EnnWmgba1kCqPkd4Gk1yQ= github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979/go.mod h1:6Lkn+/zJilRMsKmbmG1RPoamiArC6HS73xbwRyp3UyI= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= From 48d278bd2a24021ff1ea3a990984b6a4d4ddaab7 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 15 Nov 2022 19:06:14 +0000 Subject: [PATCH 084/293] Version 0.4.7 changelog (#985) --- CHANGELOG.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index af827b21..9a144c13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,24 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - in case of vulnerabilities. --> +## [0.4.7] - 2022-11-19 + +### Added + +- Dropped outbound peerings will now try to reconnect after a single second, rather than waiting up to 60 seconds for the normal peer timer + +### Changed + +- Session encryption keys are now rotated at most once per minute, which reduces CPU usage and improves throughput on fast low latency links +- Buffers are now reused in the session encryption handler, which improves session throughput and reduces memory allocations +- Buffers are now reused in the router for DHT and path traffic, which improves overall routing throughput and reduces memory allocations + +### Fixed + +- A bug in the admin socket where requests fail unless `arguments` is specified has been fixed +- Certificates on TLS listeners will no longer expire after a year +- The `-address` and `-subnet` command line options now return a useful warning when no configuration is specified + ## [0.4.6] - 2022-10-25 ### Added From b0f6544b079240c17157072dd3d3dc8d5400a0a4 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 20 Nov 2022 21:14:33 +0000 Subject: [PATCH 085/293] Update changelog date --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a144c13..2a43a4d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,7 +26,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - in case of vulnerabilities. --> -## [0.4.7] - 2022-11-19 +## [0.4.7] - 2022-11-20 ### Added From 723097fbf61f447abd6cd027f183bdea30598054 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 26 Nov 2022 16:18:15 +0000 Subject: [PATCH 086/293] Deduplicate some logic --- src/core/link_tcp.go | 43 +++++++++++++++++++++++++++++++++++-------- src/core/link_tls.go | 35 +++++++++-------------------------- 2 files changed, 44 insertions(+), 34 deletions(-) diff --git a/src/core/link_tcp.go b/src/core/link_tcp.go index c8020fe4..60054d45 100644 --- a/src/core/link_tcp.go +++ b/src/core/link_tcp.go @@ -31,19 +31,26 @@ func (l *links) newLinkTCP() *linkTCP { return lt } -func (l *linkTCP) dial(url *url.URL, options linkOptions, sintf string) error { +type tcpDialer struct { + info linkInfo + dialer *net.Dialer + addr *net.TCPAddr +} + +func (l *linkTCP) dialersFor(url *url.URL, options linkOptions, sintf string) ([]*tcpDialer, error) { host, p, err := net.SplitHostPort(url.Host) if err != nil { - return err + return nil, err } port, err := strconv.Atoi(p) if err != nil { - return err + return nil, err } ips, err := net.LookupIP(host) if err != nil { - return err + return nil, err } + dialers := make([]*tcpDialer, 0, len(ips)) for _, ip := range ips { addr := &net.TCPAddr{ IP: ip, @@ -55,10 +62,30 @@ func (l *linkTCP) dial(url *url.URL, options linkOptions, sintf string) error { } info := linkInfoFor("tcp", sintf, tcpIDFor(dialer.LocalAddr, addr)) if l.links.isConnectedTo(info) { - return nil + return nil, nil } - conn, err := dialer.DialContext(l.core.ctx, "tcp", addr.String()) + dialers = append(dialers, &tcpDialer{ + info: info, + dialer: dialer, + addr: addr, + }) + } + return dialers, nil +} + +func (l *linkTCP) dial(url *url.URL, options linkOptions, sintf string) error { + dialers, err := l.dialersFor(url, options, sintf) + if err != nil { + return err + } + if len(dialers) == 0 { + return nil + } + for _, d := range dialers { + var conn net.Conn + conn, err = d.dialer.DialContext(l.core.ctx, "tcp", d.addr.String()) if err != nil { + l.core.log.Warnf("Failed to connect to %s: %s", d.addr, err) continue } name := strings.TrimRight(strings.SplitN(url.String(), "?", 2)[0], "/") @@ -66,9 +93,9 @@ func (l *linkTCP) dial(url *url.URL, options linkOptions, sintf string) error { url: url, sintf: sintf, } - return l.handler(dial, name, info, conn, options, false, false) + return l.handler(dial, name, d.info, conn, options, false, false) } - return fmt.Errorf("failed to connect via %d addresses", len(ips)) + return fmt.Errorf("failed to connect via %d address(es), last error: %w", len(dialers), err) } func (l *linkTCP) listen(url *url.URL, sintf string) (*Listener, error) { diff --git a/src/core/link_tls.go b/src/core/link_tls.go index 33ea4dc3..6323a723 100644 --- a/src/core/link_tls.go +++ b/src/core/link_tls.go @@ -13,7 +13,6 @@ import ( "math/big" "net" "net/url" - "strconv" "strings" "time" @@ -48,38 +47,22 @@ func (l *links) newLinkTLS(tcp *linkTCP) *linkTLS { } func (l *linkTLS) dial(url *url.URL, options linkOptions, sintf, sni string) error { - host, p, err := net.SplitHostPort(url.Host) + dialers, err := l.tcp.dialersFor(url, options, sintf) if err != nil { return err } - port, err := strconv.Atoi(p) - if err != nil { - return err + if len(dialers) == 0 { + return nil } - ips, err := net.LookupIP(host) - if err != nil { - return err - } - for _, ip := range ips { - addr := &net.TCPAddr{ - IP: ip, - Port: port, - } - dialer, err := l.tcp.dialerFor(addr, sintf) - if err != nil { - continue - } - info := linkInfoFor("tls", sintf, tcpIDFor(dialer.LocalAddr, addr)) - if l.links.isConnectedTo(info) { - return nil - } + for _, d := range dialers { tlsconfig := l.config.Clone() tlsconfig.ServerName = sni tlsdialer := &tls.Dialer{ - NetDialer: dialer, + NetDialer: d.dialer, Config: tlsconfig, } - conn, err := tlsdialer.DialContext(l.core.ctx, "tcp", addr.String()) + var conn net.Conn + conn, err = tlsdialer.DialContext(l.core.ctx, "tcp", d.addr.String()) if err != nil { continue } @@ -88,9 +71,9 @@ func (l *linkTLS) dial(url *url.URL, options linkOptions, sintf, sni string) err url: url, sintf: sintf, } - return l.handler(dial, name, info, conn, options, false, false) + return l.handler(dial, name, d.info, conn, options, false, false) } - return fmt.Errorf("failed to connect via %d addresses", len(ips)) + return fmt.Errorf("failed to connect via %d address(es), last error: %w", len(dialers), err) } func (l *linkTLS) listen(url *url.URL, sintf string) (*Listener, error) { From 9cbc71bc8a9d3b160ec2e166cf8c51584187cb4d Mon Sep 17 00:00:00 2001 From: anon Date: Sun, 18 Dec 2022 00:37:34 -0500 Subject: [PATCH 087/293] Added member to Logger struct expected by tun_bsd.go --- src/core/core.go | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/core.go b/src/core/core.go index b096d1d7..bbf5b63f 100644 --- a/src/core/core.go +++ b/src/core/core.go @@ -210,4 +210,5 @@ type Logger interface { Errorln(...interface{}) Debugf(string, ...interface{}) Debugln(...interface{}) + Traceln(...interface{}) } From 886281af7c23f75e1847c644ff98bd070046a8c6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 25 Feb 2023 02:28:24 +0000 Subject: [PATCH 088/293] Bump golang.org/x/net from 0.0.0-20221014081412-f15817d10f9b to 0.7.0 Bumps [golang.org/x/net](https://github.com/golang/net) from 0.0.0-20221014081412-f15817d10f9b to 0.7.0. - [Release notes](https://github.com/golang/net/releases) - [Commits](https://github.com/golang/net/commits/v0.7.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- go.mod | 6 +++--- go.sum | 13 +++++++------ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 7d6b3bd5..ca18ef04 100644 --- a/go.mod +++ b/go.mod @@ -13,9 +13,9 @@ require ( github.com/mitchellh/mapstructure v1.4.1 github.com/vishvananda/netlink v1.1.0 golang.org/x/mobile v0.0.0-20221110043201-43a038452099 - golang.org/x/net v0.0.0-20221014081412-f15817d10f9b - golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43 - golang.org/x/text v0.3.8 + golang.org/x/net v0.7.0 + golang.org/x/sys v0.5.0 + golang.org/x/text v0.7.0 golang.zx2c4.com/wireguard v0.0.0-20211017052713-f87e87af0d9a golang.zx2c4.com/wireguard/windows v0.4.12 ) diff --git a/go.sum b/go.sum index 3c2f7310..2292b71b 100644 --- a/go.sum +++ b/go.sum @@ -70,8 +70,8 @@ golang.org/x/net v0.0.0-20210927181540-4e4d966f7476/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211011170408-caeb26a5c8c0/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20221014081412-f15817d10f9b h1:tvrvnPFcdzp294diPnrdZZZ8XUt2Tyj7svb7X52iDuU= -golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -92,18 +92,19 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43 h1:OK7RB6t2WQX54srQQYSXMW8dF5C6/8+oA/s5QBmmto4= golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0= -golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= From 6d6c4089575dfbd1619623c442de4419b3445430 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 26 Feb 2023 21:31:20 +0000 Subject: [PATCH 089/293] Test against Go 1.20, maybe fix lint issue --- .github/workflows/ci.yml | 8 ++++---- src/tun/tun.go | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0c99ac06..77475403 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,7 +51,7 @@ jobs: strategy: fail-fast: false matrix: - goversion: ["1.17", "1.18", "1.19"] + goversion: ["1.17", "1.18", "1.19", "1.20"] name: Build & Test (Linux, Go ${{ matrix.goversion }}) needs: [lint] @@ -75,7 +75,7 @@ jobs: strategy: fail-fast: false matrix: - goversion: ["1.17", "1.18", "1.19"] + goversion: ["1.17", "1.18", "1.19", "1.20"] name: Build & Test (Windows, Go ${{ matrix.goversion }}) needs: [lint] @@ -99,7 +99,7 @@ jobs: strategy: fail-fast: false matrix: - goversion: ["1.17", "1.18", "1.19"] + goversion: ["1.17", "1.18", "1.19", "1.20"] name: Build & Test (macOS, Go ${{ matrix.goversion }}) needs: [lint] @@ -128,4 +128,4 @@ jobs: - name: Check all tests passed uses: re-actors/alls-green@release/v1 with: - jobs: ${{ toJSON(needs) }} \ No newline at end of file + jobs: ${{ toJSON(needs) }} diff --git a/src/tun/tun.go b/src/tun/tun.go index ce1bd169..9ec7bc90 100644 --- a/src/tun/tun.go +++ b/src/tun/tun.go @@ -107,7 +107,8 @@ func (tun *TunAdapter) _start() error { } tun.addr = tun.rwc.Address() tun.subnet = tun.rwc.Subnet() - addr := fmt.Sprintf("%s/%d", net.IP(tun.addr[:]).String(), 8*len(address.GetPrefix())-1) + prefix := address.GetPrefix() + addr := fmt.Sprintf("%s/%d", net.IP(tun.addr[:]).String(), 8*len(prefix)-1) if tun.config.name == "none" || tun.config.name == "dummy" { tun.log.Debugln("Not starting TUN as ifname is none or dummy") tun.isEnabled = false From 1dd1d0ab8c55bba222f4ef678011b97a0382316e Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 26 Feb 2023 21:32:26 +0000 Subject: [PATCH 090/293] Build packages with Go 1.20 --- .github/workflows/pkg.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pkg.yml b/.github/workflows/pkg.yml index f282cc21..dd43fce8 100644 --- a/.github/workflows/pkg.yml +++ b/.github/workflows/pkg.yml @@ -25,7 +25,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v3 with: - go-version: 1.19 + go-version: "1.20" - name: Build package env: @@ -56,7 +56,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v3 with: - go-version: 1.19 + go-version: "1.20" - name: Build package env: @@ -87,7 +87,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v3 with: - go-version: 1.19 + go-version: "1.20" - name: Build package run: sh contrib/msi/build-msi.sh ${{ matrix.pkgarch }} @@ -122,7 +122,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v3 with: - go-version: 1.19 + go-version: "1.20" - name: Build package env: From 38736358dda588e30cbce931f7f41fdf26f5d5f0 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 26 Feb 2023 21:35:56 +0000 Subject: [PATCH 091/293] Fix lint error properly this time --- src/tun/tun.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tun/tun.go b/src/tun/tun.go index 9ec7bc90..fcd597b8 100644 --- a/src/tun/tun.go +++ b/src/tun/tun.go @@ -108,7 +108,7 @@ func (tun *TunAdapter) _start() error { tun.addr = tun.rwc.Address() tun.subnet = tun.rwc.Subnet() prefix := address.GetPrefix() - addr := fmt.Sprintf("%s/%d", net.IP(tun.addr[:]).String(), 8*len(prefix)-1) + addr := fmt.Sprintf("%s/%d", net.IP(tun.addr[:]).String(), 8*len(prefix[:])-1) if tun.config.name == "none" || tun.config.name == "dummy" { tun.log.Debugln("Not starting TUN as ifname is none or dummy") tun.isEnabled = false From 83c1a810b57d99e68ebe9e04955c290b6b4fac1b Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 18 Mar 2023 12:14:32 +0000 Subject: [PATCH 092/293] New handshake, use `softcrdt` upstream --- go.mod | 2 +- go.sum | 4 +- src/core/api.go | 4 +- src/core/link.go | 16 +++--- src/core/version.go | 110 +++++++++++++++++++++++++++------------ src/core/version_test.go | 34 ++++++++++++ 6 files changed, 123 insertions(+), 47 deletions(-) create mode 100644 src/core/version_test.go diff --git a/go.mod b/go.mod index ca18ef04..4b4e19f9 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/yggdrasil-network/yggdrasil-go go 1.17 require ( - github.com/Arceliar/ironwood v0.0.0-20221115123222-ec61cea2f439 + github.com/Arceliar/ironwood v0.0.0-20230318003210-65aa386cab13 github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 github.com/cheggaaa/pb/v3 v3.0.8 github.com/gologme/log v1.2.0 diff --git a/go.sum b/go.sum index 2292b71b..e6e4a3ed 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Arceliar/ironwood v0.0.0-20221115123222-ec61cea2f439 h1:eOW6/XIs06TnUn9GPCnfv71CQZw8edP3u3mH3lZt6iM= -github.com/Arceliar/ironwood v0.0.0-20221115123222-ec61cea2f439/go.mod h1:RP72rucOFm5udrnEzTmIWLRVGQiV/fSUAQXJ0RST/nk= +github.com/Arceliar/ironwood v0.0.0-20230318003210-65aa386cab13 h1:z0PVz7aDDW5c+JVEW7b00N2JMGAfV6BHtTcOJ8zHKcU= +github.com/Arceliar/ironwood v0.0.0-20230318003210-65aa386cab13/go.mod h1:RP72rucOFm5udrnEzTmIWLRVGQiV/fSUAQXJ0RST/nk= github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 h1:WndgpSW13S32VLQ3ugUxx2EnnWmgba1kCqPkd4Gk1yQ= github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979/go.mod h1:6Lkn+/zJilRMsKmbmG1RPoamiArC6HS73xbwRyp3UyI= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= diff --git a/src/core/api.go b/src/core/api.go index 5accdeee..fc06b9c6 100644 --- a/src/core/api.go +++ b/src/core/api.go @@ -98,7 +98,7 @@ func (c *Core) GetDHT() []DHTEntryInfo { var info DHTEntryInfo info.Key = d.Key info.Port = d.Port - info.Rest = d.Rest + //info.Rest = d.Rest dhts = append(dhts, info) } return dhts @@ -110,7 +110,7 @@ func (c *Core) GetPaths() []PathEntryInfo { for _, p := range ps { var info PathEntryInfo info.Key = p.Key - info.Path = p.Path + //info.Path = p.Path paths = append(paths, info) } return paths diff --git a/src/core/link.go b/src/core/link.go index 933e3983..06776618 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -278,7 +278,7 @@ func (intf *link) handler(dial *linkDial) error { }) meta := version_getBaseMetadata() - meta.key = intf.links.core.public + meta.publicKey = intf.links.core.public metaBytes := meta.encode() if err := intf.conn.SetDeadline(time.Now().Add(time.Second * 6)); err != nil { return fmt.Errorf("failed to set handshake deadline: %w", err) @@ -311,8 +311,8 @@ func (intf *link) handler(dial *linkDial) error { intf.links.core.log.Debugf("%s: %s is incompatible version (local %s, remote %s)", connectError, intf.lname, - fmt.Sprintf("%d.%d", base.ver, base.minorVer), - fmt.Sprintf("%d.%d", meta.ver, meta.minorVer), + fmt.Sprintf("%d.%d", base.majorVer, base.minorVer), + fmt.Sprintf("%d.%d", meta.majorVer, meta.minorVer), ) return errors.New("remote node is incompatible version") } @@ -320,7 +320,7 @@ func (intf *link) handler(dial *linkDial) error { // check - in future versions we really should check a signature or something like that. if pinned := intf.options.pinnedEd25519Keys; len(pinned) > 0 { var key keyArray - copy(key[:], meta.key) + copy(key[:], meta.publicKey) if _, allowed := pinned[key]; !allowed { return fmt.Errorf("node public key that does not match pinned keys") } @@ -329,14 +329,14 @@ func (intf *link) handler(dial *linkDial) error { allowed := intf.links.core.config._allowedPublicKeys isallowed := len(allowed) == 0 for k := range allowed { - if bytes.Equal(k[:], meta.key) { + if bytes.Equal(k[:], meta.publicKey) { isallowed = true break } } if intf.incoming && !intf.force && !isallowed { _ = intf.close() - return fmt.Errorf("node public key %q is not in AllowedPublicKeys", hex.EncodeToString(meta.key)) + return fmt.Errorf("node public key %q is not in AllowedPublicKeys", hex.EncodeToString(meta.publicKey)) } phony.Block(intf.links, func() { @@ -347,13 +347,13 @@ func (intf *link) handler(dial *linkDial) error { if intf.incoming { dir = "inbound" } - remoteAddr := net.IP(address.AddrForKey(meta.key)[:]).String() + remoteAddr := net.IP(address.AddrForKey(meta.publicKey)[:]).String() remoteStr := fmt.Sprintf("%s@%s", remoteAddr, intf.info.remote) localStr := intf.conn.LocalAddr() intf.links.core.log.Infof("Connected %s %s: %s, source %s", dir, strings.ToUpper(intf.info.linkType), remoteStr, localStr) - err = intf.links.core.HandleConn(meta.key, intf.conn, intf.options.priority) + err = intf.links.core.HandleConn(meta.publicKey, intf.conn, intf.options.priority) switch err { case io.EOF, net.ErrClosed, nil: intf.links.core.log.Infof("Disconnected %s %s: %s, source %s", diff --git a/src/core/version.go b/src/core/version.go index 0bfbbcbc..3787d1e0 100644 --- a/src/core/version.go +++ b/src/core/version.go @@ -4,65 +4,107 @@ package core // Used in the initial connection setup and key exchange // Some of this could arguably go in wire.go instead -import "crypto/ed25519" +import ( + "bytes" + "crypto/ed25519" + "encoding/binary" +) // This is the version-specific metadata exchanged at the start of a connection. // It must always begin with the 4 bytes "meta" and a wire formatted uint64 major version number. // The current version also includes a minor version number, and the box/sig/link keys that need to be exchanged to open a connection. type version_metadata struct { - meta [4]byte - ver uint8 // 1 byte in this version - // Everything after this point potentially depends on the version number, and is subject to change in future versions - minorVer uint8 // 1 byte in this version - key ed25519.PublicKey + majorVer uint16 + minorVer uint16 + publicKey ed25519.PublicKey + priority uint8 } +const ( + ProtocolVersionMajor uint16 = 0 + ProtocolVersionMinor uint16 = 5 +) + +const ( + metaVersionMajor uint16 = iota // uint16 + metaVersionMinor // uint16 + metaPublicKey // [32]byte + metaPriority // uint8 +) + // Gets a base metadata with no keys set, but with the correct version numbers. func version_getBaseMetadata() version_metadata { return version_metadata{ - meta: [4]byte{'m', 'e', 't', 'a'}, - ver: 0, - minorVer: 4, + majorVer: ProtocolVersionMajor, + minorVer: ProtocolVersionMinor, } } -// Gets the length of the metadata for this version, used to know how many bytes to read from the start of a connection. -func version_getMetaLength() (mlen int) { - mlen += 4 // meta - mlen++ // ver, as long as it's < 127, which it is in this version - mlen++ // minorVer, as long as it's < 127, which it is in this version - mlen += ed25519.PublicKeySize // key - return -} - // Encodes version metadata into its wire format. func (m *version_metadata) encode() []byte { - bs := make([]byte, 0, version_getMetaLength()) - bs = append(bs, m.meta[:]...) - bs = append(bs, m.ver) - bs = append(bs, m.minorVer) - bs = append(bs, m.key[:]...) - if len(bs) != version_getMetaLength() { - panic("Inconsistent metadata length") - } + bs := make([]byte, 0, 64) + bs = append(bs, 'm', 'e', 't', 'a') + + bs = binary.BigEndian.AppendUint16(bs, metaVersionMajor) + bs = binary.BigEndian.AppendUint16(bs, 2) + bs = binary.BigEndian.AppendUint16(bs, m.majorVer) + + bs = binary.BigEndian.AppendUint16(bs, metaVersionMinor) + bs = binary.BigEndian.AppendUint16(bs, 2) + bs = binary.BigEndian.AppendUint16(bs, m.minorVer) + + bs = binary.BigEndian.AppendUint16(bs, metaPublicKey) + bs = binary.BigEndian.AppendUint16(bs, ed25519.PublicKeySize) + bs = append(bs, m.publicKey[:]...) + + bs = binary.BigEndian.AppendUint16(bs, metaPriority) + bs = binary.BigEndian.AppendUint16(bs, 1) + bs = append(bs, m.priority) + return bs } // Decodes version metadata from its wire format into the struct. func (m *version_metadata) decode(bs []byte) bool { - if len(bs) != version_getMetaLength() { + meta := [4]byte{'m', 'e', 't', 'a'} + if !bytes.Equal(bs[:4], meta[:]) { return false } - offset := 0 - offset += copy(m.meta[:], bs[offset:]) - m.ver, offset = bs[offset], offset+1 - m.minorVer, offset = bs[offset], offset+1 - m.key = append([]byte(nil), bs[offset:]...) + for bs = bs[4:]; len(bs) >= 4; { + op := binary.BigEndian.Uint16(bs[:2]) + oplen := binary.BigEndian.Uint16(bs[2:4]) + if bs = bs[4:]; len(bs) < int(oplen) { + break + } + switch op { + case metaVersionMajor: + m.majorVer = binary.BigEndian.Uint16(bs[:2]) + + case metaVersionMinor: + m.minorVer = binary.BigEndian.Uint16(bs[:2]) + + case metaPublicKey: + m.publicKey = make(ed25519.PublicKey, ed25519.PublicKeySize) + copy(m.publicKey, bs[:ed25519.PublicKeySize]) + + case metaPriority: + m.priority = bs[0] + } + bs = bs[oplen:] + } return true } // Checks that the "meta" bytes and the version numbers are the expected values. func (m *version_metadata) check() bool { - base := version_getBaseMetadata() - return base.meta == m.meta && base.ver == m.ver && base.minorVer == m.minorVer + switch { + case m.majorVer != ProtocolVersionMajor: + return false + case m.minorVer != ProtocolVersionMinor: + return false + case len(m.publicKey) != ed25519.PublicKeySize: + return false + default: + return true + } } diff --git a/src/core/version_test.go b/src/core/version_test.go new file mode 100644 index 00000000..6fb7895c --- /dev/null +++ b/src/core/version_test.go @@ -0,0 +1,34 @@ +package core + +import ( + "crypto/ed25519" + "math/rand" + "reflect" + "testing" +) + +func TestVersionRoundtrip(t *testing.T) { + for _, test := range []*version_metadata{ + {majorVer: 1}, + {majorVer: 256}, + {majorVer: 2, minorVer: 4}, + {majorVer: 2, minorVer: 257}, + {majorVer: 258, minorVer: 259}, + {majorVer: 3, minorVer: 5, priority: 6}, + {majorVer: 260, minorVer: 261, priority: 7}, + } { + // Generate a random public key for each time, since it is + // a required field. + test.publicKey = make(ed25519.PublicKey, ed25519.PublicKeySize) + rand.Read(test.publicKey) + + encoded := test.encode() + decoded := &version_metadata{} + if !decoded.decode(encoded) { + t.Fatalf("failed to decode") + } + if !reflect.DeepEqual(test, decoded) { + t.Fatalf("round-trip failed\nwant: %+v\n got: %+v", test, decoded) + } + } +} From a148f4cfecd92d4464da9431a1416ad413b4f76d Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 19 Mar 2023 10:33:07 +0000 Subject: [PATCH 093/293] More updates for Ygg v0.5 --- cmd/yggdrasilctl/main.go | 6 +++--- contrib/mobile/mobile.go | 4 ++-- go.mod | 2 ++ go.sum | 4 ++-- src/admin/getpaths.go | 8 ++++---- src/admin/getpeers.go | 2 -- src/admin/getself.go | 14 +++++++------- src/core/api.go | 34 +++++++++++++++++----------------- src/core/link.go | 2 +- src/core/proto.go | 4 ++-- src/ipv6rwc/ipv6rwc.go | 10 ++++++---- 11 files changed, 46 insertions(+), 44 deletions(-) diff --git a/cmd/yggdrasilctl/main.go b/cmd/yggdrasilctl/main.go index c9b1522a..6aed8ac5 100644 --- a/cmd/yggdrasilctl/main.go +++ b/cmd/yggdrasilctl/main.go @@ -165,7 +165,7 @@ func run() int { table.Append([]string{"Build version:", resp.BuildVersion}) table.Append([]string{"IPv6 address:", resp.IPAddress}) table.Append([]string{"IPv6 subnet:", resp.Subnet}) - table.Append([]string{"Coordinates:", fmt.Sprintf("%v", resp.Coords)}) + table.Append([]string{"Routing table size:", fmt.Sprintf("%d", resp.RoutingEntries)}) table.Append([]string{"Public key:", resp.PublicKey}) table.Render() @@ -210,12 +210,12 @@ func run() int { if err := json.Unmarshal(recv.Response, &resp); err != nil { panic(err) } - table.SetHeader([]string{"Public Key", "IP Address", "Path"}) + table.SetHeader([]string{"Public Key", "IP Address", "Seq"}) for _, p := range resp.Paths { table.Append([]string{ p.PublicKey, p.IPAddress, - fmt.Sprintf("%v", p.Path), + fmt.Sprintf("%d", p.Sequence), }) } table.Render() diff --git a/contrib/mobile/mobile.go b/contrib/mobile/mobile.go index 3b3227bd..79937997 100644 --- a/contrib/mobile/mobile.go +++ b/contrib/mobile/mobile.go @@ -3,7 +3,6 @@ package mobile import ( "encoding/hex" "encoding/json" - "fmt" "net" "regexp" @@ -193,7 +192,8 @@ func (m *Yggdrasil) GetPublicKeyString() string { // GetCoordsString gets the node's coordinates func (m *Yggdrasil) GetCoordsString() string { - return fmt.Sprintf("%v", m.core.GetSelf().Coords) + return "N/A" + // return fmt.Sprintf("%v", m.core.GetSelf().Coords) } func (m *Yggdrasil) GetPeersJSON() (result string) { diff --git a/go.mod b/go.mod index 4b4e19f9..5ee3b140 100644 --- a/go.mod +++ b/go.mod @@ -2,6 +2,8 @@ module github.com/yggdrasil-network/yggdrasil-go go 1.17 +replace github.com/Arceliar/ironwood => github.com/neilalexander/ironwood v0.0.0-20230319103146-3ffd3d07e834 + require ( github.com/Arceliar/ironwood v0.0.0-20230318003210-65aa386cab13 github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 diff --git a/go.sum b/go.sum index e6e4a3ed..1a06a7c2 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,3 @@ -github.com/Arceliar/ironwood v0.0.0-20230318003210-65aa386cab13 h1:z0PVz7aDDW5c+JVEW7b00N2JMGAfV6BHtTcOJ8zHKcU= -github.com/Arceliar/ironwood v0.0.0-20230318003210-65aa386cab13/go.mod h1:RP72rucOFm5udrnEzTmIWLRVGQiV/fSUAQXJ0RST/nk= github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 h1:WndgpSW13S32VLQ3ugUxx2EnnWmgba1kCqPkd4Gk1yQ= github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979/go.mod h1:6Lkn+/zJilRMsKmbmG1RPoamiArC6HS73xbwRyp3UyI= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= @@ -32,6 +30,8 @@ github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4 github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/neilalexander/ironwood v0.0.0-20230319103146-3ffd3d07e834 h1:HwRcrQhhthIPKBVZVHlwkKpxZxwNv0QcwIA0Gb+5Dtk= +github.com/neilalexander/ironwood v0.0.0-20230319103146-3ffd3d07e834/go.mod h1:RP72rucOFm5udrnEzTmIWLRVGQiV/fSUAQXJ0RST/nk= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= diff --git a/src/admin/getpaths.go b/src/admin/getpaths.go index fbd52481..2d5db0fb 100644 --- a/src/admin/getpaths.go +++ b/src/admin/getpaths.go @@ -17,9 +17,9 @@ type GetPathsResponse struct { } type PathEntry struct { - IPAddress string `json:"address"` - PublicKey string `json:"key"` - Path []uint64 `json:"path"` + IPAddress string `json:"address"` + PublicKey string `json:"key"` + Sequence uint64 `json:"sequence"` } func (a *AdminSocket) getPathsHandler(req *GetPathsRequest, res *GetPathsResponse) error { @@ -30,7 +30,7 @@ func (a *AdminSocket) getPathsHandler(req *GetPathsRequest, res *GetPathsRespons res.Paths = append(res.Paths, PathEntry{ IPAddress: net.IP(addr[:]).String(), PublicKey: hex.EncodeToString(p.Key), - Path: p.Path, + Sequence: p.Sequence, }) } sort.SliceStable(res.Paths, func(i, j int) bool { diff --git a/src/admin/getpeers.go b/src/admin/getpeers.go index d51b184d..3d522fb9 100644 --- a/src/admin/getpeers.go +++ b/src/admin/getpeers.go @@ -20,7 +20,6 @@ type PeerEntry struct { PublicKey string `json:"key"` Port uint64 `json:"port"` Priority uint64 `json:"priority"` - Coords []uint64 `json:"coords"` Remote string `json:"remote"` RXBytes DataUnit `json:"bytes_recvd"` TXBytes DataUnit `json:"bytes_sent"` @@ -37,7 +36,6 @@ func (a *AdminSocket) getPeersHandler(req *GetPeersRequest, res *GetPeersRespons PublicKey: hex.EncodeToString(p.Key), Port: p.Port, Priority: uint64(p.Priority), // can't be uint8 thanks to gobind - Coords: p.Coords, Remote: p.Remote, RXBytes: DataUnit(p.RXBytes), TXBytes: DataUnit(p.TXBytes), diff --git a/src/admin/getself.go b/src/admin/getself.go index f42dc750..9b052101 100644 --- a/src/admin/getself.go +++ b/src/admin/getself.go @@ -9,12 +9,12 @@ import ( type GetSelfRequest struct{} type GetSelfResponse struct { - BuildName string `json:"build_name"` - BuildVersion string `json:"build_version"` - PublicKey string `json:"key"` - IPAddress string `json:"address"` - Coords []uint64 `json:"coords"` - Subnet string `json:"subnet"` + BuildName string `json:"build_name"` + BuildVersion string `json:"build_version"` + PublicKey string `json:"key"` + IPAddress string `json:"address"` + RoutingEntries uint64 `json:"routing_entries"` + Subnet string `json:"subnet"` } func (a *AdminSocket) getSelfHandler(req *GetSelfRequest, res *GetSelfResponse) error { @@ -25,6 +25,6 @@ func (a *AdminSocket) getSelfHandler(req *GetSelfRequest, res *GetSelfResponse) res.PublicKey = hex.EncodeToString(self.Key[:]) res.IPAddress = a.core.Address().String() res.Subnet = snet.String() - res.Coords = self.Coords + res.RoutingEntries = self.RoutingEntries return nil } diff --git a/src/core/api.go b/src/core/api.go index fc06b9c6..c617a209 100644 --- a/src/core/api.go +++ b/src/core/api.go @@ -14,9 +14,8 @@ import ( ) type SelfInfo struct { - Key ed25519.PublicKey - Root ed25519.PublicKey - Coords []uint64 + Key ed25519.PublicKey + RoutingEntries uint64 } type PeerInfo struct { @@ -38,8 +37,8 @@ type DHTEntryInfo struct { } type PathEntryInfo struct { - Key ed25519.PublicKey - Path []uint64 + Key ed25519.PublicKey + Sequence uint64 } type SessionInfo struct { @@ -53,13 +52,12 @@ func (c *Core) GetSelf() SelfInfo { var self SelfInfo s := c.PacketConn.PacketConn.Debug.GetSelf() self.Key = s.Key - self.Root = s.Root - self.Coords = s.Coords + self.RoutingEntries = s.RoutingEntries return self } func (c *Core) GetPeers() []PeerInfo { - var peers []PeerInfo + peers := []PeerInfo{} names := make(map[net.Conn]string) phony.Block(&c.links, func() { for _, info := range c.links._links { @@ -74,17 +72,18 @@ func (c *Core) GetPeers() []PeerInfo { var info PeerInfo info.Key = p.Key info.Root = p.Root - info.Coords = p.Coords info.Port = p.Port info.Priority = p.Priority - info.Remote = p.Conn.RemoteAddr().String() - if name := names[p.Conn]; name != "" { - info.Remote = name - } - if linkconn, ok := p.Conn.(*linkConn); ok { - info.RXBytes = atomic.LoadUint64(&linkconn.rx) - info.TXBytes = atomic.LoadUint64(&linkconn.tx) - info.Uptime = time.Since(linkconn.up) + if p.Conn != nil { + info.Remote = p.Conn.RemoteAddr().String() + if linkconn, ok := p.Conn.(*linkConn); ok { + info.RXBytes = atomic.LoadUint64(&linkconn.rx) + info.TXBytes = atomic.LoadUint64(&linkconn.tx) + info.Uptime = time.Since(linkconn.up) + } + if name := names[p.Conn]; name != "" { + info.Remote = name + } } peers = append(peers, info) } @@ -110,6 +109,7 @@ func (c *Core) GetPaths() []PathEntryInfo { for _, p := range ps { var info PathEntryInfo info.Key = p.Key + info.Sequence = p.Sequence //info.Path = p.Path paths = append(paths, info) } diff --git a/src/core/link.go b/src/core/link.go index 06776618..4ca854b3 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -253,7 +253,7 @@ func (l *links) create(conn net.Conn, dial *linkDial, name string, info linkInfo } go func() { if err := intf.handler(dial); err != nil { - l.core.log.Errorf("Link handler %s error (%s): %s", name, conn.RemoteAddr(), err) + //l.core.log.Errorf("Link handler %s error (%s): %s", name, conn.RemoteAddr(), err) } }() return nil diff --git a/src/core/proto.go b/src/core/proto.go index 3c68c0e3..186e9b8d 100644 --- a/src/core/proto.go +++ b/src/core/proto.go @@ -126,8 +126,8 @@ func (p *protoHandler) sendGetSelfRequest(key keyArray, callback func([]byte)) { func (p *protoHandler) _handleGetSelfRequest(key keyArray) { self := p.core.GetSelf() res := map[string]string{ - "key": hex.EncodeToString(self.Key[:]), - "coords": fmt.Sprintf("%v", self.Coords), + "key": hex.EncodeToString(self.Key[:]), + "routing_entries": fmt.Sprintf("%v", self.RoutingEntries), } bs, err := json.Marshal(res) // FIXME this puts keys in base64, not hex if err != nil { diff --git a/src/ipv6rwc/ipv6rwc.go b/src/ipv6rwc/ipv6rwc.go index bbaa8707..a3d65b61 100644 --- a/src/ipv6rwc/ipv6rwc.go +++ b/src/ipv6rwc/ipv6rwc.go @@ -57,10 +57,10 @@ func (k *keyStore) init(c *core.Core) { k.core = c k.address = *address.AddrForKey(k.core.PublicKey()) k.subnet = *address.SubnetForKey(k.core.PublicKey()) - if err := k.core.SetOutOfBandHandler(k.oobHandler); err != nil { + /*if err := k.core.SetOutOfBandHandler(k.oobHandler); err != nil { err = fmt.Errorf("tun.core.SetOutOfBandHander: %w", err) panic(err) - } + }*/ k.keyToInfo = make(map[keyArray]*keyInfo) k.addrToInfo = make(map[address.Address]*keyInfo) k.addrBuffer = make(map[address.Address]*buffer) @@ -202,13 +202,15 @@ func (k *keyStore) oobHandler(fromKey, toKey ed25519.PublicKey, data []byte) { func (k *keyStore) sendKeyLookup(partial ed25519.PublicKey) { sig := ed25519.Sign(k.core.PrivateKey(), partial[:]) bs := append([]byte{typeKeyLookup}, sig...) - _ = k.core.SendOutOfBand(partial, bs) + //_ = k.core.SendOutOfBand(partial, bs) + _ = bs } func (k *keyStore) sendKeyResponse(dest ed25519.PublicKey) { sig := ed25519.Sign(k.core.PrivateKey(), dest[:]) bs := append([]byte{typeKeyResponse}, sig...) - _ = k.core.SendOutOfBand(dest, bs) + //_ = k.core.SendOutOfBand(dest, bs) + _ = bs } func (k *keyStore) readPC(p []byte) (int, error) { From 5a243d5b9587c7dfa12b00163574e119c76d0f7e Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 19 Mar 2023 21:44:34 +0000 Subject: [PATCH 094/293] Update ironwood replace --- go.mod | 4 ++-- go.sum | 8 ++++---- src/core/link.go | 2 +- src/ipv6rwc/ipv6rwc.go | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 5ee3b140..6bd66bc6 100644 --- a/go.mod +++ b/go.mod @@ -2,11 +2,11 @@ module github.com/yggdrasil-network/yggdrasil-go go 1.17 -replace github.com/Arceliar/ironwood => github.com/neilalexander/ironwood v0.0.0-20230319103146-3ffd3d07e834 +replace github.com/Arceliar/ironwood => github.com/Arceliar/ironwood v0.0.0-20230319212913-807cbd557758 require ( github.com/Arceliar/ironwood v0.0.0-20230318003210-65aa386cab13 - github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 + github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d github.com/cheggaaa/pb/v3 v3.0.8 github.com/gologme/log v1.2.0 github.com/hashicorp/go-syslog v1.0.0 diff --git a/go.sum b/go.sum index 1a06a7c2..0a8675a0 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ -github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 h1:WndgpSW13S32VLQ3ugUxx2EnnWmgba1kCqPkd4Gk1yQ= -github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979/go.mod h1:6Lkn+/zJilRMsKmbmG1RPoamiArC6HS73xbwRyp3UyI= +github.com/Arceliar/ironwood v0.0.0-20230319212913-807cbd557758 h1:sPKt902XGRxXWQ/xtnrSnVyI8yBMR0Sx7ZsbHqOkUIk= +github.com/Arceliar/ironwood v0.0.0-20230319212913-807cbd557758/go.mod h1:PhT70gxs32jSoxpi5gLlvCguWTzbpaqnNRTY6GgFPBY= +github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM= +github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= @@ -30,8 +32,6 @@ github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4 github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/neilalexander/ironwood v0.0.0-20230319103146-3ffd3d07e834 h1:HwRcrQhhthIPKBVZVHlwkKpxZxwNv0QcwIA0Gb+5Dtk= -github.com/neilalexander/ironwood v0.0.0-20230319103146-3ffd3d07e834/go.mod h1:RP72rucOFm5udrnEzTmIWLRVGQiV/fSUAQXJ0RST/nk= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= diff --git a/src/core/link.go b/src/core/link.go index 4ca854b3..06776618 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -253,7 +253,7 @@ func (l *links) create(conn net.Conn, dial *linkDial, name string, info linkInfo } go func() { if err := intf.handler(dial); err != nil { - //l.core.log.Errorf("Link handler %s error (%s): %s", name, conn.RemoteAddr(), err) + l.core.log.Errorf("Link handler %s error (%s): %s", name, conn.RemoteAddr(), err) } }() return nil diff --git a/src/ipv6rwc/ipv6rwc.go b/src/ipv6rwc/ipv6rwc.go index a3d65b61..182aa9db 100644 --- a/src/ipv6rwc/ipv6rwc.go +++ b/src/ipv6rwc/ipv6rwc.go @@ -177,7 +177,7 @@ func (k *keyStore) resetTimeout(info *keyInfo) { }) } -func (k *keyStore) oobHandler(fromKey, toKey ed25519.PublicKey, data []byte) { +func (k *keyStore) oobHandler(fromKey, toKey ed25519.PublicKey, data []byte) { // nolint:unused if len(data) != 1+ed25519.SignatureSize { return } @@ -206,7 +206,7 @@ func (k *keyStore) sendKeyLookup(partial ed25519.PublicKey) { _ = bs } -func (k *keyStore) sendKeyResponse(dest ed25519.PublicKey) { +func (k *keyStore) sendKeyResponse(dest ed25519.PublicKey) { // nolint:unused sig := ed25519.Sign(k.core.PrivateKey(), dest[:]) bs := append([]byte{typeKeyResponse}, sig...) //_ = k.core.SendOutOfBand(dest, bs) From 5b6d9d52f302ee6d0ef56235abd4a682c40e24b7 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sun, 26 Mar 2023 16:12:45 -0500 Subject: [PATCH 095/293] update ironwood replace, update ipv6rwc to work (may need updates later if interface changes) --- go.mod | 2 +- go.sum | 4 +- src/ipv6rwc/ipv6rwc.go | 96 +++++++++++++++++++++++++----------------- 3 files changed, 61 insertions(+), 41 deletions(-) diff --git a/go.mod b/go.mod index 6bd66bc6..f94b5217 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module github.com/yggdrasil-network/yggdrasil-go go 1.17 -replace github.com/Arceliar/ironwood => github.com/Arceliar/ironwood v0.0.0-20230319212913-807cbd557758 +replace github.com/Arceliar/ironwood => github.com/Arceliar/ironwood v0.0.0-20230326182230-e1880a231350 require ( github.com/Arceliar/ironwood v0.0.0-20230318003210-65aa386cab13 diff --git a/go.sum b/go.sum index 0a8675a0..cbe8346b 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Arceliar/ironwood v0.0.0-20230319212913-807cbd557758 h1:sPKt902XGRxXWQ/xtnrSnVyI8yBMR0Sx7ZsbHqOkUIk= -github.com/Arceliar/ironwood v0.0.0-20230319212913-807cbd557758/go.mod h1:PhT70gxs32jSoxpi5gLlvCguWTzbpaqnNRTY6GgFPBY= +github.com/Arceliar/ironwood v0.0.0-20230326182230-e1880a231350 h1:9dsw9bwJKfwC/bohTvFsob7h4YeZkBI14eDtbY4WtTg= +github.com/Arceliar/ironwood v0.0.0-20230326182230-e1880a231350/go.mod h1:PhT70gxs32jSoxpi5gLlvCguWTzbpaqnNRTY6GgFPBY= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= diff --git a/src/ipv6rwc/ipv6rwc.go b/src/ipv6rwc/ipv6rwc.go index 182aa9db..34924b58 100644 --- a/src/ipv6rwc/ipv6rwc.go +++ b/src/ipv6rwc/ipv6rwc.go @@ -35,9 +35,9 @@ type keyStore struct { mutex sync.Mutex keyToInfo map[keyArray]*keyInfo addrToInfo map[address.Address]*keyInfo - addrBuffer map[address.Address]*buffer + //addrBuffer map[address.Address]*buffer subnetToInfo map[address.Subnet]*keyInfo - subnetBuffer map[address.Subnet]*buffer + //subnetBuffer map[address.Subnet]*buffer mtu uint64 } @@ -63,9 +63,9 @@ func (k *keyStore) init(c *core.Core) { }*/ k.keyToInfo = make(map[keyArray]*keyInfo) k.addrToInfo = make(map[address.Address]*keyInfo) - k.addrBuffer = make(map[address.Address]*buffer) + //k.addrBuffer = make(map[address.Address]*buffer) k.subnetToInfo = make(map[address.Subnet]*keyInfo) - k.subnetBuffer = make(map[address.Subnet]*buffer) + //k.subnetBuffer = make(map[address.Subnet]*buffer) k.mtu = 1280 // Default to something safe, expect user to set this } @@ -76,25 +76,33 @@ func (k *keyStore) sendToAddress(addr address.Address, bs []byte) { k.mutex.Unlock() _, _ = k.core.WriteTo(bs, iwt.Addr(info.key[:])) } else { - var buf *buffer - if buf = k.addrBuffer[addr]; buf == nil { - buf = new(buffer) - k.addrBuffer[addr] = buf - } - msg := append([]byte(nil), bs...) - buf.packet = msg - if buf.timeout != nil { - buf.timeout.Stop() - } - buf.timeout = time.AfterFunc(keyStoreTimeout, func() { - k.mutex.Lock() - defer k.mutex.Unlock() - if nbuf := k.addrBuffer[addr]; nbuf == buf { - delete(k.addrBuffer, addr) + /* + var buf *buffer + if buf = k.addrBuffer[addr]; buf == nil { + buf = new(buffer) + k.addrBuffer[addr] = buf } - }) + msg := append([]byte(nil), bs...) + buf.packet = msg + if buf.timeout != nil { + buf.timeout.Stop() + } + buf.timeout = time.AfterFunc(keyStoreTimeout, func() { + k.mutex.Lock() + defer k.mutex.Unlock() + if nbuf := k.addrBuffer[addr]; nbuf == buf { + delete(k.addrBuffer, addr) + } + }) + k.mutex.Unlock() + k.sendKeyLookup(addr.GetKey()) + */ k.mutex.Unlock() - k.sendKeyLookup(addr.GetKey()) + key := k.core.GetKeyFor(addr.GetKey()) + info := k.update(key) + if info.address == addr { + _, _ = k.core.WriteTo(bs, iwt.Addr(info.key[:])) + } } } @@ -105,25 +113,33 @@ func (k *keyStore) sendToSubnet(subnet address.Subnet, bs []byte) { k.mutex.Unlock() _, _ = k.core.WriteTo(bs, iwt.Addr(info.key[:])) } else { - var buf *buffer - if buf = k.subnetBuffer[subnet]; buf == nil { - buf = new(buffer) - k.subnetBuffer[subnet] = buf - } - msg := append([]byte(nil), bs...) - buf.packet = msg - if buf.timeout != nil { - buf.timeout.Stop() - } - buf.timeout = time.AfterFunc(keyStoreTimeout, func() { - k.mutex.Lock() - defer k.mutex.Unlock() - if nbuf := k.subnetBuffer[subnet]; nbuf == buf { - delete(k.subnetBuffer, subnet) + /* + var buf *buffer + if buf = k.subnetBuffer[subnet]; buf == nil { + buf = new(buffer) + k.subnetBuffer[subnet] = buf } - }) + msg := append([]byte(nil), bs...) + buf.packet = msg + if buf.timeout != nil { + buf.timeout.Stop() + } + buf.timeout = time.AfterFunc(keyStoreTimeout, func() { + k.mutex.Lock() + defer k.mutex.Unlock() + if nbuf := k.subnetBuffer[subnet]; nbuf == buf { + delete(k.subnetBuffer, subnet) + } + }) + k.mutex.Unlock() + k.sendKeyLookup(subnet.GetKey()) + */ k.mutex.Unlock() - k.sendKeyLookup(subnet.GetKey()) + key := k.core.GetKeyFor(subnet.GetKey()) + info := k.update(key) + if info.subnet == subnet { + _, _ = k.core.WriteTo(bs, iwt.Addr(info.key[:])) + } } } @@ -141,6 +157,7 @@ func (k *keyStore) update(key ed25519.PublicKey) *keyInfo { k.keyToInfo[info.key] = info k.addrToInfo[info.address] = info k.subnetToInfo[info.subnet] = info + /* if buf := k.addrBuffer[info.address]; buf != nil { packets = append(packets, buf.packet) delete(k.addrBuffer, info.address) @@ -149,6 +166,7 @@ func (k *keyStore) update(key ed25519.PublicKey) *keyInfo { packets = append(packets, buf.packet) delete(k.subnetBuffer, info.subnet) } + */ } k.resetTimeout(info) k.mutex.Unlock() @@ -177,6 +195,7 @@ func (k *keyStore) resetTimeout(info *keyInfo) { }) } +/* func (k *keyStore) oobHandler(fromKey, toKey ed25519.PublicKey, data []byte) { // nolint:unused if len(data) != 1+ed25519.SignatureSize { return @@ -198,6 +217,7 @@ func (k *keyStore) oobHandler(fromKey, toKey ed25519.PublicKey, data []byte) { / } } } +*/ func (k *keyStore) sendKeyLookup(partial ed25519.PublicKey) { sig := ed25519.Sign(k.core.PrivateKey(), partial[:]) From fc632c5caaac7159bf114598147c242a76c49ee9 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sun, 26 Mar 2023 16:17:31 -0500 Subject: [PATCH 096/293] comment out some unused ipv6rwc code --- src/ipv6rwc/ipv6rwc.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ipv6rwc/ipv6rwc.go b/src/ipv6rwc/ipv6rwc.go index 34924b58..fe92910c 100644 --- a/src/ipv6rwc/ipv6rwc.go +++ b/src/ipv6rwc/ipv6rwc.go @@ -19,12 +19,14 @@ import ( const keyStoreTimeout = 2 * time.Minute +/* // Out-of-band packet types const ( typeKeyDummy = iota // nolint:deadcode,varcheck typeKeyLookup typeKeyResponse ) +*/ type keyArray [ed25519.PublicKeySize]byte @@ -48,10 +50,12 @@ type keyInfo struct { timeout *time.Timer // From calling a time.AfterFunc to do cleanup } +/* type buffer struct { packet []byte timeout *time.Timer } +*/ func (k *keyStore) init(c *core.Core) { k.core = c @@ -219,6 +223,7 @@ func (k *keyStore) oobHandler(fromKey, toKey ed25519.PublicKey, data []byte) { / } */ +/* func (k *keyStore) sendKeyLookup(partial ed25519.PublicKey) { sig := ed25519.Sign(k.core.PrivateKey(), partial[:]) bs := append([]byte{typeKeyLookup}, sig...) @@ -232,6 +237,7 @@ func (k *keyStore) sendKeyResponse(dest ed25519.PublicKey) { // nolint:unused //_ = k.core.SendOutOfBand(dest, bs) _ = bs } +*/ func (k *keyStore) readPC(p []byte) (int, error) { buf := make([]byte, k.core.MTU(), 65535) From abbe94fa8085aefd4d9ce8962cd9fcba5d84b445 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sun, 26 Mar 2023 16:34:49 -0500 Subject: [PATCH 097/293] fix core tests and run gofmt on src --- src/core/core_test.go | 7 ++++++- src/ipv6rwc/ipv6rwc.go | 30 +++++++++++++++--------------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/core/core_test.go b/src/core/core_test.go index 8d57f336..669ed172 100644 --- a/src/core/core_test.go +++ b/src/core/core_test.go @@ -70,7 +70,12 @@ func WaitConnected(nodeA, nodeB *Core) bool { // It may take up to 3 seconds, but let's wait 5. for i := 0; i < 50; i++ { time.Sleep(100 * time.Millisecond) - if len(nodeA.GetPeers()) > 0 && len(nodeB.GetPeers()) > 0 { + /* + if len(nodeA.GetPeers()) > 0 && len(nodeB.GetPeers()) > 0 { + return true + } + */ + if len(nodeA.GetPaths()) > 1 && len(nodeB.GetPaths()) > 1 { return true } } diff --git a/src/ipv6rwc/ipv6rwc.go b/src/ipv6rwc/ipv6rwc.go index fe92910c..22213c5f 100644 --- a/src/ipv6rwc/ipv6rwc.go +++ b/src/ipv6rwc/ipv6rwc.go @@ -31,16 +31,16 @@ const ( type keyArray [ed25519.PublicKeySize]byte type keyStore struct { - core *core.Core - address address.Address - subnet address.Subnet - mutex sync.Mutex - keyToInfo map[keyArray]*keyInfo - addrToInfo map[address.Address]*keyInfo + core *core.Core + address address.Address + subnet address.Subnet + mutex sync.Mutex + keyToInfo map[keyArray]*keyInfo + addrToInfo map[address.Address]*keyInfo //addrBuffer map[address.Address]*buffer subnetToInfo map[address.Subnet]*keyInfo //subnetBuffer map[address.Subnet]*buffer - mtu uint64 + mtu uint64 } type keyInfo struct { @@ -162,14 +162,14 @@ func (k *keyStore) update(key ed25519.PublicKey) *keyInfo { k.addrToInfo[info.address] = info k.subnetToInfo[info.subnet] = info /* - if buf := k.addrBuffer[info.address]; buf != nil { - packets = append(packets, buf.packet) - delete(k.addrBuffer, info.address) - } - if buf := k.subnetBuffer[info.subnet]; buf != nil { - packets = append(packets, buf.packet) - delete(k.subnetBuffer, info.subnet) - } + if buf := k.addrBuffer[info.address]; buf != nil { + packets = append(packets, buf.packet) + delete(k.addrBuffer, info.address) + } + if buf := k.subnetBuffer[info.subnet]; buf != nil { + packets = append(packets, buf.packet) + delete(k.subnetBuffer, info.subnet) + } */ } k.resetTimeout(info) From e99c870d51878878e1de8ed1fac65bd3fafb38d4 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sun, 26 Mar 2023 16:49:40 -0500 Subject: [PATCH 098/293] update admin functions and fix core tests --- cmd/genkeys/main.go | 2 -- cmd/yggdrasilctl/main.go | 39 ++++++++++++++++++++++----------------- go.mod | 2 +- go.sum | 4 ++-- src/admin/admin.go | 30 ++++++++++++++++-------------- src/admin/getdht.go | 12 ++++++++---- src/admin/getpaths.go | 4 ++++ src/core/api.go | 16 ++++++++++++---- src/core/core_test.go | 2 +- 9 files changed, 66 insertions(+), 45 deletions(-) diff --git a/cmd/genkeys/main.go b/cmd/genkeys/main.go index 81942446..a12d303e 100644 --- a/cmd/genkeys/main.go +++ b/cmd/genkeys/main.go @@ -1,5 +1,4 @@ /* - This file generates crypto keys. It prints out a new set of keys each time if finds a "better" one. By default, "better" means a higher NodeID (-> higher IP address). @@ -8,7 +7,6 @@ This is because the IP address format can compress leading 1s in the address, to If run with the "-sig" flag, it generates signing keys instead. A "better" signing key means one with a higher TreeID. This only matters if it's high enough to make you the root of the tree. - */ package main diff --git a/cmd/yggdrasilctl/main.go b/cmd/yggdrasilctl/main.go index 6aed8ac5..656df8c8 100644 --- a/cmd/yggdrasilctl/main.go +++ b/cmd/yggdrasilctl/main.go @@ -194,31 +194,36 @@ func run() int { if err := json.Unmarshal(recv.Response, &resp); err != nil { panic(err) } - table.SetHeader([]string{"Public Key", "IP Address", "Port", "Rest"}) + //table.SetHeader([]string{"Public Key", "IP Address", "Port", "Rest"}) + table.SetHeader([]string{"Public Key", "IP Address", "Parent", "Sequence"}) for _, dht := range resp.DHT { table.Append([]string{ dht.PublicKey, dht.IPAddress, - fmt.Sprintf("%d", dht.Port), - fmt.Sprintf("%d", dht.Rest), + dht.Parent, + fmt.Sprintf("%d", dht.Sequence), + //fmt.Sprintf("%d", dht.Port), + //fmt.Sprintf("%d", dht.Rest), }) } table.Render() - case "getpaths": - var resp admin.GetPathsResponse - if err := json.Unmarshal(recv.Response, &resp); err != nil { - panic(err) - } - table.SetHeader([]string{"Public Key", "IP Address", "Seq"}) - for _, p := range resp.Paths { - table.Append([]string{ - p.PublicKey, - p.IPAddress, - fmt.Sprintf("%d", p.Sequence), - }) - } - table.Render() + /* + case "getpaths": + var resp admin.GetPathsResponse + if err := json.Unmarshal(recv.Response, &resp); err != nil { + panic(err) + } + table.SetHeader([]string{"Public Key", "IP Address", "Seq"}) + for _, p := range resp.Paths { + table.Append([]string{ + p.PublicKey, + p.IPAddress, + fmt.Sprintf("%d", p.Sequence), + }) + } + table.Render() + */ case "getsessions": var resp admin.GetSessionsResponse diff --git a/go.mod b/go.mod index f94b5217..19b7bf98 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module github.com/yggdrasil-network/yggdrasil-go go 1.17 -replace github.com/Arceliar/ironwood => github.com/Arceliar/ironwood v0.0.0-20230326182230-e1880a231350 +replace github.com/Arceliar/ironwood => github.com/Arceliar/ironwood v0.0.0-20230326213941-b977dd93b701 require ( github.com/Arceliar/ironwood v0.0.0-20230318003210-65aa386cab13 diff --git a/go.sum b/go.sum index cbe8346b..e27fb209 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Arceliar/ironwood v0.0.0-20230326182230-e1880a231350 h1:9dsw9bwJKfwC/bohTvFsob7h4YeZkBI14eDtbY4WtTg= -github.com/Arceliar/ironwood v0.0.0-20230326182230-e1880a231350/go.mod h1:PhT70gxs32jSoxpi5gLlvCguWTzbpaqnNRTY6GgFPBY= +github.com/Arceliar/ironwood v0.0.0-20230326213941-b977dd93b701 h1:Cce66vRcL0hjO/wVqBU22d2r/J5+61N/aMzfPizMS5E= +github.com/Arceliar/ironwood v0.0.0-20230326213941-b977dd93b701/go.mod h1:PhT70gxs32jSoxpi5gLlvCguWTzbpaqnNRTY6GgFPBY= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= diff --git a/src/admin/admin.go b/src/admin/admin.go index 9dbcfdca..be4482fa 100644 --- a/src/admin/admin.go +++ b/src/admin/admin.go @@ -145,20 +145,22 @@ func (a *AdminSocket) SetupAdminHandlers() { return res, nil }, ) - _ = a.AddHandler( - "getPaths", "Show established paths through this node", []string{}, - func(in json.RawMessage) (interface{}, error) { - req := &GetPathsRequest{} - res := &GetPathsResponse{} - if err := json.Unmarshal(in, &req); err != nil { - return nil, err - } - if err := a.getPathsHandler(req, res); err != nil { - return nil, err - } - return res, nil - }, - ) + /* + _ = a.AddHandler( + "getPaths", "Show established paths through this node", []string{}, + func(in json.RawMessage) (interface{}, error) { + req := &GetPathsRequest{} + res := &GetPathsResponse{} + if err := json.Unmarshal(in, &req); err != nil { + return nil, err + } + if err := a.getPathsHandler(req, res); err != nil { + return nil, err + } + return res, nil + }, + ) + */ _ = a.AddHandler( "getSessions", "Show established traffic sessions with remote nodes", []string{}, func(in json.RawMessage) (interface{}, error) { diff --git a/src/admin/getdht.go b/src/admin/getdht.go index bfb21818..acddad7c 100644 --- a/src/admin/getdht.go +++ b/src/admin/getdht.go @@ -18,8 +18,10 @@ type GetDHTResponse struct { type DHTEntry struct { IPAddress string `json:"address"` PublicKey string `json:"key"` - Port uint64 `json:"port"` - Rest uint64 `json:"rest"` + Parent string `json:"parent"` + Sequence uint64 `json:"sequence"` + //Port uint64 `json:"port"` + //Rest uint64 `json:"rest"` } func (a *AdminSocket) getDHTHandler(req *GetDHTRequest, res *GetDHTResponse) error { @@ -30,8 +32,10 @@ func (a *AdminSocket) getDHTHandler(req *GetDHTRequest, res *GetDHTResponse) err res.DHT = append(res.DHT, DHTEntry{ IPAddress: net.IP(addr[:]).String(), PublicKey: hex.EncodeToString(d.Key[:]), - Port: d.Port, - Rest: d.Rest, + Parent: hex.EncodeToString(d.Parent[:]), + Sequence: d.Sequence, + //Port: d.Port, + //Rest: d.Rest, }) } sort.SliceStable(res.DHT, func(i, j int) bool { diff --git a/src/admin/getpaths.go b/src/admin/getpaths.go index 2d5db0fb..94a9945e 100644 --- a/src/admin/getpaths.go +++ b/src/admin/getpaths.go @@ -1,5 +1,7 @@ package admin +/* + import ( "encoding/hex" "net" @@ -38,3 +40,5 @@ func (a *AdminSocket) getPathsHandler(req *GetPathsRequest, res *GetPathsRespons }) return nil } + +*/ diff --git a/src/core/api.go b/src/core/api.go index c617a209..da4ead56 100644 --- a/src/core/api.go +++ b/src/core/api.go @@ -31,15 +31,19 @@ type PeerInfo struct { } type DHTEntryInfo struct { - Key ed25519.PublicKey - Port uint64 - Rest uint64 + Key ed25519.PublicKey + Parent ed25519.PublicKey + Sequence uint64 + //Port uint64 + //Rest uint64 } +/* type PathEntryInfo struct { Key ed25519.PublicKey Sequence uint64 } +*/ type SessionInfo struct { Key ed25519.PublicKey @@ -96,13 +100,16 @@ func (c *Core) GetDHT() []DHTEntryInfo { for _, d := range ds { var info DHTEntryInfo info.Key = d.Key - info.Port = d.Port + info.Parent = d.Parent + info.Sequence = d.Sequence + //info.Port = d.Port //info.Rest = d.Rest dhts = append(dhts, info) } return dhts } +/* func (c *Core) GetPaths() []PathEntryInfo { var paths []PathEntryInfo ps := c.PacketConn.PacketConn.Debug.GetPaths() @@ -115,6 +122,7 @@ func (c *Core) GetPaths() []PathEntryInfo { } return paths } +*/ func (c *Core) GetSessions() []SessionInfo { var sessions []SessionInfo diff --git a/src/core/core_test.go b/src/core/core_test.go index 669ed172..f917030b 100644 --- a/src/core/core_test.go +++ b/src/core/core_test.go @@ -75,7 +75,7 @@ func WaitConnected(nodeA, nodeB *Core) bool { return true } */ - if len(nodeA.GetPaths()) > 1 && len(nodeB.GetPaths()) > 1 { + if len(nodeA.GetDHT()) > 1 && len(nodeB.GetDHT()) > 1 { return true } } From ebd3596c2ca83693d0f9090d5fd3aaf4a69bd378 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sun, 26 Mar 2023 17:05:55 -0500 Subject: [PATCH 099/293] Update ci.yml --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 77475403..2f19f8c9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,7 +51,7 @@ jobs: strategy: fail-fast: false matrix: - goversion: ["1.17", "1.18", "1.19", "1.20"] + goversion: ["1.19", "1.20"] name: Build & Test (Linux, Go ${{ matrix.goversion }}) needs: [lint] @@ -75,7 +75,7 @@ jobs: strategy: fail-fast: false matrix: - goversion: ["1.17", "1.18", "1.19", "1.20"] + goversion: ["1.19", "1.20"] name: Build & Test (Windows, Go ${{ matrix.goversion }}) needs: [lint] @@ -99,7 +99,7 @@ jobs: strategy: fail-fast: false matrix: - goversion: ["1.17", "1.18", "1.19", "1.20"] + goversion: ["1.19", "1.20"] name: Build & Test (macOS, Go ${{ matrix.goversion }}) needs: [lint] From 8696650958692ab8a3b02e24f6554515baff7a0c Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sun, 26 Mar 2023 17:06:18 -0500 Subject: [PATCH 100/293] Update go.mod --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 19b7bf98..c6ba7375 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/yggdrasil-network/yggdrasil-go -go 1.17 +go 1.19 replace github.com/Arceliar/ironwood => github.com/Arceliar/ironwood v0.0.0-20230326213941-b977dd93b701 From 1345960d5f71bd726bc3d7c1bdb044f7034907a5 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 7 May 2023 17:29:46 +0100 Subject: [PATCH 101/293] Update to Arceliar/ironwood@14d951a --- go.mod | 2 +- go.sum | 51 --------------------------------------------------- 2 files changed, 1 insertion(+), 52 deletions(-) diff --git a/go.mod b/go.mod index c6ba7375..970e0f9c 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.19 replace github.com/Arceliar/ironwood => github.com/Arceliar/ironwood v0.0.0-20230326213941-b977dd93b701 require ( - github.com/Arceliar/ironwood v0.0.0-20230318003210-65aa386cab13 + github.com/Arceliar/ironwood v0.0.0-20230506230631-14d951aa1d45 github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d github.com/cheggaaa/pb/v3 v3.0.8 github.com/gologme/log v1.2.0 diff --git a/go.sum b/go.sum index e27fb209..7fe478a1 100644 --- a/go.sum +++ b/go.sum @@ -2,7 +2,6 @@ github.com/Arceliar/ironwood v0.0.0-20230326213941-b977dd93b701 h1:Cce66vRcL0hjO github.com/Arceliar/ironwood v0.0.0-20230326213941-b977dd93b701/go.mod h1:PhT70gxs32jSoxpi5gLlvCguWTzbpaqnNRTY6GgFPBY= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= @@ -19,8 +18,6 @@ github.com/hjson/hjson-go v3.1.0+incompatible h1:DY/9yE8ey8Zv22bY+mHV1uk2yRy0h8t github.com/hjson/hjson-go v3.1.0+incompatible/go.mod h1:qsetwF8NlsTsOTwZTApNlTCerV+b2GjYRRcIk4JMFio= github.com/kardianos/minwinsvc v1.0.2 h1:JmZKFJQrmTGa/WiW+vkJXKmfzdjabuEW4Tirj5lLdR0= github.com/kardianos/minwinsvc v1.0.2/go.mod h1:LUZNYhNmxujx2tR7FbdxqYJ9XDDoCd3MQcl1o//FWl4= -github.com/lxn/walk v0.0.0-20210112085537-c389da54e794/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ= -github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk= github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= @@ -42,79 +39,31 @@ github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYp github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f h1:p4VB7kIXpOQvVn1ZaTIVp+3vuYAXFe3OJEvjbUYJLaA= github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= -github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20221012134737-56aed061732a h1:NmSIgad6KjE6VvHciPZuNRTKxGhlPfD6OA87W/PLkqg= golang.org/x/crypto v0.0.0-20221012134737-56aed061732a/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20221110043201-43a038452099 h1:aIu0lKmfdgtn2uTj7JI2oN4TUrQvgB+wzTPO23bCKt8= golang.org/x/mobile v0.0.0-20221110043201-43a038452099/go.mod h1:aAjjkJNdrh3PMckS4B10TGS2nag27cbKR1y2BpUxsiY= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210927181540-4e4d966f7476/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211011170408-caeb26a5c8c0/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0= golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.zx2c4.com/wireguard v0.0.0-20211012062646-82d2aa87aa62/go.mod h1:id8Oh3eCCmpj9uVGWVjsUAl6UPX5ysMLzu6QxJU2UOU= golang.zx2c4.com/wireguard v0.0.0-20211017052713-f87e87af0d9a h1:tTbyylK9/D3u/wEP26Vx7L700UpY48nhioJWZM1vhZw= golang.zx2c4.com/wireguard v0.0.0-20211017052713-f87e87af0d9a/go.mod h1:id8Oh3eCCmpj9uVGWVjsUAl6UPX5ysMLzu6QxJU2UOU= golang.zx2c4.com/wireguard/windows v0.4.12 h1:CUmbdWKVNzTSsVb4yUAiEwL3KsabdJkEPdDjCHxBlhA= From 5e95246c26b497672851ab0eccd43ed2cbed7df6 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sat, 13 May 2023 14:44:38 -0500 Subject: [PATCH 102/293] update to ironwood v0.0.0-20230513191034-495699d87ae4 with API changes --- cmd/yggdrasilctl/main.go | 45 +++++---- contrib/ansible/genkeys.go | 2 - go.mod | 18 ++-- go.sum | 53 +++++++--- src/admin/admin.go | 8 +- src/admin/getpaths.go | 12 +-- src/admin/{getdht.go => gettree.go} | 22 ++--- src/core/api.go | 33 +++---- src/core/core.go | 34 ++++++- src/core/proto.go | 60 ++++++------ src/ipv6rwc/ipv6rwc.go | 146 +++++++++++++--------------- 11 files changed, 235 insertions(+), 198 deletions(-) rename src/admin/{getdht.go => gettree.go} (56%) diff --git a/cmd/yggdrasilctl/main.go b/cmd/yggdrasilctl/main.go index 656df8c8..3c191c87 100644 --- a/cmd/yggdrasilctl/main.go +++ b/cmd/yggdrasilctl/main.go @@ -189,41 +189,40 @@ func run() int { } table.Render() - case "getdht": - var resp admin.GetDHTResponse + case "gettree": + var resp admin.GetTreeResponse if err := json.Unmarshal(recv.Response, &resp); err != nil { panic(err) } //table.SetHeader([]string{"Public Key", "IP Address", "Port", "Rest"}) table.SetHeader([]string{"Public Key", "IP Address", "Parent", "Sequence"}) - for _, dht := range resp.DHT { + for _, tree := range resp.Tree { table.Append([]string{ - dht.PublicKey, - dht.IPAddress, - dht.Parent, - fmt.Sprintf("%d", dht.Sequence), + tree.PublicKey, + tree.IPAddress, + tree.Parent, + fmt.Sprintf("%d", tree.Sequence), //fmt.Sprintf("%d", dht.Port), //fmt.Sprintf("%d", dht.Rest), }) } table.Render() - /* - case "getpaths": - var resp admin.GetPathsResponse - if err := json.Unmarshal(recv.Response, &resp); err != nil { - panic(err) - } - table.SetHeader([]string{"Public Key", "IP Address", "Seq"}) - for _, p := range resp.Paths { - table.Append([]string{ - p.PublicKey, - p.IPAddress, - fmt.Sprintf("%d", p.Sequence), - }) - } - table.Render() - */ + case "getpaths": + var resp admin.GetPathsResponse + if err := json.Unmarshal(recv.Response, &resp); err != nil { + panic(err) + } + table.SetHeader([]string{"Public Key", "IP Address", "Path", "Seq"}) + for _, p := range resp.Paths { + table.Append([]string{ + p.PublicKey, + p.IPAddress, + fmt.Sprintf("%v", p.Path), + fmt.Sprintf("%d", p.Sequence), + }) + } + table.Render() case "getsessions": var resp admin.GetSessionsResponse diff --git a/contrib/ansible/genkeys.go b/contrib/ansible/genkeys.go index 4a02b9bd..c1aed7ee 100644 --- a/contrib/ansible/genkeys.go +++ b/contrib/ansible/genkeys.go @@ -1,7 +1,5 @@ /* - This file generates crypto keys for [ansible-yggdrasil](https://github.com/jcgruenhage/ansible-yggdrasil/) - */ package main diff --git a/go.mod b/go.mod index 970e0f9c..3c1e2da8 100644 --- a/go.mod +++ b/go.mod @@ -2,10 +2,8 @@ module github.com/yggdrasil-network/yggdrasil-go go 1.19 -replace github.com/Arceliar/ironwood => github.com/Arceliar/ironwood v0.0.0-20230326213941-b977dd93b701 - require ( - github.com/Arceliar/ironwood v0.0.0-20230506230631-14d951aa1d45 + github.com/Arceliar/ironwood v0.0.0-20230513191034-495699d87ae4 github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d github.com/cheggaaa/pb/v3 v3.0.8 github.com/gologme/log v1.2.0 @@ -15,19 +13,21 @@ require ( github.com/mitchellh/mapstructure v1.4.1 github.com/vishvananda/netlink v1.1.0 golang.org/x/mobile v0.0.0-20221110043201-43a038452099 - golang.org/x/net v0.7.0 - golang.org/x/sys v0.5.0 - golang.org/x/text v0.7.0 + golang.org/x/net v0.9.0 + golang.org/x/sys v0.7.0 + golang.org/x/text v0.9.0 golang.zx2c4.com/wireguard v0.0.0-20211017052713-f87e87af0d9a golang.zx2c4.com/wireguard/windows v0.4.12 ) require ( + github.com/bits-and-blooms/bitset v1.5.0 // indirect + github.com/bits-and-blooms/bloom/v3 v3.3.1 // indirect github.com/mattn/go-colorable v0.1.8 // indirect github.com/rivo/uniseg v0.2.0 // indirect - golang.org/x/crypto v0.0.0-20221012134737-56aed061732a // indirect - golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect - golang.org/x/tools v0.1.12 // indirect + golang.org/x/crypto v0.8.0 // indirect + golang.org/x/mod v0.8.0 // indirect + golang.org/x/tools v0.6.0 // indirect ) require ( diff --git a/go.sum b/go.sum index 7fe478a1..ca7fde5d 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,15 @@ -github.com/Arceliar/ironwood v0.0.0-20230326213941-b977dd93b701 h1:Cce66vRcL0hjO/wVqBU22d2r/J5+61N/aMzfPizMS5E= -github.com/Arceliar/ironwood v0.0.0-20230326213941-b977dd93b701/go.mod h1:PhT70gxs32jSoxpi5gLlvCguWTzbpaqnNRTY6GgFPBY= +github.com/Arceliar/ironwood v0.0.0-20230513191034-495699d87ae4 h1:I2IlZA7DQAZCR3xasKDJ2YgHbXh1fbyOu0Jqn1i4Zi8= +github.com/Arceliar/ironwood v0.0.0-20230513191034-495699d87ae4/go.mod h1:MIfrhR4b+U6gurd5pln622Zwaf2kzpIvXcnvRZMvlRI= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q= github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= +github.com/bits-and-blooms/bitset v1.3.1/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= +github.com/bits-and-blooms/bitset v1.5.0 h1:NpE8frKRLGHIcEzkR+gZhiioW1+WbYV6fKwD6ZIpQT8= +github.com/bits-and-blooms/bitset v1.5.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= +github.com/bits-and-blooms/bloom/v3 v3.3.1 h1:K2+A19bXT8gJR5mU7y+1yW6hsKfNCjcP2uNfLFKncjQ= +github.com/bits-and-blooms/bloom/v3 v3.3.1/go.mod h1:bhUUknWd5khVbTe4UgMCSiOOVJzr3tMoijSK3WwvW90= github.com/cheggaaa/pb/v3 v3.0.8 h1:bC8oemdChbke2FHIIGy9mn4DPJ2caZYQnfbRqwmdCoA= github.com/cheggaaa/pb/v3 v3.0.8/go.mod h1:UICbiLec/XO6Hw6k+BHEtHeQFzzBH4i2/qk/ow1EJTA= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= @@ -34,36 +39,62 @@ github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6 github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= +github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f h1:p4VB7kIXpOQvVn1ZaTIVp+3vuYAXFe3OJEvjbUYJLaA= github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20221012134737-56aed061732a h1:NmSIgad6KjE6VvHciPZuNRTKxGhlPfD6OA87W/PLkqg= -golang.org/x/crypto v0.0.0-20221012134737-56aed061732a/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= +golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= golang.org/x/mobile v0.0.0-20221110043201-43a038452099 h1:aIu0lKmfdgtn2uTj7JI2oN4TUrQvgB+wzTPO23bCKt8= golang.org/x/mobile v0.0.0-20221110043201-43a038452099/go.mod h1:aAjjkJNdrh3PMckS4B10TGS2nag27cbKR1y2BpUxsiY= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.zx2c4.com/wireguard v0.0.0-20211017052713-f87e87af0d9a h1:tTbyylK9/D3u/wEP26Vx7L700UpY48nhioJWZM1vhZw= golang.zx2c4.com/wireguard v0.0.0-20211017052713-f87e87af0d9a/go.mod h1:id8Oh3eCCmpj9uVGWVjsUAl6UPX5ysMLzu6QxJU2UOU= golang.zx2c4.com/wireguard/windows v0.4.12 h1:CUmbdWKVNzTSsVb4yUAiEwL3KsabdJkEPdDjCHxBlhA= diff --git a/src/admin/admin.go b/src/admin/admin.go index be4482fa..75089ba5 100644 --- a/src/admin/admin.go +++ b/src/admin/admin.go @@ -132,14 +132,14 @@ func (a *AdminSocket) SetupAdminHandlers() { }, ) _ = a.AddHandler( - "getDHT", "Show known DHT entries", []string{}, + "getTree", "Show known Tree entries", []string{}, func(in json.RawMessage) (interface{}, error) { - req := &GetDHTRequest{} - res := &GetDHTResponse{} + req := &GetTreeRequest{} + res := &GetTreeResponse{} if err := json.Unmarshal(in, &req); err != nil { return nil, err } - if err := a.getDHTHandler(req, res); err != nil { + if err := a.getTreeHandler(req, res); err != nil { return nil, err } return res, nil diff --git a/src/admin/getpaths.go b/src/admin/getpaths.go index 94a9945e..66e11bd2 100644 --- a/src/admin/getpaths.go +++ b/src/admin/getpaths.go @@ -1,7 +1,5 @@ package admin -/* - import ( "encoding/hex" "net" @@ -19,9 +17,10 @@ type GetPathsResponse struct { } type PathEntry struct { - IPAddress string `json:"address"` - PublicKey string `json:"key"` - Sequence uint64 `json:"sequence"` + IPAddress string `json:"address"` + PublicKey string `json:"key"` + Path []uint64 `json:"path"` + Sequence uint64 `json:"sequence"` } func (a *AdminSocket) getPathsHandler(req *GetPathsRequest, res *GetPathsResponse) error { @@ -32,6 +31,7 @@ func (a *AdminSocket) getPathsHandler(req *GetPathsRequest, res *GetPathsRespons res.Paths = append(res.Paths, PathEntry{ IPAddress: net.IP(addr[:]).String(), PublicKey: hex.EncodeToString(p.Key), + Path: p.Path, Sequence: p.Sequence, }) } @@ -40,5 +40,3 @@ func (a *AdminSocket) getPathsHandler(req *GetPathsRequest, res *GetPathsRespons }) return nil } - -*/ diff --git a/src/admin/getdht.go b/src/admin/gettree.go similarity index 56% rename from src/admin/getdht.go rename to src/admin/gettree.go index acddad7c..06cf8e73 100644 --- a/src/admin/getdht.go +++ b/src/admin/gettree.go @@ -9,13 +9,13 @@ import ( "github.com/yggdrasil-network/yggdrasil-go/src/address" ) -type GetDHTRequest struct{} +type GetTreeRequest struct{} -type GetDHTResponse struct { - DHT []DHTEntry `json:"dht"` +type GetTreeResponse struct { + Tree []TreeEntry `json:"tree"` } -type DHTEntry struct { +type TreeEntry struct { IPAddress string `json:"address"` PublicKey string `json:"key"` Parent string `json:"parent"` @@ -24,12 +24,12 @@ type DHTEntry struct { //Rest uint64 `json:"rest"` } -func (a *AdminSocket) getDHTHandler(req *GetDHTRequest, res *GetDHTResponse) error { - dht := a.core.GetDHT() - res.DHT = make([]DHTEntry, 0, len(dht)) - for _, d := range dht { +func (a *AdminSocket) getTreeHandler(req *GetTreeRequest, res *GetTreeResponse) error { + tree := a.core.GetTree() + res.Tree = make([]TreeEntry, 0, len(tree)) + for _, d := range tree { addr := address.AddrForKey(d.Key) - res.DHT = append(res.DHT, DHTEntry{ + res.Tree = append(res.Tree, TreeEntry{ IPAddress: net.IP(addr[:]).String(), PublicKey: hex.EncodeToString(d.Key[:]), Parent: hex.EncodeToString(d.Parent[:]), @@ -38,8 +38,8 @@ func (a *AdminSocket) getDHTHandler(req *GetDHTRequest, res *GetDHTResponse) err //Rest: d.Rest, }) } - sort.SliceStable(res.DHT, func(i, j int) bool { - return strings.Compare(res.DHT[i].PublicKey, res.DHT[j].PublicKey) < 0 + sort.SliceStable(res.Tree, func(i, j int) bool { + return strings.Compare(res.Tree[i].PublicKey, res.Tree[j].PublicKey) < 0 }) return nil } diff --git a/src/core/api.go b/src/core/api.go index da4ead56..feece23c 100644 --- a/src/core/api.go +++ b/src/core/api.go @@ -30,7 +30,7 @@ type PeerInfo struct { Uptime time.Duration } -type DHTEntryInfo struct { +type TreeEntryInfo struct { Key ed25519.PublicKey Parent ed25519.PublicKey Sequence uint64 @@ -38,12 +38,11 @@ type DHTEntryInfo struct { //Rest uint64 } -/* type PathEntryInfo struct { Key ed25519.PublicKey + Path []uint64 Sequence uint64 } -*/ type SessionInfo struct { Key ed25519.PublicKey @@ -94,22 +93,21 @@ func (c *Core) GetPeers() []PeerInfo { return peers } -func (c *Core) GetDHT() []DHTEntryInfo { - var dhts []DHTEntryInfo - ds := c.PacketConn.PacketConn.Debug.GetDHT() - for _, d := range ds { - var info DHTEntryInfo - info.Key = d.Key - info.Parent = d.Parent - info.Sequence = d.Sequence +func (c *Core) GetTree() []TreeEntryInfo { + var trees []TreeEntryInfo + ts := c.PacketConn.PacketConn.Debug.GetTree() + for _, t := range ts { + var info TreeEntryInfo + info.Key = t.Key + info.Parent = t.Parent + info.Sequence = t.Sequence //info.Port = d.Port //info.Rest = d.Rest - dhts = append(dhts, info) + trees = append(trees, info) } - return dhts + return trees } -/* func (c *Core) GetPaths() []PathEntryInfo { var paths []PathEntryInfo ps := c.PacketConn.PacketConn.Debug.GetPaths() @@ -117,12 +115,11 @@ func (c *Core) GetPaths() []PathEntryInfo { var info PathEntryInfo info.Key = p.Key info.Sequence = p.Sequence - //info.Path = p.Path + info.Path = p.Path paths = append(paths, info) } return paths } -*/ func (c *Core) GetSessions() []SessionInfo { var sessions []SessionInfo @@ -282,8 +279,8 @@ func (c *Core) SetAdmin(a AddHandler) error { return err } if err := a.AddHandler( - "debug_remoteGetDHT", "Debug use only", []string{"key"}, - c.proto.getDHTHandler, + "debug_remoteGetTree", "Debug use only", []string{"key"}, + c.proto.getTreeHandler, ); err != nil { return err } diff --git a/src/core/core.go b/src/core/core.go index bbf5b63f..9243be0f 100644 --- a/src/core/core.go +++ b/src/core/core.go @@ -10,10 +10,12 @@ import ( "time" iwe "github.com/Arceliar/ironwood/encrypted" + iwn "github.com/Arceliar/ironwood/network" iwt "github.com/Arceliar/ironwood/types" "github.com/Arceliar/phony" "github.com/gologme/log" + "github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/version" ) @@ -40,6 +42,7 @@ type Core struct { nodeinfoPrivacy NodeInfoPrivacy // immutable after startup _allowedPublicKeys map[[32]byte]struct{} // configurable after startup } + pathNotify func(ed25519.PublicKey) } func New(secret ed25519.PrivateKey, logger Logger, opts ...SetupOption) (*Core, error) { @@ -61,7 +64,14 @@ func New(secret ed25519.PrivateKey, logger Logger, opts ...SetupOption) (*Core, copy(c.secret, secret) c.public = secret.Public().(ed25519.PublicKey) var err error - if c.PacketConn, err = iwe.NewPacketConn(c.secret); err != nil { + keyXform := func(key ed25519.PublicKey) ed25519.PublicKey { + return address.SubnetForKey(key).GetKey() + } + if c.PacketConn, err = iwe.NewPacketConn(c.secret, + iwn.WithBloomTransform(keyXform), + iwn.WithPeerMaxMessageSize(65535*2), + iwn.WithPathNotify(c.doPathNotify), + ); err != nil { return nil, fmt.Errorf("error creating encryption: %w", err) } c.config._peers = map[Peer]*linkInfo{} @@ -151,11 +161,15 @@ func (c *Core) _close() error { func (c *Core) MTU() uint64 { const sessionTypeOverhead = 1 - return c.PacketConn.MTU() - sessionTypeOverhead + MTU := c.PacketConn.MTU() - sessionTypeOverhead + if MTU > 65535 { + MTU = 65535 + } + return MTU } func (c *Core) ReadFrom(p []byte) (n int, from net.Addr, err error) { - buf := make([]byte, c.PacketConn.MTU(), 65535) + buf := make([]byte, c.PacketConn.MTU()) for { bs := buf n, from, err = c.PacketConn.ReadFrom(bs) @@ -199,6 +213,20 @@ func (c *Core) WriteTo(p []byte, addr net.Addr) (n int, err error) { return } +func (c *Core) doPathNotify(key ed25519.PublicKey) { + c.Act(nil, func() { + if c.pathNotify != nil { + c.pathNotify(key) + } + }) +} + +func (c *Core) SetPathNotify(notify func(ed25519.PublicKey)) { + c.Act(nil, func() { + c.pathNotify = notify + }) +} + type Logger interface { Printf(string, ...interface{}) Println(...interface{}) diff --git a/src/core/proto.go b/src/core/proto.go index 186e9b8d..d8cae737 100644 --- a/src/core/proto.go +++ b/src/core/proto.go @@ -21,8 +21,8 @@ const ( typeDebugGetSelfResponse typeDebugGetPeersRequest typeDebugGetPeersResponse - typeDebugGetDHTRequest - typeDebugGetDHTResponse + typeDebugGetTreeRequest + typeDebugGetTreeResponse ) type reqInfo struct { @@ -40,7 +40,7 @@ type protoHandler struct { selfRequests map[keyArray]*reqInfo peersRequests map[keyArray]*reqInfo - dhtRequests map[keyArray]*reqInfo + treeRequests map[keyArray]*reqInfo } func (p *protoHandler) init(core *Core) { @@ -49,7 +49,7 @@ func (p *protoHandler) init(core *Core) { p.selfRequests = make(map[keyArray]*reqInfo) p.peersRequests = make(map[keyArray]*reqInfo) - p.dhtRequests = make(map[keyArray]*reqInfo) + p.treeRequests = make(map[keyArray]*reqInfo) } // Common functions @@ -89,10 +89,10 @@ func (p *protoHandler) _handleDebug(key keyArray, bs []byte) { p._handleGetPeersRequest(key) case typeDebugGetPeersResponse: p._handleGetPeersResponse(key, bs[1:]) - case typeDebugGetDHTRequest: - p._handleGetDHTRequest(key) - case typeDebugGetDHTResponse: - p._handleGetDHTResponse(key, bs[1:]) + case typeDebugGetTreeRequest: + p._handleGetTreeRequest(key) + case typeDebugGetTreeResponse: + p._handleGetTreeResponse(key, bs[1:]) } } @@ -188,47 +188,47 @@ func (p *protoHandler) _handleGetPeersResponse(key keyArray, bs []byte) { } } -// Get DHT +// Get Tree -func (p *protoHandler) sendGetDHTRequest(key keyArray, callback func([]byte)) { +func (p *protoHandler) sendGetTreeRequest(key keyArray, callback func([]byte)) { p.Act(nil, func() { - if info := p.dhtRequests[key]; info != nil { + if info := p.treeRequests[key]; info != nil { info.timer.Stop() - delete(p.dhtRequests, key) + delete(p.treeRequests, key) } info := new(reqInfo) info.callback = callback info.timer = time.AfterFunc(time.Minute, func() { p.Act(nil, func() { - if p.dhtRequests[key] == info { - delete(p.dhtRequests, key) + if p.treeRequests[key] == info { + delete(p.treeRequests, key) } }) }) - p.dhtRequests[key] = info - p._sendDebug(key, typeDebugGetDHTRequest, nil) + p.treeRequests[key] = info + p._sendDebug(key, typeDebugGetTreeRequest, nil) }) } -func (p *protoHandler) _handleGetDHTRequest(key keyArray) { - dinfos := p.core.GetDHT() +func (p *protoHandler) _handleGetTreeRequest(key keyArray) { + dinfos := p.core.GetTree() var bs []byte for _, dinfo := range dinfos { tmp := append(bs, dinfo.Key[:]...) - const responseOverhead = 2 // 1 debug type, 1 getdht type + const responseOverhead = 2 // 1 debug type, 1 gettree type if uint64(len(tmp))+responseOverhead > p.core.MTU() { break } bs = tmp } - p._sendDebug(key, typeDebugGetDHTResponse, bs) + p._sendDebug(key, typeDebugGetTreeResponse, bs) } -func (p *protoHandler) _handleGetDHTResponse(key keyArray, bs []byte) { - if info := p.dhtRequests[key]; info != nil { +func (p *protoHandler) _handleGetTreeResponse(key keyArray, bs []byte) { + if info := p.treeRequests[key]; info != nil { info.timer.Stop() info.callback(bs) - delete(p.dhtRequests, key) + delete(p.treeRequests, key) } } @@ -322,16 +322,16 @@ func (p *protoHandler) getPeersHandler(in json.RawMessage) (interface{}, error) } } -// Admin socket stuff for "Get DHT" +// Admin socket stuff for "Get Tree" -type DebugGetDHTRequest struct { +type DebugGetTreeRequest struct { Key string `json:"key"` } -type DebugGetDHTResponse map[string]interface{} +type DebugGetTreeResponse map[string]interface{} -func (p *protoHandler) getDHTHandler(in json.RawMessage) (interface{}, error) { - var req DebugGetDHTRequest +func (p *protoHandler) getTreeHandler(in json.RawMessage) (interface{}, error) { + var req DebugGetTreeRequest if err := json.Unmarshal(in, &req); err != nil { return nil, err } @@ -343,7 +343,7 @@ func (p *protoHandler) getDHTHandler(in json.RawMessage) (interface{}, error) { } copy(key[:], kbs) ch := make(chan []byte, 1) - p.sendGetDHTRequest(key, func(info []byte) { + p.sendGetTreeRequest(key, func(info []byte) { ch <- info }) timer := time.NewTimer(6 * time.Second) @@ -367,7 +367,7 @@ func (p *protoHandler) getDHTHandler(in json.RawMessage) (interface{}, error) { return nil, err } ip := net.IP(address.AddrForKey(kbs)[:]) - res := DebugGetDHTResponse{ip.String(): msg} + res := DebugGetTreeResponse{ip.String(): msg} return res, nil } } diff --git a/src/ipv6rwc/ipv6rwc.go b/src/ipv6rwc/ipv6rwc.go index 22213c5f..59f4f022 100644 --- a/src/ipv6rwc/ipv6rwc.go +++ b/src/ipv6rwc/ipv6rwc.go @@ -31,16 +31,16 @@ const ( type keyArray [ed25519.PublicKeySize]byte type keyStore struct { - core *core.Core - address address.Address - subnet address.Subnet - mutex sync.Mutex - keyToInfo map[keyArray]*keyInfo - addrToInfo map[address.Address]*keyInfo - //addrBuffer map[address.Address]*buffer + core *core.Core + address address.Address + subnet address.Subnet + mutex sync.Mutex + keyToInfo map[keyArray]*keyInfo + addrToInfo map[address.Address]*keyInfo + addrBuffer map[address.Address]*buffer subnetToInfo map[address.Subnet]*keyInfo - //subnetBuffer map[address.Subnet]*buffer - mtu uint64 + subnetBuffer map[address.Subnet]*buffer + mtu uint64 } type keyInfo struct { @@ -50,12 +50,10 @@ type keyInfo struct { timeout *time.Timer // From calling a time.AfterFunc to do cleanup } -/* type buffer struct { packet []byte timeout *time.Timer } -*/ func (k *keyStore) init(c *core.Core) { k.core = c @@ -65,11 +63,14 @@ func (k *keyStore) init(c *core.Core) { err = fmt.Errorf("tun.core.SetOutOfBandHander: %w", err) panic(err) }*/ + k.core.SetPathNotify(func(key ed25519.PublicKey) { + k.update(key) + }) k.keyToInfo = make(map[keyArray]*keyInfo) k.addrToInfo = make(map[address.Address]*keyInfo) - //k.addrBuffer = make(map[address.Address]*buffer) + k.addrBuffer = make(map[address.Address]*buffer) k.subnetToInfo = make(map[address.Subnet]*keyInfo) - //k.subnetBuffer = make(map[address.Subnet]*buffer) + k.subnetBuffer = make(map[address.Subnet]*buffer) k.mtu = 1280 // Default to something safe, expect user to set this } @@ -80,33 +81,25 @@ func (k *keyStore) sendToAddress(addr address.Address, bs []byte) { k.mutex.Unlock() _, _ = k.core.WriteTo(bs, iwt.Addr(info.key[:])) } else { - /* - var buf *buffer - if buf = k.addrBuffer[addr]; buf == nil { - buf = new(buffer) - k.addrBuffer[addr] = buf - } - msg := append([]byte(nil), bs...) - buf.packet = msg - if buf.timeout != nil { - buf.timeout.Stop() - } - buf.timeout = time.AfterFunc(keyStoreTimeout, func() { - k.mutex.Lock() - defer k.mutex.Unlock() - if nbuf := k.addrBuffer[addr]; nbuf == buf { - delete(k.addrBuffer, addr) - } - }) - k.mutex.Unlock() - k.sendKeyLookup(addr.GetKey()) - */ - k.mutex.Unlock() - key := k.core.GetKeyFor(addr.GetKey()) - info := k.update(key) - if info.address == addr { - _, _ = k.core.WriteTo(bs, iwt.Addr(info.key[:])) + var buf *buffer + if buf = k.addrBuffer[addr]; buf == nil { + buf = new(buffer) + k.addrBuffer[addr] = buf } + msg := append([]byte(nil), bs...) + buf.packet = msg + if buf.timeout != nil { + buf.timeout.Stop() + } + buf.timeout = time.AfterFunc(keyStoreTimeout, func() { + k.mutex.Lock() + defer k.mutex.Unlock() + if nbuf := k.addrBuffer[addr]; nbuf == buf { + delete(k.addrBuffer, addr) + } + }) + k.mutex.Unlock() + k.sendKeyLookup(addr.GetKey()) } } @@ -117,33 +110,25 @@ func (k *keyStore) sendToSubnet(subnet address.Subnet, bs []byte) { k.mutex.Unlock() _, _ = k.core.WriteTo(bs, iwt.Addr(info.key[:])) } else { - /* - var buf *buffer - if buf = k.subnetBuffer[subnet]; buf == nil { - buf = new(buffer) - k.subnetBuffer[subnet] = buf - } - msg := append([]byte(nil), bs...) - buf.packet = msg - if buf.timeout != nil { - buf.timeout.Stop() - } - buf.timeout = time.AfterFunc(keyStoreTimeout, func() { - k.mutex.Lock() - defer k.mutex.Unlock() - if nbuf := k.subnetBuffer[subnet]; nbuf == buf { - delete(k.subnetBuffer, subnet) - } - }) - k.mutex.Unlock() - k.sendKeyLookup(subnet.GetKey()) - */ - k.mutex.Unlock() - key := k.core.GetKeyFor(subnet.GetKey()) - info := k.update(key) - if info.subnet == subnet { - _, _ = k.core.WriteTo(bs, iwt.Addr(info.key[:])) + var buf *buffer + if buf = k.subnetBuffer[subnet]; buf == nil { + buf = new(buffer) + k.subnetBuffer[subnet] = buf } + msg := append([]byte(nil), bs...) + buf.packet = msg + if buf.timeout != nil { + buf.timeout.Stop() + } + buf.timeout = time.AfterFunc(keyStoreTimeout, func() { + k.mutex.Lock() + defer k.mutex.Unlock() + if nbuf := k.subnetBuffer[subnet]; nbuf == buf { + delete(k.subnetBuffer, subnet) + } + }) + k.mutex.Unlock() + k.sendKeyLookup(subnet.GetKey()) } } @@ -161,16 +146,14 @@ func (k *keyStore) update(key ed25519.PublicKey) *keyInfo { k.keyToInfo[info.key] = info k.addrToInfo[info.address] = info k.subnetToInfo[info.subnet] = info - /* - if buf := k.addrBuffer[info.address]; buf != nil { - packets = append(packets, buf.packet) - delete(k.addrBuffer, info.address) - } - if buf := k.subnetBuffer[info.subnet]; buf != nil { - packets = append(packets, buf.packet) - delete(k.subnetBuffer, info.subnet) - } - */ + if buf := k.addrBuffer[info.address]; buf != nil { + packets = append(packets, buf.packet) + delete(k.addrBuffer, info.address) + } + if buf := k.subnetBuffer[info.subnet]; buf != nil { + packets = append(packets, buf.packet) + delete(k.subnetBuffer, info.subnet) + } } k.resetTimeout(info) k.mutex.Unlock() @@ -223,14 +206,17 @@ func (k *keyStore) oobHandler(fromKey, toKey ed25519.PublicKey, data []byte) { / } */ -/* func (k *keyStore) sendKeyLookup(partial ed25519.PublicKey) { - sig := ed25519.Sign(k.core.PrivateKey(), partial[:]) - bs := append([]byte{typeKeyLookup}, sig...) - //_ = k.core.SendOutOfBand(partial, bs) - _ = bs + /* + sig := ed25519.Sign(k.core.PrivateKey(), partial[:]) + bs := append([]byte{typeKeyLookup}, sig...) + //_ = k.core.SendOutOfBand(partial, bs) + _ = bs + */ + k.core.SendLookup(partial) } +/* func (k *keyStore) sendKeyResponse(dest ed25519.PublicKey) { // nolint:unused sig := ed25519.Sign(k.core.PrivateKey(), dest[:]) bs := append([]byte{typeKeyResponse}, sig...) From 669e61af9a378544cee16d4058f99460a5326081 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sat, 13 May 2023 16:15:04 -0500 Subject: [PATCH 103/293] update to bugfixed ironwood, fix broken core test, add getPaths handler to admin socket --- go.mod | 2 +- go.sum | 4 ++-- src/admin/admin.go | 30 ++++++++++++++---------------- src/core/core_test.go | 3 ++- 4 files changed, 19 insertions(+), 20 deletions(-) diff --git a/go.mod b/go.mod index 3c1e2da8..5a685912 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/yggdrasil-network/yggdrasil-go go 1.19 require ( - github.com/Arceliar/ironwood v0.0.0-20230513191034-495699d87ae4 + github.com/Arceliar/ironwood v0.0.0-20230513211242-fac5b9486c3c github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d github.com/cheggaaa/pb/v3 v3.0.8 github.com/gologme/log v1.2.0 diff --git a/go.sum b/go.sum index ca7fde5d..66dfae5c 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Arceliar/ironwood v0.0.0-20230513191034-495699d87ae4 h1:I2IlZA7DQAZCR3xasKDJ2YgHbXh1fbyOu0Jqn1i4Zi8= -github.com/Arceliar/ironwood v0.0.0-20230513191034-495699d87ae4/go.mod h1:MIfrhR4b+U6gurd5pln622Zwaf2kzpIvXcnvRZMvlRI= +github.com/Arceliar/ironwood v0.0.0-20230513211242-fac5b9486c3c h1:+MCqerP9VlZw/ECZaDItAgihDDcDGAszgKu/n14WjP0= +github.com/Arceliar/ironwood v0.0.0-20230513211242-fac5b9486c3c/go.mod h1:MIfrhR4b+U6gurd5pln622Zwaf2kzpIvXcnvRZMvlRI= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q= github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= diff --git a/src/admin/admin.go b/src/admin/admin.go index 75089ba5..d0d444be 100644 --- a/src/admin/admin.go +++ b/src/admin/admin.go @@ -145,22 +145,20 @@ func (a *AdminSocket) SetupAdminHandlers() { return res, nil }, ) - /* - _ = a.AddHandler( - "getPaths", "Show established paths through this node", []string{}, - func(in json.RawMessage) (interface{}, error) { - req := &GetPathsRequest{} - res := &GetPathsResponse{} - if err := json.Unmarshal(in, &req); err != nil { - return nil, err - } - if err := a.getPathsHandler(req, res); err != nil { - return nil, err - } - return res, nil - }, - ) - */ + _ = a.AddHandler( + "getPaths", "Show established paths through this node", []string{}, + func(in json.RawMessage) (interface{}, error) { + req := &GetPathsRequest{} + res := &GetPathsResponse{} + if err := json.Unmarshal(in, &req); err != nil { + return nil, err + } + if err := a.getPathsHandler(req, res); err != nil { + return nil, err + } + return res, nil + }, + ) _ = a.AddHandler( "getSessions", "Show established traffic sessions with remote nodes", []string{}, func(in json.RawMessage) (interface{}, error) { diff --git a/src/core/core_test.go b/src/core/core_test.go index f917030b..ce28f4e5 100644 --- a/src/core/core_test.go +++ b/src/core/core_test.go @@ -75,7 +75,8 @@ func WaitConnected(nodeA, nodeB *Core) bool { return true } */ - if len(nodeA.GetDHT()) > 1 && len(nodeB.GetDHT()) > 1 { + if len(nodeA.GetTree()) > 1 && len(nodeB.GetTree()) > 1 { + time.Sleep(3*time.Second) // FIXME hack, there's still stuff happening internally return true } } From c7ea223a9a1b045ab00572a6407243f01a6a91b3 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 14 May 2023 10:16:33 +0100 Subject: [PATCH 104/293] Update mobile bindings --- contrib/mobile/mobile.go | 19 +++++++++++++------ contrib/mobile/mobile_test.go | 2 +- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/contrib/mobile/mobile.go b/contrib/mobile/mobile.go index 79937997..dcf5eb2a 100644 --- a/contrib/mobile/mobile.go +++ b/contrib/mobile/mobile.go @@ -190,10 +190,9 @@ func (m *Yggdrasil) GetPublicKeyString() string { return hex.EncodeToString(m.core.GetSelf().Key) } -// GetCoordsString gets the node's coordinates -func (m *Yggdrasil) GetCoordsString() string { - return "N/A" - // return fmt.Sprintf("%v", m.core.GetSelf().Coords) +// GetRoutingEntries gets the number of entries in the routing table +func (m *Yggdrasil) GetRoutingEntries() int { + return int(m.core.GetSelf().RoutingEntries) } func (m *Yggdrasil) GetPeersJSON() (result string) { @@ -219,8 +218,16 @@ func (m *Yggdrasil) GetPeersJSON() (result string) { } } -func (m *Yggdrasil) GetDHTJSON() (result string) { - if res, err := json.Marshal(m.core.GetDHT()); err == nil { +func (m *Yggdrasil) GetPathsJSON() (result string) { + if res, err := json.Marshal(m.core.GetPaths()); err == nil { + return string(res) + } else { + return "{}" + } +} + +func (m *Yggdrasil) GetTreeJSON() (result string) { + if res, err := json.Marshal(m.core.GetTree()); err == nil { return string(res) } else { return "{}" diff --git a/contrib/mobile/mobile_test.go b/contrib/mobile/mobile_test.go index 19916407..880e5fc5 100644 --- a/contrib/mobile/mobile_test.go +++ b/contrib/mobile/mobile_test.go @@ -9,7 +9,7 @@ func TestStartYggdrasil(t *testing.T) { } t.Log("Address:", ygg.GetAddressString()) t.Log("Subnet:", ygg.GetSubnetString()) - t.Log("Coords:", ygg.GetCoordsString()) + t.Log("Routing entries:", ygg.GetRoutingEntries()) if err := ygg.Stop(); err != nil { t.Fatalf("Failed to stop Yggdrasil: %s", err) } From 101189a9dc8214fd361130c4c5512de904988b14 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sun, 14 May 2023 21:13:53 -0500 Subject: [PATCH 105/293] update ironwood dependency --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 5a685912..768b2720 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/yggdrasil-network/yggdrasil-go go 1.19 require ( - github.com/Arceliar/ironwood v0.0.0-20230513211242-fac5b9486c3c + github.com/Arceliar/ironwood v0.0.0-20230515021216-e42a1f40fdd5 github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d github.com/cheggaaa/pb/v3 v3.0.8 github.com/gologme/log v1.2.0 diff --git a/go.sum b/go.sum index 66dfae5c..43d65fdc 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Arceliar/ironwood v0.0.0-20230513211242-fac5b9486c3c h1:+MCqerP9VlZw/ECZaDItAgihDDcDGAszgKu/n14WjP0= -github.com/Arceliar/ironwood v0.0.0-20230513211242-fac5b9486c3c/go.mod h1:MIfrhR4b+U6gurd5pln622Zwaf2kzpIvXcnvRZMvlRI= +github.com/Arceliar/ironwood v0.0.0-20230515021216-e42a1f40fdd5 h1:oKPSoQuM8N6oso0xUjA/OhX8Wi9XFJFrs0Xx6Kv0cfg= +github.com/Arceliar/ironwood v0.0.0-20230515021216-e42a1f40fdd5/go.mod h1:MIfrhR4b+U6gurd5pln622Zwaf2kzpIvXcnvRZMvlRI= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q= github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= From c7ee7d96813ff787cfa6192a90f784cd69e1ab47 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sun, 14 May 2023 21:24:08 -0500 Subject: [PATCH 106/293] update ironwood dependency (it should build now...) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 768b2720..dd6dfb48 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/yggdrasil-network/yggdrasil-go go 1.19 require ( - github.com/Arceliar/ironwood v0.0.0-20230515021216-e42a1f40fdd5 + github.com/Arceliar/ironwood v0.0.0-20230515022317-31b976732ebe github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d github.com/cheggaaa/pb/v3 v3.0.8 github.com/gologme/log v1.2.0 diff --git a/go.sum b/go.sum index 43d65fdc..0a0e6b8f 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Arceliar/ironwood v0.0.0-20230515021216-e42a1f40fdd5 h1:oKPSoQuM8N6oso0xUjA/OhX8Wi9XFJFrs0Xx6Kv0cfg= -github.com/Arceliar/ironwood v0.0.0-20230515021216-e42a1f40fdd5/go.mod h1:MIfrhR4b+U6gurd5pln622Zwaf2kzpIvXcnvRZMvlRI= +github.com/Arceliar/ironwood v0.0.0-20230515022317-31b976732ebe h1:u69sr6Y9jqu6sk43Yyt+izLnLGgqCw3OYh2HU+jYUBw= +github.com/Arceliar/ironwood v0.0.0-20230515022317-31b976732ebe/go.mod h1:MIfrhR4b+U6gurd5pln622Zwaf2kzpIvXcnvRZMvlRI= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q= github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= From 7afa23be4c9850cc0d8a3fa5cf2cfd3b554a40e2 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 6 Apr 2023 21:45:49 +0100 Subject: [PATCH 107/293] Link refactoring, admin socket changes --- cmd/yggdrasil/main.go | 351 +++++------- cmd/yggdrasilctl/cmd_line_env.go | 24 +- cmd/yggdrasilctl/main.go | 21 +- contrib/mobile/mobile.go | 17 +- go.mod | 3 +- go.sum | 6 +- src/admin/addpeer.go | 11 +- src/admin/admin.go | 35 +- src/admin/getpeers.go | 51 +- src/config/config.go | 297 ++++++++++- src/config/config_test.go | 66 +-- src/config/defaults.go | 34 ++ src/{defaults => config}/defaults_darwin.go | 2 +- src/{defaults => config}/defaults_freebsd.go | 2 +- src/{defaults => config}/defaults_linux.go | 2 +- src/{defaults => config}/defaults_openbsd.go | 2 +- src/{defaults => config}/defaults_other.go | 2 +- src/{defaults => config}/defaults_windows.go | 2 +- src/core/api.go | 153 +++--- src/core/core.go | 128 +++-- src/core/core_test.go | 17 +- src/core/link.go | 532 +++++++++++-------- src/core/link_socks.go | 38 +- src/core/link_tcp.go | 109 +--- src/core/link_tls.go | 119 +---- src/core/link_unix.go | 64 +-- src/core/options.go | 20 +- src/core/tls.go | 63 +++ src/defaults/defaults.go | 60 --- src/multicast/advertisement.go | 28 + src/multicast/multicast.go | 67 +-- src/tun/tun.go | 10 +- 32 files changed, 1206 insertions(+), 1130 deletions(-) create mode 100644 src/config/defaults.go rename src/{defaults => config}/defaults_darwin.go (97%) rename src/{defaults => config}/defaults_freebsd.go (97%) rename src/{defaults => config}/defaults_linux.go (97%) rename src/{defaults => config}/defaults_openbsd.go (97%) rename src/{defaults => config}/defaults_other.go (97%) rename src/{defaults => config}/defaults_windows.go (97%) create mode 100644 src/core/tls.go delete mode 100644 src/defaults/defaults.go create mode 100644 src/multicast/advertisement.go diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index f85525d6..7ac45260 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -1,34 +1,27 @@ package main import ( - "bytes" "context" "crypto/ed25519" "encoding/hex" "encoding/json" "flag" "fmt" - "io" "net" "os" "os/signal" "regexp" "strings" - "sync" "syscall" - "golang.org/x/text/encoding/unicode" - "github.com/gologme/log" gsyslog "github.com/hashicorp/go-syslog" - "github.com/hjson/hjson-go" + "github.com/hjson/hjson-go/v4" "github.com/kardianos/minwinsvc" - "github.com/mitchellh/mapstructure" "github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/admin" "github.com/yggdrasil-network/yggdrasil-go/src/config" - "github.com/yggdrasil-network/yggdrasil-go/src/defaults" "github.com/yggdrasil-network/yggdrasil-go/src/ipv6rwc" "github.com/yggdrasil-network/yggdrasil-go/src/core" @@ -44,122 +37,15 @@ type node struct { admin *admin.AdminSocket } -func readConfig(log *log.Logger, useconf bool, useconffile string, normaliseconf bool) *config.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 conf []byte - var err error - if useconffile != "" { - // Read the file from the filesystem - conf, err = os.ReadFile(useconffile) - } else { - // Read the file from stdin. - conf, err = io.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.Equal(conf[0:2], []byte{0xFF, 0xFE}) || - bytes.Equal(conf[0:2], []byte{0xFE, 0xFF}) { - utf := unicode.UTF16(unicode.BigEndian, unicode.UseBOM) - decoder := utf.NewDecoder() - conf, err = decoder.Bytes(conf) - 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 := defaults.GenerateConfig() - var dat map[string]interface{} - if err := hjson.Unmarshal(conf, &dat); err != nil { - panic(err) - } - // Sanitise the config - confJson, err := json.Marshal(dat) - if err != nil { - panic(err) - } - if err := json.Unmarshal(confJson, &cfg); err != nil { - panic(err) - } - // 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 { - cfg := defaults.GenerateConfig() - var bs []byte - var err error - if isjson { - bs, err = json.MarshalIndent(cfg, "", " ") - } else { - bs, err = hjson.Marshal(cfg) - } - if err != nil { - panic(err) - } - 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 - } - } -} - -type yggArgs struct { - genconf bool - useconf bool - normaliseconf bool - confjson bool - autoconf bool - ver bool - getaddr bool - getsnet bool - useconffile string - logto string - loglevel string -} - -func getArgs() yggArgs { +// The main function is responsible for configuring and starting Yggdrasil. +func main() { genconf := flag.Bool("genconf", false, "print a new config to stdout") useconf := flag.Bool("useconf", false, "read HJSON/JSON config from stdin") useconffile := flag.String("useconffile", "", "read HJSON/JSON config from specified file path") normaliseconf := flag.Bool("normaliseconf", false, "use in combination with either -useconf or -useconffile, outputs your configuration normalised") + exportkey := flag.Bool("exportkey", false, "use in combination with either -useconf or -useconffile, outputs your private key in PEM format") + exportcsr := flag.Bool("exportcsr", false, "use in combination with either -useconf or -useconffile, outputs your self-signed certificate request in PEM format") + exportcert := flag.Bool("exportcert", false, "use in combination with either -useconf or -useconffile, outputs your self-signed certificate in PEM format") 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") @@ -168,34 +54,26 @@ func getArgs() yggArgs { 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() - return yggArgs{ - genconf: *genconf, - useconf: *useconf, - useconffile: *useconffile, - normaliseconf: *normaliseconf, - confjson: *confjson, - autoconf: *autoconf, - ver: *ver, - logto: *logto, - getaddr: *getaddr, - getsnet: *getsnet, - loglevel: *loglevel, - } -} -// The main function is responsible for configuring and starting Yggdrasil. -func run(args yggArgs, ctx context.Context) { + // Catch interrupts from the operating system to exit gracefully. + ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) + + // Capture the service being stopped on Windows. + minwinsvc.SetOnExit(cancel) + // Create a new logger that logs output to stdout. var logger *log.Logger - switch args.logto { + switch *logto { case "stdout": logger = log.New(os.Stdout, "", log.Flags()) + case "syslog": if syslogger, err := gsyslog.NewLogger(gsyslog.LOG_NOTICE, "DAEMON", version.BuildName()); err == nil { logger = log.New(syslogger, "", log.Flags()) } + default: - if logfd, err := os.OpenFile(args.logto, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644); err == nil { + if logfd, err := os.OpenFile(*logto, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644); err == nil { logger = log.New(logfd, "", log.Flags()) } } @@ -203,87 +81,115 @@ func run(args yggArgs, ctx context.Context) { logger = log.New(os.Stdout, "", log.Flags()) logger.Warnln("Logging defaulting to stdout") } - - if args.normaliseconf { + if *normaliseconf { setLogLevel("error", logger) } else { - setLogLevel(args.loglevel, logger) + setLogLevel(*loglevel, logger) } - var cfg *config.NodeConfig + cfg := config.GenerateConfig() var err error switch { - case args.ver: + case *ver: fmt.Println("Build name:", version.BuildName()) fmt.Println("Build version:", version.BuildVersion()) return - case args.autoconf: + + case *autoconf: // Use an autoconf-generated config, this will give us random keys and // port numbers, and will use an automatically selected TUN interface. - cfg = defaults.GenerateConfig() - case args.useconffile != "" || args.useconf: - // Read the configuration from either stdin or from the filesystem - cfg = readConfig(logger, args.useconf, args.useconffile, args.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 - // convert from plain JSON to commented HJSON. - if args.normaliseconf { - var bs []byte - if args.confjson { - bs, err = json.MarshalIndent(cfg, "", " ") - } else { - bs, err = hjson.Marshal(cfg) - } - if err != nil { - panic(err) - } - fmt.Println(string(bs)) - return + + case *useconf: + if _, err := cfg.ReadFrom(os.Stdin); err != nil { + panic(err) } - case args.genconf: - // Generate a new configuration and print it to stdout. - fmt.Println(doGenconf(args.confjson)) + + case *useconffile != "": + f, err := os.Open(*useconffile) + if err != nil { + panic(err) + } + if _, err := cfg.ReadFrom(f); err != nil { + panic(err) + } + _ = f.Close() + + case *genconf: + var bs []byte + if *confjson { + bs, err = json.MarshalIndent(cfg, "", " ") + } else { + bs, err = hjson.Marshal(cfg) + } + if err != nil { + panic(err) + } + fmt.Println(string(bs)) return + default: - // No flags were provided, therefore print the list of flags to stdout. fmt.Println("Usage:") flag.PrintDefaults() - if args.getaddr || args.getsnet { + if *getaddr || *getsnet { fmt.Println("\nError: You need to specify some config data using -useconf or -useconffile.") } } - // Have we got a working configuration? If we don't then it probably means - // that neither -autoconf, -useconf or -useconffile were set above. Stop - // if we don't. - if cfg == nil { - return - } - // Have we been asked for the node address yet? If so, print it and then stop. - getNodeKey := func() ed25519.PublicKey { - if pubkey, err := hex.DecodeString(cfg.PrivateKey); err == nil { - return ed25519.PrivateKey(pubkey).Public().(ed25519.PublicKey) - } - return nil - } + + privateKey := ed25519.PrivateKey(cfg.PrivateKey) + publicKey := privateKey.Public().(ed25519.PublicKey) + switch { - case args.getaddr: - if key := getNodeKey(); key != nil { - addr := address.AddrForKey(key) - ip := net.IP(addr[:]) - fmt.Println(ip.String()) - } + case *getaddr: + addr := address.AddrForKey(publicKey) + ip := net.IP(addr[:]) + fmt.Println(ip.String()) return - case args.getsnet: - if key := getNodeKey(); key != nil { - snet := address.SubnetForKey(key) - ipnet := net.IPNet{ - IP: append(snet[:], 0, 0, 0, 0, 0, 0, 0, 0), - Mask: net.CIDRMask(len(snet)*8, 128), - } - fmt.Println(ipnet.String()) + + case *getsnet: + snet := address.SubnetForKey(publicKey) + ipnet := net.IPNet{ + IP: append(snet[:], 0, 0, 0, 0, 0, 0, 0, 0), + Mask: net.CIDRMask(len(snet)*8, 128), } + fmt.Println(ipnet.String()) + return + + case *normaliseconf: + var bs []byte + if *confjson { + bs, err = json.MarshalIndent(cfg, "", " ") + } else { + bs, err = hjson.Marshal(cfg) + } + if err != nil { + panic(err) + } + fmt.Println(string(bs)) + return + + case *exportkey: + pem, err := cfg.MarshalPEMPrivateKey() + if err != nil { + panic(err) + } + fmt.Println(string(pem)) + return + + case *exportcsr: + pem, err := cfg.GenerateCertificateSigningRequest() + if err != nil { + panic(err) + } + fmt.Println(string(pem)) + return + + case *exportcert: + pem, err := cfg.MarshalPEMCertificate() + if err != nil { + panic(err) + } + fmt.Println(string(pem)) return } @@ -291,10 +197,6 @@ func run(args yggArgs, ctx context.Context) { // Setup the Yggdrasil node itself. { - sk, err := hex.DecodeString(cfg.PrivateKey) - if err != nil { - panic(err) - } options := []core.SetupOption{ core.NodeInfo(cfg.NodeInfo), core.NodeInfoPrivacy(cfg.NodeInfoPrivacy), @@ -310,6 +212,9 @@ func run(args yggArgs, ctx context.Context) { options = append(options, core.Peer{URI: peer, SourceInterface: intf}) } } + for _, root := range cfg.RootCertificates { + options = append(options, core.RootCertificate(*root)) + } for _, allowed := range cfg.AllowedPublicKeys { k, err := hex.DecodeString(allowed) if err != nil { @@ -317,7 +222,7 @@ func run(args yggArgs, ctx context.Context) { } options = append(options, core.AllowedPublicKey(k[:])) } - if n.core, err = core.New(sk[:], logger, options...); err != nil { + if n.core, err = core.New(cfg.Certificate, logger, options...); err != nil { panic(err) } } @@ -369,15 +274,6 @@ func run(args yggArgs, ctx context.Context) { } } - // Make some nice output that tells us what our IPv6 address and subnet are. - // This is just logged to stdout for the user. - address := n.core.Address() - subnet := n.core.Subnet() - public := n.core.GetSelf().Key - logger.Infof("Your public key is %s", hex.EncodeToString(public[:])) - logger.Infof("Your IPv6 address is %s", address.String()) - logger.Infof("Your IPv6 subnet is %s", subnet.String()) - // Block until we are told to shut down. <-ctx.Done() @@ -388,21 +284,28 @@ func run(args yggArgs, ctx context.Context) { n.core.Stop() } -func main() { - args := getArgs() +func setLogLevel(loglevel string, logger *log.Logger) { + levels := [...]string{"error", "warn", "info", "debug", "trace"} + loglevel = strings.ToLower(loglevel) - // Catch interrupts from the operating system to exit gracefully. - ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) + contains := func() bool { + for _, l := range levels { + if l == loglevel { + return true + } + } + return false + } - // Capture the service being stopped on Windows. - minwinsvc.SetOnExit(cancel) + if !contains() { // set default log level + logger.Infoln("Loglevel parse failed. Set default level(info)") + loglevel = "info" + } - // Start the node, block and then wait for it to shut down. - var wg sync.WaitGroup - wg.Add(1) - go func() { - defer wg.Done() - run(args, ctx) - }() - wg.Wait() + for _, l := range levels { + logger.EnableLevel(l) + if l == loglevel { + break + } + } } diff --git a/cmd/yggdrasilctl/cmd_line_env.go b/cmd/yggdrasilctl/cmd_line_env.go index 9fcabad9..be24a558 100644 --- a/cmd/yggdrasilctl/cmd_line_env.go +++ b/cmd/yggdrasilctl/cmd_line_env.go @@ -7,10 +7,10 @@ import ( "log" "os" - "github.com/hjson/hjson-go" + "github.com/hjson/hjson-go/v4" "golang.org/x/text/encoding/unicode" - "github.com/yggdrasil-network/yggdrasil-go/src/defaults" + "github.com/yggdrasil-network/yggdrasil-go/src/config" ) type CmdLineEnv struct { @@ -21,7 +21,7 @@ type CmdLineEnv struct { func newCmdLineEnv() CmdLineEnv { var cmdLineEnv CmdLineEnv - cmdLineEnv.endpoint = defaults.GetDefaults().DefaultAdminListen + cmdLineEnv.endpoint = config.GetDefaults().DefaultAdminListen return cmdLineEnv } @@ -58,31 +58,31 @@ func (cmdLineEnv *CmdLineEnv) parseFlagsAndArgs() { func (cmdLineEnv *CmdLineEnv) setEndpoint(logger *log.Logger) { if cmdLineEnv.server == cmdLineEnv.endpoint { - if config, err := os.ReadFile(defaults.GetDefaults().DefaultConfigFile); err == nil { - if bytes.Equal(config[0:2], []byte{0xFF, 0xFE}) || - bytes.Equal(config[0:2], []byte{0xFE, 0xFF}) { + if cfg, err := os.ReadFile(config.GetDefaults().DefaultConfigFile); err == nil { + if bytes.Equal(cfg[0:2], []byte{0xFF, 0xFE}) || + bytes.Equal(cfg[0:2], []byte{0xFE, 0xFF}) { utf := unicode.UTF16(unicode.BigEndian, unicode.UseBOM) decoder := utf.NewDecoder() - config, err = decoder.Bytes(config) + cfg, err = decoder.Bytes(cfg) if err != nil { panic(err) } } var dat map[string]interface{} - if err := hjson.Unmarshal(config, &dat); err != nil { + if err := hjson.Unmarshal(cfg, &dat); err != nil { panic(err) } if ep, ok := dat["AdminListen"].(string); ok && (ep != "none" && ep != "") { cmdLineEnv.endpoint = ep - logger.Println("Found platform default config file", defaults.GetDefaults().DefaultConfigFile) + logger.Println("Found platform default config file", config.GetDefaults().DefaultConfigFile) logger.Println("Using endpoint", cmdLineEnv.endpoint, "from AdminListen") } else { logger.Println("Configuration file doesn't contain appropriate AdminListen option") - logger.Println("Falling back to platform default", defaults.GetDefaults().DefaultAdminListen) + logger.Println("Falling back to platform default", config.GetDefaults().DefaultAdminListen) } } else { - logger.Println("Can't open config file from default location", defaults.GetDefaults().DefaultConfigFile) - logger.Println("Falling back to platform default", defaults.GetDefaults().DefaultAdminListen) + logger.Println("Can't open config file from default location", config.GetDefaults().DefaultConfigFile) + logger.Println("Falling back to platform default", config.GetDefaults().DefaultAdminListen) } } else { cmdLineEnv.endpoint = cmdLineEnv.server diff --git a/cmd/yggdrasilctl/main.go b/cmd/yggdrasilctl/main.go index 3c191c87..884fc052 100644 --- a/cmd/yggdrasilctl/main.go +++ b/cmd/yggdrasilctl/main.go @@ -174,17 +174,30 @@ func run() int { if err := json.Unmarshal(recv.Response, &resp); err != nil { panic(err) } - table.SetHeader([]string{"Port", "Public Key", "IP Address", "Uptime", "RX", "TX", "Pr", "URI"}) + table.SetHeader([]string{"URI", "State", "Dir", "IP Address", "Uptime", "RX", "TX", "Pr", "Last Error"}) for _, peer := range resp.Peers { + state, lasterr, dir := "Up", "(none)", "Out" + if !peer.Up { + state, lasterr = "Down", fmt.Sprintf("%s (%s ago)", peer.LastError, peer.LastErrorTime.Round(time.Second)) + } + if peer.Inbound { + dir = "In" + } + uri, err := url.Parse(peer.URI) + if err != nil { + panic(err) + } + uri.RawQuery = "" table.Append([]string{ - fmt.Sprintf("%d", peer.Port), - peer.PublicKey, + uri.String(), + state, + dir, peer.IPAddress, (time.Duration(peer.Uptime) * time.Second).String(), peer.RXBytes.String(), peer.TXBytes.String(), fmt.Sprintf("%d", peer.Priority), - peer.Remote, + lasterr, }) } table.Render() diff --git a/contrib/mobile/mobile.go b/contrib/mobile/mobile.go index dcf5eb2a..6b036094 100644 --- a/contrib/mobile/mobile.go +++ b/contrib/mobile/mobile.go @@ -11,7 +11,6 @@ import ( "github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/config" "github.com/yggdrasil-network/yggdrasil-go/src/core" - "github.com/yggdrasil-network/yggdrasil-go/src/defaults" "github.com/yggdrasil-network/yggdrasil-go/src/ipv6rwc" "github.com/yggdrasil-network/yggdrasil-go/src/multicast" "github.com/yggdrasil-network/yggdrasil-go/src/version" @@ -44,16 +43,12 @@ func (m *Yggdrasil) StartJSON(configjson []byte) error { logger.EnableLevel("error") logger.EnableLevel("warn") logger.EnableLevel("info") - m.config = defaults.GenerateConfig() - if err := json.Unmarshal(configjson, &m.config); err != nil { + m.config = config.GenerateConfig() + if err := m.config.UnmarshalHJSON(configjson); err != nil { return err } // Setup the Yggdrasil node itself. { - sk, err := hex.DecodeString(m.config.PrivateKey) - if err != nil { - panic(err) - } options := []core.SetupOption{} for _, peer := range m.config.Peers { options = append(options, core.Peer{URI: peer}) @@ -70,7 +65,11 @@ func (m *Yggdrasil) StartJSON(configjson []byte) error { } options = append(options, core.AllowedPublicKey(k[:])) } - m.core, err = core.New(sk[:], logger, options...) + for _, root := range m.config.RootCertificates { + options = append(options, core.RootCertificate(*root)) + } + var err error + m.core, err = core.New(m.config.Certificate, logger, options...) if err != nil { panic(err) } @@ -165,7 +164,7 @@ func (m *Yggdrasil) RetryPeersNow() { // GenerateConfigJSON generates mobile-friendly configuration in JSON format func GenerateConfigJSON() []byte { - nc := defaults.GenerateConfig() + nc := config.GenerateConfig() nc.IfName = "none" if json, err := json.Marshal(nc); err == nil { return json diff --git a/go.mod b/go.mod index dd6dfb48..4fd73400 100644 --- a/go.mod +++ b/go.mod @@ -8,9 +8,8 @@ require ( github.com/cheggaaa/pb/v3 v3.0.8 github.com/gologme/log v1.2.0 github.com/hashicorp/go-syslog v1.0.0 - github.com/hjson/hjson-go v3.1.0+incompatible + github.com/hjson/hjson-go/v4 v4.3.0 github.com/kardianos/minwinsvc v1.0.2 - github.com/mitchellh/mapstructure v1.4.1 github.com/vishvananda/netlink v1.1.0 golang.org/x/mobile v0.0.0-20221110043201-43a038452099 golang.org/x/net v0.9.0 diff --git a/go.sum b/go.sum index 0a0e6b8f..ed5f7448 100644 --- a/go.sum +++ b/go.sum @@ -19,8 +19,8 @@ github.com/gologme/log v1.2.0 h1:Ya5Ip/KD6FX7uH0S31QO87nCCSucKtF44TLbTtO7V4c= github.com/gologme/log v1.2.0/go.mod h1:gq31gQ8wEHkR+WekdWsqDuf8pXTUZA9BnnzTuPz1Y9U= github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hjson/hjson-go v3.1.0+incompatible h1:DY/9yE8ey8Zv22bY+mHV1uk2yRy0h8tKhZ77hEdi0Aw= -github.com/hjson/hjson-go v3.1.0+incompatible/go.mod h1:qsetwF8NlsTsOTwZTApNlTCerV+b2GjYRRcIk4JMFio= +github.com/hjson/hjson-go/v4 v4.3.0 h1:dyrzJdqqFGhHt+FSrs5n9s6b0fPM8oSJdWo+oS3YnJw= +github.com/hjson/hjson-go/v4 v4.3.0/go.mod h1:KaYt3bTw3zhBjYqnXkYywcYctk0A2nxeEFTse3rH13E= github.com/kardianos/minwinsvc v1.0.2 h1:JmZKFJQrmTGa/WiW+vkJXKmfzdjabuEW4Tirj5lLdR0= github.com/kardianos/minwinsvc v1.0.2/go.mod h1:LUZNYhNmxujx2tR7FbdxqYJ9XDDoCd3MQcl1o//FWl4= github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= @@ -32,8 +32,6 @@ github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= -github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= diff --git a/src/admin/addpeer.go b/src/admin/addpeer.go index f5e37ee9..843ed52a 100644 --- a/src/admin/addpeer.go +++ b/src/admin/addpeer.go @@ -1,5 +1,10 @@ package admin +import ( + "fmt" + "net/url" +) + type AddPeerRequest struct { Uri string `json:"uri"` Sintf string `json:"interface,omitempty"` @@ -8,5 +13,9 @@ type AddPeerRequest struct { type AddPeerResponse struct{} func (a *AdminSocket) addPeerHandler(req *AddPeerRequest, res *AddPeerResponse) error { - return a.core.AddPeer(req.Uri, req.Sintf) + u, err := url.Parse(req.Uri) + if err != nil { + return fmt.Errorf("unable to parse peering URI: %w", err) + } + return a.core.AddPeer(u, req.Sintf) } diff --git a/src/admin/admin.go b/src/admin/admin.go index d0d444be..8fa76c07 100644 --- a/src/admin/admin.go +++ b/src/admin/admin.go @@ -35,10 +35,10 @@ type AdminSocketRequest struct { } type AdminSocketResponse struct { - Status string `json:"status"` - Error string `json:"error,omitempty"` - Request json.RawMessage `json:"request"` - Response json.RawMessage `json:"response"` + Status string `json:"status"` + Error string `json:"error,omitempty"` + Request AdminSocketRequest `json:"request"` + Response json.RawMessage `json:"response"` } type handler struct { @@ -309,18 +309,22 @@ func (a *AdminSocket) handleRequest(conn net.Conn) { defer conn.Close() - defer func() { - r := recover() - if r != nil { - a.log.Debugln("Admin socket error:", r) - if err := encoder.Encode(&ErrorResponse{ - Error: "Check your syntax and input types", - }); err != nil { - a.log.Debugln("Admin socket JSON encode error:", err) + /* + defer func() { + r := recover() + if r != nil { + fmt.Println("ERROR:", r) + a.log.Debugln("Admin socket error:", r) + if err := encoder.Encode(&ErrorResponse{ + Error: "Check your syntax and input types", + }); err != nil { + fmt.Println("ERROR 2:", err) + a.log.Debugln("Admin socket JSON encode error:", err) + } + conn.Close() } - conn.Close() - } - }() + }() + */ for { var err error @@ -335,6 +339,7 @@ func (a *AdminSocket) handleRequest(conn net.Conn) { if err = json.Unmarshal(buf, &req); err != nil { return fmt.Errorf("Failed to unmarshal request") } + resp.Request = req if req.Name == "" { return fmt.Errorf("No request specified") } diff --git a/src/admin/getpeers.go b/src/admin/getpeers.go index 3d522fb9..c6cd5be4 100644 --- a/src/admin/getpeers.go +++ b/src/admin/getpeers.go @@ -4,6 +4,7 @@ import ( "encoding/hex" "net" "sort" + "time" "github.com/yggdrasil-network/yggdrasil-go/src/address" ) @@ -16,31 +17,43 @@ type GetPeersResponse struct { } type PeerEntry struct { - IPAddress string `json:"address"` - PublicKey string `json:"key"` - Port uint64 `json:"port"` - Priority uint64 `json:"priority"` - Remote string `json:"remote"` - RXBytes DataUnit `json:"bytes_recvd"` - TXBytes DataUnit `json:"bytes_sent"` - Uptime float64 `json:"uptime"` + URI string `json:"remote,omitempty"` + Up bool `json:"up"` + Inbound bool `json:"inbound"` + IPAddress string `json:"address,omitempty"` + PublicKey string `json:"key"` + Port uint64 `json:"port"` + Priority uint64 `json:"priority"` + RXBytes DataUnit `json:"bytes_recvd,omitempty"` + TXBytes DataUnit `json:"bytes_sent,omitempty"` + Uptime float64 `json:"uptime,omitempty"` + LastError string `json:"last_error,omitempty"` + LastErrorTime time.Duration `json:"last_error_time,omitempty"` } func (a *AdminSocket) getPeersHandler(req *GetPeersRequest, res *GetPeersResponse) error { peers := a.core.GetPeers() res.Peers = make([]PeerEntry, 0, len(peers)) for _, p := range peers { - addr := address.AddrForKey(p.Key) - res.Peers = append(res.Peers, PeerEntry{ - IPAddress: net.IP(addr[:]).String(), - PublicKey: hex.EncodeToString(p.Key), - Port: p.Port, - Priority: uint64(p.Priority), // can't be uint8 thanks to gobind - Remote: p.Remote, - RXBytes: DataUnit(p.RXBytes), - TXBytes: DataUnit(p.TXBytes), - Uptime: p.Uptime.Seconds(), - }) + peer := PeerEntry{ + Port: p.Port, + Up: p.Up, + Inbound: p.Inbound, + Priority: uint64(p.Priority), // can't be uint8 thanks to gobind + URI: p.URI, + RXBytes: DataUnit(p.RXBytes), + TXBytes: DataUnit(p.TXBytes), + Uptime: p.Uptime.Seconds(), + } + if addr := address.AddrForKey(p.Key); addr != nil { + peer.PublicKey = hex.EncodeToString(p.Key) + peer.IPAddress = net.IP(addr[:]).String() + } + if p.LastError != nil { + peer.LastError = p.LastError.Error() + peer.LastErrorTime = time.Since(p.LastErrorTime) + } + res.Peers = append(res.Peers, peer) } sort.Slice(res.Peers, func(i, j int) bool { if res.Peers[i].Port == res.Peers[j].Port { diff --git a/src/config/config.go b/src/config/config.go index f7f0f6ba..76f7476b 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -17,26 +17,45 @@ configuration option that is not provided. package config import ( + "bytes" "crypto/ed25519" + "crypto/rand" + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" "encoding/hex" + "encoding/json" + "encoding/pem" + "fmt" + "io" + "math/big" + "os" + "time" + + "github.com/hjson/hjson-go/v4" + "golang.org/x/text/encoding/unicode" ) // NodeConfig is the main configuration structure, containing configuration // options that are necessary for an Yggdrasil node to run. You will need to // supply one of these structs to the Yggdrasil core when starting a node. type NodeConfig struct { - Peers []string `comment:"List of connection strings for outbound peer connections in URI format,\ne.g. tls://a.b.c.d:e or socks://a.b.c.d:e/f.g.h.i:j. These connections\nwill obey the operating system routing table, therefore you should\nuse this section when you may connect via different interfaces."` - InterfacePeers map[string][]string `comment:"List of connection strings for outbound peer connections in URI format,\narranged by source interface, e.g. { \"eth0\": [ \"tls://a.b.c.d:e\" ] }.\nNote that SOCKS peerings will NOT be affected by this option and should\ngo in the \"Peers\" section instead."` - Listen []string `comment:"Listen addresses for incoming connections. You will need to add\nlisteners in order to accept incoming peerings from non-local nodes.\nMulticast peer discovery will work regardless of any listeners set\nhere. Each listener should be specified in URI format as above, e.g.\ntls://0.0.0.0:0 or tls://[::]:0 to listen on all interfaces."` - 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."` - MulticastInterfaces []MulticastInterfaceConfig `comment:"Configuration for which interfaces multicast peer discovery should be\nenabled on. Each entry in the list should be a json object which may\ncontain Regex, Beacon, Listen, and Port. Regex is a regular expression\nwhich is matched against an interface name, and interfaces use the\nfirst configuration that they match gainst. Beacon configures whether\nor not the node should send link-local multicast beacons to advertise\ntheir presence, while listening for incoming connections on Port.\nListen controls whether or not the node listens for multicast beacons\nand opens outgoing connections."` - AllowedPublicKeys []string `comment:"List of peer public keys to allow incoming peering connections\nfrom. If left empty/undefined then all connections will be allowed\nby default. This does not affect outgoing peerings, nor does it\naffect link-local peers discovered via multicast."` - PublicKey string `comment:"Your public key. Your peers may ask you for this to put\ninto their AllowedPublicKeys configuration."` - PrivateKey string `comment:"Your private key. DO NOT share this with anyone!"` - IfName string `comment:"Local network interface name for TUN adapter, or \"auto\" to select\nan interface automatically, or \"none\" to run without TUN."` - IfMTU uint64 `comment:"Maximum Transmission Unit (MTU) size for your local TUN interface.\nDefault is the largest supported size for your platform. The lowest\npossible value is 1280."` - NodeInfoPrivacy bool `comment:"By default, nodeinfo contains some defaults including the platform,\narchitecture and Yggdrasil version. These can help when surveying\nthe network and diagnosing network routing problems. Enabling\nnodeinfo privacy prevents this, so that only items specified in\n\"NodeInfo\" are sent back if specified."` - NodeInfo map[string]interface{} `comment:"Optional node info. This must be a { \"key\": \"value\", ... } map\nor set as null. This is entirely optional but, if set, is visible\nto the whole network on request."` + PrivateKey KeyBytes `comment:"Your private key. DO NOT share this with anyone!"` + PrivateKeyPath string `json:",omitempty"` + Certificate *tls.Certificate `json:"-"` + CertificatePath string `json:",omitempty"` + RootCertificates []*x509.Certificate `json:"-"` + RootCertificatePaths []string `json:",omitempty"` + Peers []string `comment:"List of connection strings for outbound peer connections in URI format,\ne.g. tls://a.b.c.d:e or socks://a.b.c.d:e/f.g.h.i:j. These connections\nwill obey the operating system routing table, therefore you should\nuse this section when you may connect via different interfaces."` + InterfacePeers map[string][]string `comment:"List of connection strings for outbound peer connections in URI format,\narranged by source interface, e.g. { \"eth0\": [ \"tls://a.b.c.d:e\" ] }.\nNote that SOCKS peerings will NOT be affected by this option and should\ngo in the \"Peers\" section instead."` + Listen []string `comment:"Listen addresses for incoming connections. You will need to add\nlisteners in order to accept incoming peerings from non-local nodes.\nMulticast peer discovery will work regardless of any listeners set\nhere. Each listener should be specified in URI format as above, e.g.\ntls://0.0.0.0:0 or tls://[::]:0 to listen on all interfaces."` + 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."` + MulticastInterfaces []MulticastInterfaceConfig `comment:"Configuration for which interfaces multicast peer discovery should be\nenabled on. Each entry in the list should be a json object which may\ncontain Regex, Beacon, Listen, and Port. Regex is a regular expression\nwhich is matched against an interface name, and interfaces use the\nfirst configuration that they match gainst. Beacon configures whether\nor not the node should send link-local multicast beacons to advertise\ntheir presence, while listening for incoming connections on Port.\nListen controls whether or not the node listens for multicast beacons\nand opens outgoing connections."` + AllowedPublicKeys []string `comment:"List of peer public keys to allow incoming peering connections\nfrom. If left empty/undefined then all connections will be allowed\nby default. This does not affect outgoing peerings, nor does it\naffect link-local peers discovered via multicast."` + IfName string `comment:"Local network interface name for TUN adapter, or \"auto\" to select\nan interface automatically, or \"none\" to run without TUN."` + IfMTU uint64 `comment:"Maximum Transmission Unit (MTU) size for your local TUN interface.\nDefault is the largest supported size for your platform. The lowest\npossible value is 1280."` + NodeInfoPrivacy bool `comment:"By default, nodeinfo contains some defaults including the platform,\narchitecture and Yggdrasil version. These can help when surveying\nthe network and diagnosing network routing problems. Enabling\nnodeinfo privacy prevents this, so that only items specified in\n\"NodeInfo\" are sent back if specified."` + NodeInfo map[string]interface{} `comment:"Optional node info. This must be a { \"key\": \"value\", ... } map\nor set as null. This is entirely optional but, if set, is visible\nto the whole network on request."` } type MulticastInterfaceConfig struct { @@ -47,14 +66,254 @@ type MulticastInterfaceConfig struct { Priority uint64 // really uint8, but gobind won't export it } -// NewSigningKeys replaces the signing keypair in the NodeConfig with a new -// signing keypair. The signing keys are used by the switch to derive the -// structure of the spanning tree. -func (cfg *NodeConfig) NewKeys() { - spub, spriv, err := ed25519.GenerateKey(nil) +// Generates default configuration and returns a pointer to the resulting +// NodeConfig. This is used when outputting the -genconf parameter and also when +// using -autoconf. +func GenerateConfig() *NodeConfig { + // Get the defaults for the platform. + defaults := GetDefaults() + // Create a node configuration and populate it. + cfg := new(NodeConfig) + cfg.NewPrivateKey() + cfg.Listen = []string{} + cfg.AdminListen = defaults.DefaultAdminListen + cfg.Peers = []string{} + cfg.InterfacePeers = map[string][]string{} + cfg.AllowedPublicKeys = []string{} + cfg.MulticastInterfaces = defaults.DefaultMulticastInterfaces + cfg.IfName = defaults.DefaultIfName + cfg.IfMTU = defaults.DefaultIfMTU + cfg.NodeInfoPrivacy = false + + return cfg +} + +func (cfg *NodeConfig) ReadFrom(r io.Reader) (int64, error) { + conf, err := io.ReadAll(r) + if err != nil { + return 0, err + } + n := int64(len(conf)) + // 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.Equal(conf[0:2], []byte{0xFF, 0xFE}) || + bytes.Equal(conf[0:2], []byte{0xFE, 0xFF}) { + utf := unicode.UTF16(unicode.BigEndian, unicode.UseBOM) + decoder := utf.NewDecoder() + conf, err = decoder.Bytes(conf) + if err != nil { + return n, 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() + if err := cfg.UnmarshalHJSON(conf); err != nil { + return n, err + } + return n, nil +} + +func (cfg *NodeConfig) UnmarshalHJSON(b []byte) error { + if err := hjson.Unmarshal(b, cfg); err != nil { + return err + } + if cfg.PrivateKeyPath != "" { + cfg.PrivateKey = nil + f, err := os.ReadFile(cfg.PrivateKeyPath) + if err != nil { + return err + } + if err := cfg.UnmarshalPEMPrivateKey(f); err != nil { + return err + } + } + if cfg.CertificatePath != "" { + if cfg.PrivateKeyPath == "" { + return fmt.Errorf("CertificatePath requires PrivateKeyPath") + } + cfg.Certificate = nil + f, err := os.ReadFile(cfg.CertificatePath) + if err != nil { + return err + } + if err := cfg.UnmarshalPEMCertificate(f); err != nil { + return err + } + } + if cfg.Certificate == nil { + if err := cfg.GenerateSelfSignedCertificate(); err != nil { + return err + } + } + cfg.RootCertificates = cfg.RootCertificates[:0] + for _, path := range cfg.RootCertificatePaths { + f, err := os.ReadFile(path) + if err != nil { + return err + } + if err := cfg.UnmarshalRootCertificate(f); err != nil { + return err + } + } + return nil +} + +func (cfg *NodeConfig) UnmarshalRootCertificate(b []byte) error { + p, _ := pem.Decode(b) + if p == nil { + return fmt.Errorf("failed to parse PEM file") + } + if p.Type != "CERTIFICATE" { + return fmt.Errorf("unexpected PEM type %q", p.Type) + } + cert, err := x509.ParseCertificate(p.Bytes) + if err != nil { + return fmt.Errorf("failed to load X.509 keypair: %w", err) + } + if !cert.IsCA { + return fmt.Errorf("supplied root certificate is not a certificate authority") + } + cfg.RootCertificates = append(cfg.RootCertificates, cert) + return nil +} + +// RFC5280 section 4.1.2.5 +var notAfterNeverExpires = time.Date(9999, time.December, 31, 23, 59, 59, 0, time.UTC) + +func (cfg *NodeConfig) GenerateSelfSignedCertificate() error { + key, err := cfg.MarshalPEMPrivateKey() + if err != nil { + return err + } + cert, err := cfg.MarshalPEMCertificate() + if err != nil { + return err + } + tlsCert, err := tls.X509KeyPair(cert, key) + if err != nil { + return err + } + cfg.Certificate = &tlsCert + return nil +} + +func (cfg *NodeConfig) GenerateCertificateSigningRequest() ([]byte, error) { + template := &x509.CertificateRequest{ + Subject: pkix.Name{ + CommonName: hex.EncodeToString(cfg.PrivateKey), + }, + SignatureAlgorithm: x509.PureEd25519, + } + + csrBytes, err := x509.CreateCertificateRequest(rand.Reader, template, ed25519.PrivateKey(cfg.PrivateKey)) + if err != nil { + return nil, err + } + + pemBytes := bytes.NewBuffer(nil) + if err := pem.Encode(pemBytes, &pem.Block{Type: "CERTIFICATE REQUEST", Bytes: csrBytes}); err != nil { + return nil, err + } + return pemBytes.Bytes(), nil +} + +func (cfg *NodeConfig) MarshalPEMCertificate() ([]byte, error) { + privateKey := ed25519.PrivateKey(cfg.PrivateKey) + publicKey := privateKey.Public().(ed25519.PublicKey) + + cert := &x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + CommonName: hex.EncodeToString(publicKey), + }, + NotBefore: time.Now(), + NotAfter: notAfterNeverExpires, + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + } + + certbytes, err := x509.CreateCertificate(rand.Reader, cert, cert, publicKey, privateKey) + if err != nil { + return nil, err + } + + block := &pem.Block{ + Type: "CERTIFICATE", + Bytes: certbytes, + } + return pem.EncodeToMemory(block), nil +} + +func (cfg *NodeConfig) UnmarshalPEMCertificate(b []byte) error { + tlsCert, err := tls.LoadX509KeyPair(cfg.CertificatePath, cfg.PrivateKeyPath) + if err != nil { + return fmt.Errorf("failed to load X.509 keypair: %w", err) + } + cfg.Certificate = &tlsCert + return nil +} + +func (cfg *NodeConfig) NewPrivateKey() { + _, spriv, err := ed25519.GenerateKey(nil) if err != nil { panic(err) } - cfg.PublicKey = hex.EncodeToString(spub[:]) - cfg.PrivateKey = hex.EncodeToString(spriv[:]) + cfg.PrivateKey = KeyBytes(spriv) +} + +func (cfg *NodeConfig) MarshalPEMPrivateKey() ([]byte, error) { + b, err := x509.MarshalPKCS8PrivateKey(ed25519.PrivateKey(cfg.PrivateKey)) + if err != nil { + return nil, fmt.Errorf("failed to marshal PKCS8 key: %w", err) + } + block := &pem.Block{ + Type: "PRIVATE KEY", + Bytes: b, + } + return pem.EncodeToMemory(block), nil +} + +func (cfg *NodeConfig) UnmarshalPEMPrivateKey(b []byte) error { + p, _ := pem.Decode(b) + if p == nil { + return fmt.Errorf("failed to parse PEM file") + } + if p.Type != "PRIVATE KEY" { + return fmt.Errorf("unexpected PEM type %q", p.Type) + } + k, err := x509.ParsePKCS8PrivateKey(p.Bytes) + if err != nil { + return fmt.Errorf("failed to unmarshal PKCS8 key: %w", err) + } + key, ok := k.(ed25519.PrivateKey) + if !ok { + return fmt.Errorf("private key must be ed25519 key") + } + if len(key) != ed25519.PrivateKeySize { + return fmt.Errorf("unexpected ed25519 private key length") + } + cfg.PrivateKey = KeyBytes(key) + return nil +} + +type KeyBytes []byte + +func (k KeyBytes) MarshalJSON() ([]byte, error) { + return json.Marshal(hex.EncodeToString(k)) +} + +func (k *KeyBytes) UnmarshalJSON(b []byte) error { + var s string + var err error + if err = json.Unmarshal(b, &s); err != nil { + return err + } + *k, err = hex.DecodeString(s) + return err } diff --git a/src/config/config_test.go b/src/config/config_test.go index 8b6e14e1..6b74b50f 100644 --- a/src/config/config_test.go +++ b/src/config/config_test.go @@ -1,54 +1,54 @@ package config import ( - "bytes" - "encoding/hex" "testing" ) func TestConfig_Keys(t *testing.T) { - var nodeConfig NodeConfig - nodeConfig.NewKeys() + /* + var nodeConfig NodeConfig + nodeConfig.NewKeys() - publicKey1, err := hex.DecodeString(nodeConfig.PublicKey) + publicKey1, err := hex.DecodeString(nodeConfig.PublicKey) - if err != nil { - t.Fatal("can not decode generated public key") - } + if err != nil { + t.Fatal("can not decode generated public key") + } - if len(publicKey1) == 0 { - t.Fatal("empty public key generated") - } + if len(publicKey1) == 0 { + t.Fatal("empty public key generated") + } - privateKey1, err := hex.DecodeString(nodeConfig.PrivateKey) + privateKey1, err := hex.DecodeString(nodeConfig.PrivateKey) - if err != nil { - t.Fatal("can not decode generated private key") - } + if err != nil { + t.Fatal("can not decode generated private key") + } - if len(privateKey1) == 0 { - t.Fatal("empty private key generated") - } + if len(privateKey1) == 0 { + t.Fatal("empty private key generated") + } - nodeConfig.NewKeys() + nodeConfig.NewKeys() - publicKey2, err := hex.DecodeString(nodeConfig.PublicKey) + publicKey2, err := hex.DecodeString(nodeConfig.PublicKey) - if err != nil { - t.Fatal("can not decode generated public key") - } + if err != nil { + t.Fatal("can not decode generated public key") + } - if bytes.Equal(publicKey2, publicKey1) { - t.Fatal("same public key generated") - } + if bytes.Equal(publicKey2, publicKey1) { + t.Fatal("same public key generated") + } - privateKey2, err := hex.DecodeString(nodeConfig.PrivateKey) + privateKey2, err := hex.DecodeString(nodeConfig.PrivateKey) - if err != nil { - t.Fatal("can not decode generated private key") - } + if err != nil { + t.Fatal("can not decode generated private key") + } - if bytes.Equal(privateKey2, privateKey1) { - t.Fatal("same private key generated") - } + if bytes.Equal(privateKey2, privateKey1) { + t.Fatal("same private key generated") + } + */ } diff --git a/src/config/defaults.go b/src/config/defaults.go new file mode 100644 index 00000000..aaccfd0c --- /dev/null +++ b/src/config/defaults.go @@ -0,0 +1,34 @@ +package config + +var defaultConfig = "" // LDFLAGS='-X github.com/yggdrasil-network/yggdrasil-go/src/config.defaultConfig=/path/to/config +var defaultAdminListen = "" // LDFLAGS='-X github.com/yggdrasil-network/yggdrasil-go/src/config.defaultAdminListen=unix://path/to/sock' + +// Defines which parameters are expected by default for configuration on a +// specific platform. These values are populated in the relevant defaults_*.go +// for the platform being targeted. They must be set. +type platformDefaultParameters struct { + // Admin socket + DefaultAdminListen string + + // Configuration (used for yggdrasilctl) + DefaultConfigFile string + + // Multicast interfaces + DefaultMulticastInterfaces []MulticastInterfaceConfig + + // TUN + MaximumIfMTU uint64 + DefaultIfMTU uint64 + DefaultIfName string +} + +func GetDefaults() platformDefaultParameters { + defaults := getDefaults() + if defaultConfig != "" { + defaults.DefaultConfigFile = defaultConfig + } + if defaultAdminListen != "" { + defaults.DefaultAdminListen = defaultAdminListen + } + return defaults +} diff --git a/src/defaults/defaults_darwin.go b/src/config/defaults_darwin.go similarity index 97% rename from src/defaults/defaults_darwin.go rename to src/config/defaults_darwin.go index 3ffd9ffb..e851d6b9 100644 --- a/src/defaults/defaults_darwin.go +++ b/src/config/defaults_darwin.go @@ -1,7 +1,7 @@ //go:build darwin // +build darwin -package defaults +package config // Sane defaults for the macOS/Darwin platform. The "default" options may be // may be replaced by the running configuration. diff --git a/src/defaults/defaults_freebsd.go b/src/config/defaults_freebsd.go similarity index 97% rename from src/defaults/defaults_freebsd.go rename to src/config/defaults_freebsd.go index 43359389..97f7b4c3 100644 --- a/src/defaults/defaults_freebsd.go +++ b/src/config/defaults_freebsd.go @@ -1,7 +1,7 @@ //go:build freebsd // +build freebsd -package defaults +package config // Sane defaults for the BSD platforms. The "default" options may be // may be replaced by the running configuration. diff --git a/src/defaults/defaults_linux.go b/src/config/defaults_linux.go similarity index 97% rename from src/defaults/defaults_linux.go rename to src/config/defaults_linux.go index bad6233e..6f7cbfc3 100644 --- a/src/defaults/defaults_linux.go +++ b/src/config/defaults_linux.go @@ -1,7 +1,7 @@ //go:build linux // +build linux -package defaults +package config // Sane defaults for the Linux platform. The "default" options may be // may be replaced by the running configuration. diff --git a/src/defaults/defaults_openbsd.go b/src/config/defaults_openbsd.go similarity index 97% rename from src/defaults/defaults_openbsd.go rename to src/config/defaults_openbsd.go index e5d1fa9a..81ddf7e8 100644 --- a/src/defaults/defaults_openbsd.go +++ b/src/config/defaults_openbsd.go @@ -1,7 +1,7 @@ //go:build openbsd // +build openbsd -package defaults +package config // Sane defaults for the BSD platforms. The "default" options may be // may be replaced by the running configuration. diff --git a/src/defaults/defaults_other.go b/src/config/defaults_other.go similarity index 97% rename from src/defaults/defaults_other.go rename to src/config/defaults_other.go index bb22864e..8299364c 100644 --- a/src/defaults/defaults_other.go +++ b/src/config/defaults_other.go @@ -1,7 +1,7 @@ //go:build !linux && !darwin && !windows && !openbsd && !freebsd // +build !linux,!darwin,!windows,!openbsd,!freebsd -package defaults +package config // Sane defaults for the other platforms. The "default" options may be // may be replaced by the running configuration. diff --git a/src/defaults/defaults_windows.go b/src/config/defaults_windows.go similarity index 97% rename from src/defaults/defaults_windows.go rename to src/config/defaults_windows.go index e2601bf7..5b30b4fd 100644 --- a/src/defaults/defaults_windows.go +++ b/src/config/defaults_windows.go @@ -1,7 +1,7 @@ //go:build windows // +build windows -package defaults +package config // Sane defaults for the Windows platform. The "default" options may be // may be replaced by the running configuration. diff --git a/src/core/api.go b/src/core/api.go index feece23c..8f448965 100644 --- a/src/core/api.go +++ b/src/core/api.go @@ -6,9 +6,9 @@ import ( "fmt" "net" "net/url" - "sync/atomic" "time" + "github.com/Arceliar/ironwood/network" "github.com/Arceliar/phony" "github.com/yggdrasil-network/yggdrasil-go/src/address" ) @@ -19,15 +19,19 @@ type SelfInfo struct { } type PeerInfo struct { - Key ed25519.PublicKey - Root ed25519.PublicKey - Coords []uint64 - Port uint64 - Priority uint8 - Remote string - RXBytes uint64 - TXBytes uint64 - Uptime time.Duration + URI string + Up bool + Inbound bool + LastError error + LastErrorTime time.Time + Key ed25519.PublicKey + Root ed25519.PublicKey + Coords []uint64 + Port uint64 + Priority uint8 + RXBytes uint64 + TXBytes uint64 + Uptime time.Duration } type TreeEntryInfo struct { @@ -61,35 +65,39 @@ func (c *Core) GetSelf() SelfInfo { func (c *Core) GetPeers() []PeerInfo { peers := []PeerInfo{} - names := make(map[net.Conn]string) + conns := map[net.Conn]network.DebugPeerInfo{} + iwpeers := c.PacketConn.PacketConn.Debug.GetPeers() + for _, p := range iwpeers { + conns[p.Conn] = p + } + phony.Block(&c.links, func() { - for _, info := range c.links._links { - if info == nil { - continue + for info, state := range c.links._links { + var peerinfo PeerInfo + var conn net.Conn + phony.Block(state, func() { + peerinfo.URI = info.uri + peerinfo.LastError = state._err + peerinfo.LastErrorTime = state._errtime + if c := state._conn; c != nil { + conn = c + peerinfo.Up = true + peerinfo.Inbound = info.linkType == linkTypeIncoming + peerinfo.RXBytes = c.rx + peerinfo.TXBytes = c.tx + peerinfo.Uptime = time.Since(c.up) + } + }) + if p, ok := conns[conn]; ok { + peerinfo.Key = p.Key + peerinfo.Root = p.Root + peerinfo.Port = p.Port + peerinfo.Priority = p.Priority } - names[info.conn] = info.lname + peers = append(peers, peerinfo) } }) - ps := c.PacketConn.PacketConn.Debug.GetPeers() - for _, p := range ps { - var info PeerInfo - info.Key = p.Key - info.Root = p.Root - info.Port = p.Port - info.Priority = p.Priority - if p.Conn != nil { - info.Remote = p.Conn.RemoteAddr().String() - if linkconn, ok := p.Conn.(*linkConn); ok { - info.RXBytes = atomic.LoadUint64(&linkconn.rx) - info.TXBytes = atomic.LoadUint64(&linkconn.tx) - info.Uptime = time.Since(linkconn.up) - } - if name := names[p.Conn]; name != "" { - info.Remote = name - } - } - peers = append(peers, info) - } + return peers } @@ -139,16 +147,7 @@ func (c *Core) GetSessions() []SessionInfo { // parsed from a string of the form e.g. "tcp://a.b.c.d:e". In the case of a // link-local address, the interface should be provided as the second argument. func (c *Core) Listen(u *url.URL, sintf string) (*Listener, error) { - switch u.Scheme { - case "tcp": - return c.links.tcp.listen(u, sintf) - case "tls": - return c.links.tls.listen(u, sintf) - case "unix": - return c.links.unix.listen(u, sintf) - default: - return nil, fmt.Errorf("unrecognised scheme %q", u.Scheme) - } + return c.links.listen(u, sintf) } // Address gets the IPv6 address of the Yggdrasil node. This is always a /128 @@ -187,49 +186,34 @@ func (c *Core) SetLogger(log Logger) { // // This adds the peer to the peer list, so that they will be called again if the // connection drops. -func (c *Core) AddPeer(uri string, sourceInterface string) error { - var known bool - phony.Block(c, func() { - _, known = c.config._peers[Peer{uri, sourceInterface}] - }) - if known { - return fmt.Errorf("peer already configured") - } - u, err := url.Parse(uri) - if err != nil { - return err - } - info, err := c.links.call(u, sourceInterface, nil) - if err != nil { - return err - } - phony.Block(c, func() { - c.config._peers[Peer{uri, sourceInterface}] = &info - }) - return nil +func (c *Core) AddPeer(u *url.URL, sintf string) error { + return c.links.add(u, sintf, linkTypePersistent) } // RemovePeer removes a peer. The peer should be specified in URI format, see AddPeer. // The peer is not disconnected immediately. func (c *Core) RemovePeer(uri string, sourceInterface string) error { - var err error - phony.Block(c, func() { - peer := Peer{uri, sourceInterface} - linkInfo, ok := c.config._peers[peer] - if !ok { - err = fmt.Errorf("peer not configured") - return - } - if ok && linkInfo != nil { - c.links.Act(nil, func() { - if link := c.links._links[*linkInfo]; link != nil { - _ = link.close() - } - }) - } - delete(c.config._peers, peer) - }) - return err + return fmt.Errorf("not implemented yet") + /* + var err error + phony.Block(c, func() { + peer := Peer{uri, sourceInterface} + linkInfo, ok := c.config._peers[peer] + if !ok { + err = fmt.Errorf("peer not configured") + return + } + if ok && linkInfo != nil { + c.links.Act(nil, func() { + if link := c.links._links[*linkInfo]; link != nil { + _ = link.conn.Close() + } + }) + } + delete(c.config._peers, peer) + }) + return err + */ } // CallPeer calls a peer once. This should be specified in the peer URI format, @@ -241,8 +225,7 @@ func (c *Core) RemovePeer(uri string, sourceInterface string) error { // This does not add the peer to the peer list, so if the connection drops, the // peer will not be called again automatically. func (c *Core) CallPeer(u *url.URL, sintf string) error { - _, err := c.links.call(u, sintf, nil) - return err + return c.links.add(u, sintf, linkTypeEphemeral) } func (c *Core) PublicKey() ed25519.PublicKey { diff --git a/src/core/core.go b/src/core/core.go index 9243be0f..39142add 100644 --- a/src/core/core.go +++ b/src/core/core.go @@ -3,6 +3,9 @@ package core import ( "context" "crypto/ed25519" + "crypto/tls" + "crypto/x509" + "encoding/hex" "fmt" "io" "net" @@ -10,12 +13,10 @@ import ( "time" iwe "github.com/Arceliar/ironwood/encrypted" - iwn "github.com/Arceliar/ironwood/network" iwt "github.com/Arceliar/ironwood/types" "github.com/Arceliar/phony" "github.com/gologme/log" - "github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/version" ) @@ -36,7 +37,9 @@ type Core struct { log Logger addPeerTimer *time.Timer config struct { - _peers map[Peer]*linkInfo // configurable after startup + tls *tls.Config // immutable after startup + roots *x509.CertPool // immutable after startup + //_peers map[Peer]*linkInfo // configurable after startup _listeners map[ListenAddress]struct{} // configurable after startup nodeinfo NodeInfo // immutable after startup nodeinfoPrivacy NodeInfoPrivacy // immutable after startup @@ -45,48 +48,76 @@ type Core struct { pathNotify func(ed25519.PublicKey) } -func New(secret ed25519.PrivateKey, logger Logger, opts ...SetupOption) (*Core, error) { +func New(cert *tls.Certificate, logger Logger, opts ...SetupOption) (*Core, error) { c := &Core{ log: logger, } + c.ctx, c.cancel = context.WithCancel(context.Background()) + if c.log == nil { + c.log = log.New(io.Discard, "", 0) + } + if name := version.BuildName(); name != "unknown" { c.log.Infoln("Build name:", name) } if version := version.BuildVersion(); version != "unknown" { c.log.Infoln("Build version:", version) } - c.ctx, c.cancel = context.WithCancel(context.Background()) - // Take a copy of the private key so that it is in our own memory space. - if len(secret) != ed25519.PrivateKeySize { - return nil, fmt.Errorf("private key is incorrect length") - } - c.secret = make(ed25519.PrivateKey, ed25519.PrivateKeySize) - copy(c.secret, secret) - c.public = secret.Public().(ed25519.PublicKey) + var err error - keyXform := func(key ed25519.PublicKey) ed25519.PublicKey { - return address.SubnetForKey(key).GetKey() - } - if c.PacketConn, err = iwe.NewPacketConn(c.secret, - iwn.WithBloomTransform(keyXform), - iwn.WithPeerMaxMessageSize(65535*2), - iwn.WithPathNotify(c.doPathNotify), - ); err != nil { - return nil, fmt.Errorf("error creating encryption: %w", err) - } - c.config._peers = map[Peer]*linkInfo{} c.config._listeners = map[ListenAddress]struct{}{} c.config._allowedPublicKeys = map[[32]byte]struct{}{} for _, opt := range opts { - c._applyOption(opt) + switch opt.(type) { + case Peer, ListenAddress: + // We can't do peers yet as the links aren't set up. + continue + default: + if err = c._applyOption(opt); err != nil { + return nil, fmt.Errorf("failed to apply configuration option %T: %w", opt, err) + } + } } - if c.log == nil { - c.log = log.New(io.Discard, "", 0) + if cert == nil || cert.PrivateKey == nil { + return nil, fmt.Errorf("no private key supplied") + } + var ok bool + if c.secret, ok = cert.PrivateKey.(ed25519.PrivateKey); !ok { + return nil, fmt.Errorf("private key must be ed25519") + } + if len(c.secret) != ed25519.PrivateKeySize { + return nil, fmt.Errorf("private key is incorrect length") + } + c.public = c.secret.Public().(ed25519.PublicKey) + + if c.config.tls, err = c.generateTLSConfig(cert); err != nil { + return nil, fmt.Errorf("error generating TLS config: %w", err) + } + if c.PacketConn, err = iwe.NewPacketConn(c.secret); err != nil { + return nil, fmt.Errorf("error creating encryption: %w", err) + } + address, subnet := c.Address(), c.Subnet() + c.log.Infof("Your public key is %s", hex.EncodeToString(c.public)) + c.log.Infof("Your IPv6 address is %s", address.String()) + c.log.Infof("Your IPv6 subnet is %s", subnet.String()) + if c.config.roots != nil { + c.log.Println("Yggdrasil is running in TLS-only mode") } c.proto.init(c) if err := c.links.init(c); err != nil { return nil, fmt.Errorf("error initialising links: %w", err) } + for _, opt := range opts { + switch opt.(type) { + case Peer, ListenAddress: + // Now do the peers and listeners. + if err = c._applyOption(opt); err != nil { + return nil, fmt.Errorf("failed to apply configuration option %T: %w", opt, err) + } + default: + continue + } + } if err := c.proto.nodeinfo.setNodeInfo(c.config.nodeinfo, bool(c.config.nodeinfoPrivacy)); err != nil { return nil, fmt.Errorf("error setting node info: %w", err) } @@ -100,42 +131,11 @@ func New(secret ed25519.PrivateKey, logger Logger, opts ...SetupOption) (*Core, c.log.Errorf("Failed to start listener %q: %s\n", listenaddr, err) } } - c.Act(nil, c._addPeerLoop) return c, 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() { - select { - case <-c.ctx.Done(): - return - default: - } - // Add peers from the Peers section - for peer := range c.config._peers { - go func(peer string, intf string) { - u, err := url.Parse(peer) - if err != nil { - c.log.Errorln("Failed to parse peer url:", peer, err) - } - if err := c.CallPeer(u, intf); err != nil { - c.log.Errorln("Failed to add peer:", err) - } - }(peer.URI, peer.SourceInterface) // TODO: this should be acted and not in a goroutine? - } - - c.addPeerTimer = time.AfterFunc(time.Minute, func() { - c.Act(nil, c._addPeerLoop) - }) -} - func (c *Core) RetryPeersNow() { - if c.addPeerTimer != nil && !c.addPeerTimer.Stop() { - <-c.addPeerTimer.C - } - c.Act(nil, c._addPeerLoop) + // TODO: figure out a way to retrigger peer connections. } // Stop shuts down the Yggdrasil node. @@ -159,6 +159,10 @@ func (c *Core) _close() error { return err } +func (c *Core) isTLSOnly() bool { + return c.config.roots != nil +} + func (c *Core) MTU() uint64 { const sessionTypeOverhead = 1 MTU := c.PacketConn.MTU() - sessionTypeOverhead @@ -213,14 +217,6 @@ func (c *Core) WriteTo(p []byte, addr net.Addr) (n int, err error) { return } -func (c *Core) doPathNotify(key ed25519.PublicKey) { - c.Act(nil, func() { - if c.pathNotify != nil { - c.pathNotify(key) - } - }) -} - func (c *Core) SetPathNotify(notify func(ed25519.PublicKey)) { c.Act(nil, func() { c.pathNotify = notify diff --git a/src/core/core_test.go b/src/core/core_test.go index ce28f4e5..49901324 100644 --- a/src/core/core_test.go +++ b/src/core/core_test.go @@ -2,7 +2,6 @@ package core import ( "bytes" - "crypto/ed25519" "math/rand" "net/url" "os" @@ -10,6 +9,7 @@ import ( "time" "github.com/gologme/log" + "github.com/yggdrasil-network/yggdrasil-go/src/config" ) // GetLoggerWithPrefix creates a new logger instance with prefix. @@ -29,18 +29,21 @@ func GetLoggerWithPrefix(prefix string, verbose bool) *log.Logger { // Verbosity flag is passed to logger. func CreateAndConnectTwo(t testing.TB, verbose bool) (nodeA *Core, nodeB *Core) { var err error - var skA, skB ed25519.PrivateKey - if _, skA, err = ed25519.GenerateKey(nil); err != nil { + + cfgA, cfgB := config.GenerateConfig(), config.GenerateConfig() + if err = cfgA.GenerateSelfSignedCertificate(); err != nil { t.Fatal(err) } - if _, skB, err = ed25519.GenerateKey(nil); err != nil { + if err = cfgB.GenerateSelfSignedCertificate(); err != nil { t.Fatal(err) } + logger := GetLoggerWithPrefix("", false) - if nodeA, err = New(skA, logger, ListenAddress("tcp://127.0.0.1:0")); err != nil { + + if nodeA, err = New(cfgA.Certificate, logger, ListenAddress("tcp://127.0.0.1:0")); err != nil { t.Fatal(err) } - if nodeB, err = New(skB, logger, ListenAddress("tcp://127.0.0.1:0")); err != nil { + if nodeB, err = New(cfgB.Certificate, logger, ListenAddress("tcp://127.0.0.1:0")); err != nil { t.Fatal(err) } @@ -76,7 +79,7 @@ func WaitConnected(nodeA, nodeB *Core) bool { } */ if len(nodeA.GetTree()) > 1 && len(nodeB.GetTree()) > 1 { - time.Sleep(3*time.Second) // FIXME hack, there's still stuff happening internally + time.Sleep(3 * time.Second) // FIXME hack, there's still stuff happening internally return true } } diff --git a/src/core/link.go b/src/core/link.go index 06776618..ed225904 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -2,10 +2,12 @@ package core import ( "bytes" + "context" "encoding/hex" "errors" "fmt" "io" + "math" "net" "net/url" "strconv" @@ -17,6 +19,14 @@ import ( "github.com/yggdrasil-network/yggdrasil-go/src/address" ) +type linkType int + +const ( + linkTypePersistent linkType = iota // Statically configured + linkTypeEphemeral // Multicast discovered + linkTypeIncoming // Incoming connection +) + type links struct { phony.Inbox core *Core @@ -27,41 +37,52 @@ type links struct { _links map[linkInfo]*link // *link is nil if connection in progress } +type linkProtocol interface { + dial(url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) + listen(ctx context.Context, url *url.URL, sintf string) (net.Listener, error) +} + // linkInfo is used as a map key type linkInfo struct { - linkType string // Type of link, e.g. TCP, AWDL - local string // Local name or address - remote string // Remote name or address -} - -type linkDial struct { - url *url.URL - sintf string + uri string // Peering URI in complete form + sintf string // Peering source interface (i.e. from InterfacePeers) + linkType linkType // Type of link, i.e. outbound/inbound, persistent/ephemeral } +// link tracks the state of a connection, either persistent or non-persistent type link struct { - lname string - links *links - conn *linkConn - options linkOptions - info linkInfo - incoming bool - force bool + phony.Inbox + ctx context.Context // + cancel context.CancelFunc // + kick chan struct{} // Attempt to reconnect now, if backing off + info linkInfo // + linkProto string // Protocol carrier of link, e.g. TCP, AWDL + _conn *linkConn // Connected link, if any, nil if not connected + _err error // Last error on the connection, if any + _errtime time.Time // Last time an error occured + } type linkOptions struct { pinnedEd25519Keys map[keyArray]struct{} priority uint8 + tlsSNI string } type Listener struct { - net.Listener - closed chan struct{} + listener net.Listener + ctx context.Context + Cancel context.CancelFunc +} + +func (l *Listener) Addr() net.Addr { + return l.listener.Addr() } func (l *Listener) Close() error { - err := l.Listener.Close() - <-l.closed + l.Cancel() + err := l.listener.Close() + <-l.ctx.Done() return err } @@ -105,195 +126,294 @@ func (l *links) shutdown() { func (l *links) isConnectedTo(info linkInfo) bool { var isConnected bool phony.Block(l, func() { - _, isConnected = l._links[info] + link, ok := l._links[info] + if !ok { + return + } + isConnected = link._conn != nil }) return isConnected } -func (l *links) call(u *url.URL, sintf string, errch chan<- error) (info linkInfo, err error) { - info = linkInfoFor(u.Scheme, sintf, u.Host) - if l.isConnectedTo(info) { - if errch != nil { - close(errch) // already connected, no error +type linkError string + +func (e linkError) Error() string { return string(e) } + +const ErrLinkAlreadyConfigured = linkError("peer is already configured") +const ErrLinkPriorityInvalid = linkError("priority value is invalid") +const ErrLinkPinnedKeyInvalid = linkError("pinned public key is invalid") +const ErrLinkUnrecognisedSchema = linkError("link schema unknown") + +func (l *links) add(u *url.URL, sintf string, linkType linkType) error { + // Generate the link info and see whether we think we already + // have an open peering to this peer. + info := linkInfo{ + uri: u.String(), + sintf: sintf, + linkType: linkType, + } + if state, ok := l._links[info]; ok { + select { + case state.kick <- struct{}{}: + default: } - return info, nil + return ErrLinkAlreadyConfigured } - options := linkOptions{ - pinnedEd25519Keys: map[keyArray]struct{}{}, + + // Create the link entry. This will contain the connection + // in progress (if any), any error details and a context that + // lets the link be cancelled later. + ctx, cancel := context.WithCancel(l.core.ctx) + state := &link{ + info: info, + linkProto: strings.ToUpper(u.Scheme), + ctx: ctx, + cancel: cancel, } + + // Collect together the link options, these are global options + // that are not specific to any given protocol. + var options linkOptions for _, pubkey := range u.Query()["key"] { sigPub, err := hex.DecodeString(pubkey) if err != nil { - if errch != nil { - close(errch) - } - return info, fmt.Errorf("pinned key contains invalid hex characters") + return ErrLinkPinnedKeyInvalid } var sigPubKey keyArray copy(sigPubKey[:], sigPub) + if options.pinnedEd25519Keys == nil { + options.pinnedEd25519Keys = map[keyArray]struct{}{} + } options.pinnedEd25519Keys[sigPubKey] = struct{}{} } if p := u.Query().Get("priority"); p != "" { pi, err := strconv.ParseUint(p, 10, 8) if err != nil { - if errch != nil { - close(errch) - } - return info, fmt.Errorf("priority invalid: %w", err) + return ErrLinkPriorityInvalid } options.priority = uint8(pi) } - switch info.linkType { - case "tcp": - go func() { - if errch != nil { - defer close(errch) - } - if err := l.tcp.dial(u, options, sintf); err != nil && err != io.EOF { - l.core.log.Warnf("Failed to dial TCP %s: %s\n", u.Host, err) - if errch != nil { - errch <- err - } - } - }() - case "socks": - go func() { - if errch != nil { - defer close(errch) - } - if err := l.socks.dial(u, options); err != nil && err != io.EOF { - l.core.log.Warnf("Failed to dial SOCKS %s: %s\n", u.Host, err) - if errch != nil { - errch <- err - } - } - }() + // Store the state of the link, try to connect and then run + // the handler. + phony.Block(l, func() { + l._links[info] = state + }) - case "tls": - // SNI headers must contain hostnames and not IP addresses, so we must make sure - // that we do not populate the SNI with an IP literal. We do this by splitting - // the host-port combo from the query option and then seeing if it parses to an - // IP address successfully or not. - var tlsSNI string - if sni := u.Query().Get("sni"); sni != "" { - if net.ParseIP(sni) == nil { - tlsSNI = sni - } + // Track how many consecutive connection failures we have had, + // as we will back off exponentially rather than hammering the + // remote node endlessly. + var backoff int + + // backoffNow is called when there's a connection error. It + // will wait for the specified amount of time and then return + // true, unless the peering context was cancelled (due to a + // peer removal most likely), in which case it returns false. + // The caller should check the return value to decide whether + // or not to give up trying. + backoffNow := func() bool { + backoff++ + duration := time.Second * time.Duration(math.Exp2(float64(backoff))) + select { + case <-time.After(duration): + return true + case <-state.kick: + return true + case <-ctx.Done(): + return false } - // If the SNI is not configured still because the above failed then we'll try - // again but this time we'll use the host part of the peering URI instead. - if tlsSNI == "" { - if host, _, err := net.SplitHostPort(u.Host); err == nil && net.ParseIP(host) == nil { - tlsSNI = host - } - } - go func() { - if errch != nil { - defer close(errch) - } - if err := l.tls.dial(u, options, sintf, tlsSNI); err != nil && err != io.EOF { - l.core.log.Warnf("Failed to dial TLS %s: %s\n", u.Host, err) - if errch != nil { - errch <- err - } - } - }() - - case "unix": - go func() { - if errch != nil { - defer close(errch) - } - if err := l.unix.dial(u, options, sintf); err != nil && err != io.EOF { - l.core.log.Warnf("Failed to dial UNIX %s: %s\n", u.Host, err) - if errch != nil { - errch <- err - } - } - }() - - default: - if errch != nil { - close(errch) - } - return info, errors.New("unknown call scheme: " + u.Scheme) } - return info, nil -} -func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { - var listener *Listener - var err error - switch u.Scheme { - case "tcp": - listener, err = l.tcp.listen(u, sintf) - case "tls": - listener, err = l.tls.listen(u, sintf) - case "unix": - listener, err = l.unix.listen(u, sintf) - default: - return nil, fmt.Errorf("unrecognised scheme %q", u.Scheme) - } - return listener, err -} - -func (l *links) create(conn net.Conn, dial *linkDial, name string, info linkInfo, incoming, force bool, options linkOptions) error { - intf := link{ - conn: &linkConn{ - Conn: conn, - up: time.Now(), - }, - lname: name, - links: l, - options: options, - info: info, - incoming: incoming, - force: force, - } + // The goroutine is responsible for attempting the connection + // and then running the handler. If the connection is persistent + // then the loop will run endlessly, using backoffs as needed. + // Otherwise the loop will end, cleaning up the link entry. go func() { - if err := intf.handler(dial); err != nil { - l.core.log.Errorf("Link handler %s error (%s): %s", name, conn.RemoteAddr(), err) + defer phony.Block(l, func() { + delete(l._links, info) + }) + for { + conn, err := l.connect(u, info, options) + if err != nil { + if linkType == linkTypePersistent { + phony.Block(state, func() { + state._err = err + state._errtime = time.Now() + }) + if backoffNow() { + continue + } else { + return + } + } else { + break + } + } + lc := &linkConn{ + Conn: conn, + up: time.Now(), + } + phony.Block(state, func() { + state._conn = lc + state._err = nil + state._errtime = time.Time{} + }) + if err = l.handler(&info, options, lc); err != nil && err != io.EOF { + l.core.log.Debugf("Link %s error: %s\n", info.uri, err) + } else { + backoff = 0 + } + _ = conn.Close() + phony.Block(state, func() { + state._conn = nil + if state._err = err; state._err != nil { + state._errtime = time.Now() + } + }) + if linkType == linkTypePersistent { + if backoffNow() { + continue + } else { + return + } + } else { + break + } } }() return nil } -func (intf *link) handler(dial *linkDial) error { - defer intf.conn.Close() // nolint:errcheck - - // Don't connect to this link more than once. - if intf.links.isConnectedTo(intf.info) { - return nil +func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { + ctx, cancel := context.WithCancel(l.core.ctx) + var protocol linkProtocol + switch strings.ToLower(u.Scheme) { + case "tcp": + protocol = l.tcp + case "tls": + protocol = l.tls + case "unix": + protocol = l.unix + default: + cancel() + return nil, ErrLinkUnrecognisedSchema } + listener, err := protocol.listen(ctx, u, sintf) + if err != nil { + cancel() + return nil, err + } + li := &Listener{ + listener: listener, + ctx: ctx, + Cancel: cancel, + } + go func() { + l.core.log.Printf("%s listener started on %s", strings.ToUpper(u.Scheme), listener.Addr()) + defer l.core.log.Printf("%s listener stopped on %s", strings.ToUpper(u.Scheme), listener.Addr()) + for { + conn, err := listener.Accept() + if err != nil { + continue + } + pu := *u + pu.Host = conn.RemoteAddr().String() + info := linkInfo{ + uri: pu.String(), + sintf: sintf, + linkType: linkTypeIncoming, + } + if l.isConnectedTo(info) { + _ = conn.Close() + continue + } + state := l._links[info] + if state == nil { + state = &link{ + info: info, + } + } + lc := &linkConn{ + Conn: conn, + up: time.Now(), + } + var options linkOptions + phony.Block(state, func() { + state._conn = lc + state._err = nil + state.linkProto = strings.ToUpper(u.Scheme) + }) + phony.Block(l, func() { + l._links[info] = state + }) + if err = l.handler(&info, options, lc); err != nil && err != io.EOF { + l.core.log.Debugf("Link %s error: %s\n", u.Host, err) + } + phony.Block(state, func() { + state._conn = nil + if state._err = err; state._err != nil { + state._errtime = time.Now() + } + }) + phony.Block(l, func() { + delete(l._links, info) + }) + } + }() + return li, nil +} - // Mark the connection as in progress. - phony.Block(intf.links, func() { - intf.links._links[intf.info] = nil - }) - - // When we're done, clean up the connection entry. - defer phony.Block(intf.links, func() { - delete(intf.links._links, intf.info) - }) +func (l *links) connect(u *url.URL, info linkInfo, options linkOptions) (net.Conn, error) { + var dialer linkProtocol + switch strings.ToLower(u.Scheme) { + case "tcp": + dialer = l.tcp + case "tls": + // SNI headers must contain hostnames and not IP addresses, so we must make sure + // that we do not populate the SNI with an IP literal. We do this by splitting + // the host-port combo from the query option and then seeing if it parses to an + // IP address successfully or not. + if sni := u.Query().Get("sni"); sni != "" { + if net.ParseIP(sni) == nil { + options.tlsSNI = sni + } + } + // If the SNI is not configured still because the above failed then we'll try + // again but this time we'll use the host part of the peering URI instead. + if options.tlsSNI == "" { + if host, _, err := net.SplitHostPort(u.Host); err == nil && net.ParseIP(host) == nil { + options.tlsSNI = host + } + } + dialer = l.tls + case "socks": + dialer = l.socks + case "unix": + dialer = l.unix + default: + return nil, ErrLinkUnrecognisedSchema + } + return dialer.dial(u, info, options) +} +func (l *links) handler(info *linkInfo, options linkOptions, conn net.Conn) error { meta := version_getBaseMetadata() - meta.publicKey = intf.links.core.public + meta.publicKey = l.core.public metaBytes := meta.encode() - if err := intf.conn.SetDeadline(time.Now().Add(time.Second * 6)); err != nil { + if err := conn.SetDeadline(time.Now().Add(time.Second * 6)); err != nil { return fmt.Errorf("failed to set handshake deadline: %w", err) } - n, err := intf.conn.Write(metaBytes) + n, err := conn.Write(metaBytes) switch { case err != nil: return fmt.Errorf("write handshake: %w", err) case err == nil && n != len(metaBytes): return fmt.Errorf("incomplete handshake send") } - if _, err = io.ReadFull(intf.conn, metaBytes); err != nil { + if _, err = io.ReadFull(conn, metaBytes); err != nil { return fmt.Errorf("read handshake: %w", err) } - if err = intf.conn.SetDeadline(time.Time{}); err != nil { + if err = conn.SetDeadline(time.Time{}); err != nil { return fmt.Errorf("failed to clear handshake deadline: %w", err) } meta = version_metadata{} @@ -302,23 +422,14 @@ func (intf *link) handler(dial *linkDial) error { return errors.New("failed to decode metadata") } if !meta.check() { - var connectError string - if intf.incoming { - connectError = "Rejected incoming connection" - } else { - connectError = "Failed to connect" - } - intf.links.core.log.Debugf("%s: %s is incompatible version (local %s, remote %s)", - connectError, - intf.lname, + return fmt.Errorf("remote node incompatible version (local %s, remote %s)", fmt.Sprintf("%d.%d", base.majorVer, base.minorVer), fmt.Sprintf("%d.%d", meta.majorVer, meta.minorVer), ) - return errors.New("remote node is incompatible version") } // Check if the remote side matches the keys we expected. This is a bit of a weak // check - in future versions we really should check a signature or something like that. - if pinned := intf.options.pinnedEd25519Keys; len(pinned) > 0 { + if pinned := options.pinnedEd25519Keys; len(pinned) > 0 { var key keyArray copy(key[:], meta.publicKey) if _, allowed := pinned[key]; !allowed { @@ -326,7 +437,10 @@ func (intf *link) handler(dial *linkDial) error { } } // Check if we're authorized to connect to this key / IP - allowed := intf.links.core.config._allowedPublicKeys + var allowed map[[32]byte]struct{} + phony.Block(l.core, func() { + allowed = l.core.config._allowedPublicKeys + }) isallowed := len(allowed) == 0 for k := range allowed { if bytes.Equal(k[:], meta.publicKey) { @@ -334,73 +448,32 @@ func (intf *link) handler(dial *linkDial) error { break } } - if intf.incoming && !intf.force && !isallowed { - _ = intf.close() + if info.linkType == linkTypeIncoming && !isallowed { return fmt.Errorf("node public key %q is not in AllowedPublicKeys", hex.EncodeToString(meta.publicKey)) } - phony.Block(intf.links, func() { - intf.links._links[intf.info] = intf - }) - dir := "outbound" - if intf.incoming { + if info.linkType == linkTypeIncoming { dir = "inbound" } remoteAddr := net.IP(address.AddrForKey(meta.publicKey)[:]).String() - remoteStr := fmt.Sprintf("%s@%s", remoteAddr, intf.info.remote) - localStr := intf.conn.LocalAddr() - intf.links.core.log.Infof("Connected %s %s: %s, source %s", - dir, strings.ToUpper(intf.info.linkType), remoteStr, localStr) + remoteStr := fmt.Sprintf("%s@%s", remoteAddr, conn.RemoteAddr()) + localStr := conn.LocalAddr() + l.core.log.Infof("Connected %s: %s, source %s", + dir, remoteStr, localStr) - err = intf.links.core.HandleConn(meta.publicKey, intf.conn, intf.options.priority) + err = l.core.HandleConn(meta.publicKey, conn, options.priority) switch err { case io.EOF, net.ErrClosed, nil: - intf.links.core.log.Infof("Disconnected %s %s: %s, source %s", - dir, strings.ToUpper(intf.info.linkType), remoteStr, localStr) + l.core.log.Infof("Disconnected %s: %s, source %s", + dir, remoteStr, localStr) default: - intf.links.core.log.Infof("Disconnected %s %s: %s, source %s; error: %s", - dir, strings.ToUpper(intf.info.linkType), remoteStr, localStr, err) + l.core.log.Infof("Disconnected %s: %s, source %s; error: %s", + dir, remoteStr, localStr, err) } - - if !intf.incoming && dial != nil { - // The connection was one that we dialled, so wait a second and try to - // dial it again. - var retry func(attempt int) - retry = func(attempt int) { - // intf.links.core.log.Infof("Retrying %s (attempt %d of 5)...", dial.url.String(), attempt) - errch := make(chan error, 1) - if _, err := intf.links.call(dial.url, dial.sintf, errch); err != nil { - return - } - if err := <-errch; err != nil { - if attempt < 3 { - time.AfterFunc(time.Second, func() { - retry(attempt + 1) - }) - } - } - } - time.AfterFunc(time.Second, func() { - retry(1) - }) - } - return nil } -func (intf *link) close() error { - return intf.conn.Close() -} - -func linkInfoFor(linkType, sintf, remote string) linkInfo { - return linkInfo{ - linkType: linkType, - local: sintf, - remote: remote, - } -} - type linkConn struct { // tx and rx are at the beginning of the struct to ensure 64-bit alignment // on 32-bit platforms, see https://pkg.go.dev/sync/atomic#pkg-note-BUG @@ -421,12 +494,3 @@ func (c *linkConn) Write(p []byte) (n int, err error) { atomic.AddUint64(&c.tx, uint64(n)) return } - -func linkOptionsForListener(u *url.URL) (l linkOptions) { - if p := u.Query().Get("priority"); p != "" { - if pi, err := strconv.ParseUint(p, 10, 8); err == nil { - l.priority = uint8(pi) - } - } - return -} diff --git a/src/core/link_socks.go b/src/core/link_socks.go index 4cdffa51..538394e5 100644 --- a/src/core/link_socks.go +++ b/src/core/link_socks.go @@ -1,6 +1,7 @@ package core import ( + "context" "fmt" "net" "net/url" @@ -20,37 +21,22 @@ func (l *links) newLinkSOCKS() *linkSOCKS { return lt } -func (l *linkSOCKS) dial(url *url.URL, options linkOptions) error { - info := linkInfoFor("socks", "", url.Path) - if l.links.isConnectedTo(info) { - return nil +func (l *linkSOCKS) dial(url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) { + var proxyAuth *proxy.Auth + if url.User != nil && url.User.Username() != "" { + proxyAuth = &proxy.Auth{ + User: url.User.Username(), + } + proxyAuth.Password, _ = url.User.Password() } - proxyAuth := &proxy.Auth{} - proxyAuth.User = url.User.Username() - proxyAuth.Password, _ = url.User.Password() dialer, err := proxy.SOCKS5("tcp", url.Host, proxyAuth, proxy.Direct) if err != nil { - return fmt.Errorf("failed to configure proxy") + return nil, fmt.Errorf("failed to configure proxy") } pathtokens := strings.Split(strings.Trim(url.Path, "/"), "/") - conn, err := dialer.Dial("tcp", pathtokens[0]) - if err != nil { - return err - } - dial := &linkDial{ - url: url, - } - return l.handler(dial, info, conn, options, false) + return dialer.Dial("tcp", pathtokens[0]) } -func (l *linkSOCKS) handler(dial *linkDial, info linkInfo, conn net.Conn, options linkOptions, incoming bool) error { - return l.links.create( - conn, // connection - dial, // connection URL - dial.url.String(), // connection name - info, // connection info - incoming, // not incoming - false, // not forced - options, // connection options - ) +func (l *linkSOCKS) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) { + return nil, fmt.Errorf("SOCKS listener not supported") } diff --git a/src/core/link_tcp.go b/src/core/link_tcp.go index 60054d45..e2e51c35 100644 --- a/src/core/link_tcp.go +++ b/src/core/link_tcp.go @@ -6,7 +6,6 @@ import ( "net" "net/url" "strconv" - "strings" "time" "github.com/Arceliar/phony" @@ -15,19 +14,19 @@ import ( type linkTCP struct { phony.Inbox *links - listener *net.ListenConfig - _listeners map[*Listener]context.CancelFunc + listenconfig *net.ListenConfig + _listeners map[*Listener]context.CancelFunc } func (l *links) newLinkTCP() *linkTCP { lt := &linkTCP{ links: l, - listener: &net.ListenConfig{ + listenconfig: &net.ListenConfig{ KeepAlive: -1, }, _listeners: map[*Listener]context.CancelFunc{}, } - lt.listener.Control = lt.tcpContext + lt.listenconfig.Control = lt.tcpContext return lt } @@ -37,7 +36,7 @@ type tcpDialer struct { addr *net.TCPAddr } -func (l *linkTCP) dialersFor(url *url.URL, options linkOptions, sintf string) ([]*tcpDialer, error) { +func (l *linkTCP) dialersFor(url *url.URL, info linkInfo) ([]*tcpDialer, error) { host, p, err := net.SplitHostPort(url.Host) if err != nil { return nil, err @@ -56,14 +55,10 @@ func (l *linkTCP) dialersFor(url *url.URL, options linkOptions, sintf string) ([ IP: ip, Port: port, } - dialer, err := l.dialerFor(addr, sintf) + dialer, err := l.dialerFor(addr, info.sintf) if err != nil { continue } - info := linkInfoFor("tcp", sintf, tcpIDFor(dialer.LocalAddr, addr)) - if l.links.isConnectedTo(info) { - return nil, nil - } dialers = append(dialers, &tcpDialer{ info: info, dialer: dialer, @@ -73,13 +68,16 @@ func (l *linkTCP) dialersFor(url *url.URL, options linkOptions, sintf string) ([ return dialers, nil } -func (l *linkTCP) dial(url *url.URL, options linkOptions, sintf string) error { - dialers, err := l.dialersFor(url, options, sintf) +func (l *linkTCP) dial(url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) { + if l.core.isTLSOnly() { + return nil, fmt.Errorf("TCP peer prohibited in TLS-only mode") + } + dialers, err := l.dialersFor(url, info) if err != nil { - return err + return nil, err } if len(dialers) == 0 { - return nil + return nil, nil } for _, d := range dialers { var conn net.Conn @@ -88,72 +86,22 @@ func (l *linkTCP) dial(url *url.URL, options linkOptions, sintf string) error { l.core.log.Warnf("Failed to connect to %s: %s", d.addr, err) continue } - name := strings.TrimRight(strings.SplitN(url.String(), "?", 2)[0], "/") - dial := &linkDial{ - url: url, - sintf: sintf, - } - return l.handler(dial, name, d.info, conn, options, false, false) + return conn, nil } - return fmt.Errorf("failed to connect via %d address(es), last error: %w", len(dialers), err) + return nil, err } -func (l *linkTCP) listen(url *url.URL, sintf string) (*Listener, error) { - ctx, cancel := context.WithCancel(l.core.ctx) +func (l *linkTCP) listen(ctx context.Context, url *url.URL, sintf string) (net.Listener, error) { + if l.core.isTLSOnly() { + return nil, fmt.Errorf("TCP listener prohibited in TLS-only mode") + } hostport := url.Host if sintf != "" { if host, port, err := net.SplitHostPort(hostport); err == nil { hostport = fmt.Sprintf("[%s%%%s]:%s", host, sintf, port) } } - listener, err := l.listener.Listen(ctx, "tcp", hostport) - if err != nil { - cancel() - return nil, err - } - entry := &Listener{ - Listener: listener, - closed: make(chan struct{}), - } - phony.Block(l, func() { - l._listeners[entry] = cancel - }) - l.core.log.Printf("TCP listener started on %s", listener.Addr()) - go func() { - defer phony.Block(l, func() { - delete(l._listeners, entry) - }) - for { - conn, err := listener.Accept() - if err != nil { - cancel() - break - } - laddr := conn.LocalAddr().(*net.TCPAddr) - raddr := conn.RemoteAddr().(*net.TCPAddr) - name := fmt.Sprintf("tcp://%s", raddr) - info := linkInfoFor("tcp", sintf, tcpIDFor(laddr, raddr)) - if err = l.handler(nil, name, info, conn, linkOptionsForListener(url), true, raddr.IP.IsLinkLocalUnicast()); err != nil { - l.core.log.Errorln("Failed to create inbound link:", err) - } - } - _ = listener.Close() - close(entry.closed) - l.core.log.Printf("TCP listener stopped on %s", listener.Addr()) - }() - return entry, nil -} - -func (l *linkTCP) handler(dial *linkDial, name string, info linkInfo, conn net.Conn, options linkOptions, incoming, force bool) error { - return l.links.create( - conn, // connection - dial, // connection URL - name, // connection name - info, // connection info - incoming, // not incoming - force, // not forced - options, // connection options - ) + return l.listenconfig.Listen(ctx, "tcp", hostport) } // Returns the address of the listener. @@ -163,8 +111,8 @@ func (l *linkTCP) getAddr() *net.TCPAddr { // doesn't have the ability to send more than one address in a packet either var addr *net.TCPAddr phony.Block(l, func() { - for listener := range l._listeners { - addr = listener.Addr().(*net.TCPAddr) + for li := range l._listeners { + addr = li.listener.Addr().(*net.TCPAddr) } }) return addr @@ -228,16 +176,3 @@ func (l *linkTCP) dialerFor(dst *net.TCPAddr, sintf string) (*net.Dialer, error) } return dialer, nil } - -func tcpIDFor(local net.Addr, remoteAddr *net.TCPAddr) string { - if localAddr, ok := local.(*net.TCPAddr); ok && localAddr.IP.Equal(remoteAddr.IP) { - // Nodes running on the same host — include both the IP and port. - return remoteAddr.String() - } - if remoteAddr.IP.IsLinkLocalUnicast() { - // Nodes discovered via multicast — include the IP only. - return remoteAddr.IP.String() - } - // Nodes connected remotely — include both the IP and port. - return remoteAddr.String() -} diff --git a/src/core/link_tls.go b/src/core/link_tls.go index 6323a723..b962d529 100644 --- a/src/core/link_tls.go +++ b/src/core/link_tls.go @@ -1,20 +1,11 @@ package core import ( - "bytes" "context" - "crypto/rand" "crypto/tls" - "crypto/x509" - "crypto/x509/pkix" - "encoding/hex" - "encoding/pem" "fmt" - "math/big" "net" "net/url" - "strings" - "time" "github.com/Arceliar/phony" ) @@ -36,27 +27,23 @@ func (l *links) newLinkTLS(tcp *linkTCP) *linkTLS { Control: tcp.tcpContext, KeepAlive: -1, }, + config: l.core.config.tls.Clone(), _listeners: map[*Listener]context.CancelFunc{}, } - var err error - lt.config, err = lt.generateConfig() - if err != nil { - panic(err) - } return lt } -func (l *linkTLS) dial(url *url.URL, options linkOptions, sintf, sni string) error { - dialers, err := l.tcp.dialersFor(url, options, sintf) +func (l *linkTLS) dial(url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) { + dialers, err := l.tcp.dialersFor(url, info) if err != nil { - return err + return nil, err } if len(dialers) == 0 { - return nil + return nil, nil } for _, d := range dialers { tlsconfig := l.config.Clone() - tlsconfig.ServerName = sni + tlsconfig.ServerName = options.tlsSNI tlsdialer := &tls.Dialer{ NetDialer: d.dialer, Config: tlsconfig, @@ -66,18 +53,12 @@ func (l *linkTLS) dial(url *url.URL, options linkOptions, sintf, sni string) err if err != nil { continue } - name := strings.TrimRight(strings.SplitN(url.String(), "?", 2)[0], "/") - dial := &linkDial{ - url: url, - sintf: sintf, - } - return l.handler(dial, name, d.info, conn, options, false, false) + return conn, nil } - return fmt.Errorf("failed to connect via %d address(es), last error: %w", len(dialers), err) + return nil, err } -func (l *linkTLS) listen(url *url.URL, sintf string) (*Listener, error) { - ctx, cancel := context.WithCancel(l.core.ctx) +func (l *linkTLS) listen(ctx context.Context, url *url.URL, sintf string) (net.Listener, error) { hostport := url.Host if sintf != "" { if host, port, err := net.SplitHostPort(hostport); err == nil { @@ -86,88 +67,8 @@ func (l *linkTLS) listen(url *url.URL, sintf string) (*Listener, error) { } listener, err := l.listener.Listen(ctx, "tcp", hostport) if err != nil { - cancel() return nil, err } tlslistener := tls.NewListener(listener, l.config) - entry := &Listener{ - Listener: tlslistener, - closed: make(chan struct{}), - } - phony.Block(l, func() { - l._listeners[entry] = cancel - }) - l.core.log.Printf("TLS listener started on %s", listener.Addr()) - go func() { - defer phony.Block(l, func() { - delete(l._listeners, entry) - }) - for { - conn, err := tlslistener.Accept() - if err != nil { - cancel() - break - } - laddr := conn.LocalAddr().(*net.TCPAddr) - raddr := conn.RemoteAddr().(*net.TCPAddr) - name := fmt.Sprintf("tls://%s", raddr) - info := linkInfoFor("tls", sintf, tcpIDFor(laddr, raddr)) - if err = l.handler(nil, name, info, conn, linkOptionsForListener(url), true, raddr.IP.IsLinkLocalUnicast()); err != nil { - l.core.log.Errorln("Failed to create inbound link:", err) - } - } - _ = tlslistener.Close() - close(entry.closed) - l.core.log.Printf("TLS listener stopped on %s", listener.Addr()) - }() - return entry, nil -} - -// RFC5280 section 4.1.2.5 -var notAfterNeverExpires = time.Date(9999, time.December, 31, 23, 59, 59, 0, time.UTC) - -func (l *linkTLS) generateConfig() (*tls.Config, error) { - certBuf := &bytes.Buffer{} - cert := x509.Certificate{ - SerialNumber: big.NewInt(1), - Subject: pkix.Name{ - CommonName: hex.EncodeToString(l.links.core.public[:]), - }, - NotBefore: time.Now(), - NotAfter: notAfterNeverExpires, - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, - BasicConstraintsValid: true, - } - - certbytes, err := x509.CreateCertificate(rand.Reader, &cert, &cert, l.links.core.public, l.links.core.secret) - if err != nil { - return nil, err - } - - if err := pem.Encode(certBuf, &pem.Block{ - Type: "CERTIFICATE", - Bytes: certbytes, - }); err != nil { - return nil, err - } - - rootCAs := x509.NewCertPool() - rootCAs.AppendCertsFromPEM(certbytes) - - return &tls.Config{ - RootCAs: rootCAs, - Certificates: []tls.Certificate{ - { - Certificate: [][]byte{certbytes}, - PrivateKey: l.links.core.secret, - }, - }, - InsecureSkipVerify: true, - MinVersion: tls.VersionTLS13, - }, nil -} - -func (l *linkTLS) handler(dial *linkDial, name string, info linkInfo, conn net.Conn, options linkOptions, incoming, force bool) error { - return l.tcp.handler(dial, name, info, conn, options, incoming, force) + return tlslistener, nil } diff --git a/src/core/link_unix.go b/src/core/link_unix.go index 7f782575..501689ad 100644 --- a/src/core/link_unix.go +++ b/src/core/link_unix.go @@ -32,70 +32,14 @@ func (l *links) newLinkUNIX() *linkUNIX { return lt } -func (l *linkUNIX) dial(url *url.URL, options linkOptions, _ string) error { - info := linkInfoFor("unix", "", url.Path) - if l.links.isConnectedTo(info) { - return nil - } +func (l *linkUNIX) dial(url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) { addr, err := net.ResolveUnixAddr("unix", url.Path) if err != nil { - return err - } - conn, err := l.dialer.DialContext(l.core.ctx, "unix", addr.String()) - if err != nil { - return err - } - dial := &linkDial{ - url: url, - } - return l.handler(dial, url.String(), info, conn, options, false) -} - -func (l *linkUNIX) listen(url *url.URL, _ string) (*Listener, error) { - ctx, cancel := context.WithCancel(l.core.ctx) - listener, err := l.listener.Listen(ctx, "unix", url.Path) - if err != nil { - cancel() return nil, err } - entry := &Listener{ - Listener: listener, - closed: make(chan struct{}), - } - phony.Block(l, func() { - l._listeners[entry] = cancel - }) - l.core.log.Printf("UNIX listener started on %s", listener.Addr()) - go func() { - defer phony.Block(l, func() { - delete(l._listeners, entry) - }) - for { - conn, err := listener.Accept() - if err != nil { - cancel() - break - } - info := linkInfoFor("unix", "", url.String()) - if err = l.handler(nil, url.String(), info, conn, linkOptionsForListener(url), true); err != nil { - l.core.log.Errorln("Failed to create inbound link:", err) - } - } - _ = listener.Close() - close(entry.closed) - l.core.log.Printf("UNIX listener stopped on %s", listener.Addr()) - }() - return entry, nil + return l.dialer.DialContext(l.core.ctx, "unix", addr.String()) } -func (l *linkUNIX) handler(dial *linkDial, name string, info linkInfo, conn net.Conn, options linkOptions, incoming bool) error { - return l.links.create( - conn, // connection - dial, // connection URL - name, // connection name - info, // connection info - incoming, // not incoming - false, // not forced - options, // connection options - ) +func (l *linkUNIX) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) { + return l.listener.Listen(ctx, "unix", url.Path) } diff --git a/src/core/options.go b/src/core/options.go index 66aa16ce..e294896c 100644 --- a/src/core/options.go +++ b/src/core/options.go @@ -2,12 +2,25 @@ package core import ( "crypto/ed25519" + "crypto/x509" + "fmt" + "net/url" ) -func (c *Core) _applyOption(opt SetupOption) { +func (c *Core) _applyOption(opt SetupOption) (err error) { switch v := opt.(type) { + case RootCertificate: + cert := x509.Certificate(v) + if c.config.roots == nil { + c.config.roots = x509.NewCertPool() + } + c.config.roots.AddCert(&cert) case Peer: - c.config._peers[v] = nil + u, err := url.Parse(v.URI) + if err != nil { + return fmt.Errorf("unable to parse peering URI: %w", err) + } + return c.links.add(u, v.SourceInterface, linkTypePersistent) case ListenAddress: c.config._listeners[v] = struct{}{} case NodeInfo: @@ -19,12 +32,14 @@ func (c *Core) _applyOption(opt SetupOption) { copy(pk[:], v) c.config._allowedPublicKeys[pk] = struct{}{} } + return } type SetupOption interface { isSetupOption() } +type RootCertificate x509.Certificate type ListenAddress string type Peer struct { URI string @@ -34,6 +49,7 @@ type NodeInfo map[string]interface{} type NodeInfoPrivacy bool type AllowedPublicKey ed25519.PublicKey +func (a RootCertificate) isSetupOption() {} func (a ListenAddress) isSetupOption() {} func (a Peer) isSetupOption() {} func (a NodeInfo) isSetupOption() {} diff --git a/src/core/tls.go b/src/core/tls.go new file mode 100644 index 00000000..4a55ea35 --- /dev/null +++ b/src/core/tls.go @@ -0,0 +1,63 @@ +package core + +import ( + "crypto/tls" + "crypto/x509" + "fmt" +) + +func (c *Core) generateTLSConfig(cert *tls.Certificate) (*tls.Config, error) { + config := &tls.Config{ + Certificates: []tls.Certificate{*cert}, + ClientAuth: tls.RequireAnyClientCert, + GetClientCertificate: func(cri *tls.CertificateRequestInfo) (*tls.Certificate, error) { + return cert, nil + }, + VerifyPeerCertificate: c.verifyTLSCertificate, + VerifyConnection: c.verifyTLSConnection, + InsecureSkipVerify: true, + MinVersion: tls.VersionTLS13, + } + return config, nil +} + +func (c *Core) verifyTLSCertificate(rawCerts [][]byte, _ [][]*x509.Certificate) error { + if c.config.roots == nil { + // If there's no certificate pool configured then we will + // accept all TLS certificates. + return nil + } + if len(rawCerts) == 0 { + return fmt.Errorf("expected at least one certificate") + } + + opts := x509.VerifyOptions{ + Roots: c.config.roots, + } + + for i, rawCert := range rawCerts { + if i == 0 { + // The first certificate is the leaf certificate. All other + // certificates in the list are intermediates, so add them + // into the VerifyOptions. + continue + } + cert, err := x509.ParseCertificate(rawCert) + if err != nil { + return fmt.Errorf("failed to parse intermediate certificate: %w", err) + } + opts.Intermediates.AddCert(cert) + } + + cert, err := x509.ParseCertificate(rawCerts[0]) + if err != nil { + return fmt.Errorf("failed to parse leaf certificate: %w", err) + } + + _, err = cert.Verify(opts) + return err +} + +func (c *Core) verifyTLSConnection(cs tls.ConnectionState) error { + return nil +} diff --git a/src/defaults/defaults.go b/src/defaults/defaults.go deleted file mode 100644 index 6374f4ed..00000000 --- a/src/defaults/defaults.go +++ /dev/null @@ -1,60 +0,0 @@ -package defaults - -import "github.com/yggdrasil-network/yggdrasil-go/src/config" - -type MulticastInterfaceConfig = config.MulticastInterfaceConfig - -var defaultConfig = "" // LDFLAGS='-X github.com/yggdrasil-network/yggdrasil-go/src/defaults.defaultConfig=/path/to/config -var defaultAdminListen = "" // LDFLAGS='-X github.com/yggdrasil-network/yggdrasil-go/src/defaults.defaultAdminListen=unix://path/to/sock' - -// Defines which parameters are expected by default for configuration on a -// specific platform. These values are populated in the relevant defaults_*.go -// for the platform being targeted. They must be set. -type platformDefaultParameters struct { - // Admin socket - DefaultAdminListen string - - // Configuration (used for yggdrasilctl) - DefaultConfigFile string - - // Multicast interfaces - DefaultMulticastInterfaces []MulticastInterfaceConfig - - // TUN - MaximumIfMTU uint64 - DefaultIfMTU uint64 - DefaultIfName string -} - -func GetDefaults() platformDefaultParameters { - defaults := getDefaults() - if defaultConfig != "" { - defaults.DefaultConfigFile = defaultConfig - } - if defaultAdminListen != "" { - defaults.DefaultAdminListen = defaultAdminListen - } - return defaults -} - -// Generates default configuration and returns a pointer to the resulting -// NodeConfig. This is used when outputting the -genconf parameter and also when -// using -autoconf. -func GenerateConfig() *config.NodeConfig { - // Get the defaults for the platform. - defaults := GetDefaults() - // Create a node configuration and populate it. - cfg := new(config.NodeConfig) - cfg.NewKeys() - cfg.Listen = []string{} - cfg.AdminListen = defaults.DefaultAdminListen - cfg.Peers = []string{} - cfg.InterfacePeers = map[string][]string{} - cfg.AllowedPublicKeys = []string{} - cfg.MulticastInterfaces = defaults.DefaultMulticastInterfaces - cfg.IfName = defaults.DefaultIfName - cfg.IfMTU = defaults.DefaultIfMTU - cfg.NodeInfoPrivacy = false - - return cfg -} diff --git a/src/multicast/advertisement.go b/src/multicast/advertisement.go new file mode 100644 index 00000000..4b65b609 --- /dev/null +++ b/src/multicast/advertisement.go @@ -0,0 +1,28 @@ +package multicast + +import ( + "crypto/ed25519" + "encoding/binary" + "fmt" +) + +type multicastAdvertisement struct { + PublicKey ed25519.PublicKey + Port uint16 +} + +func (m *multicastAdvertisement) MarshalBinary() ([]byte, error) { + b := make([]byte, 0, ed25519.PublicKeySize+2) + b = append(b, m.PublicKey...) + b = binary.BigEndian.AppendUint16(b, m.Port) + return b, nil +} + +func (m *multicastAdvertisement) UnmarshalBinary(b []byte) error { + if len(b) < ed25519.PublicKeySize+2 { + return fmt.Errorf("invalid multicast beacon") + } + m.PublicKey = b[:ed25519.PublicKeySize] + m.Port = binary.BigEndian.Uint16(b[ed25519.PublicKeySize:]) + return nil +} diff --git a/src/multicast/multicast.go b/src/multicast/multicast.go index ec145230..e6fdb80a 100644 --- a/src/multicast/multicast.go +++ b/src/multicast/multicast.go @@ -1,10 +1,7 @@ package multicast import ( - "bytes" "context" - "crypto/ed25519" - "encoding/binary" "encoding/hex" "fmt" "net" @@ -248,7 +245,7 @@ func (m *Multicast) _announce() { // It's possible that the link-local listener address has changed so if // that is the case then we should clean up the interface listener found := false - listenaddr, err := net.ResolveTCPAddr("tcp6", info.listener.Listener.Addr().String()) + listenaddr, err := net.ResolveTCPAddr("tcp6", info.listener.Addr().String()) if err != nil { stop() continue @@ -295,7 +292,7 @@ func (m *Multicast) _announce() { } // Try and see if we already have a TCP listener for this interface var linfo *listenerInfo - if nfo, ok := m._listeners[iface.Name]; !ok || nfo.listener.Listener == nil { + if _, ok := m._listeners[iface.Name]; !ok { // No listener was found - let's create one urlString := fmt.Sprintf("tls://[%s]:%d", addrIP, info.port) u, err := url.Parse(urlString) @@ -321,17 +318,18 @@ func (m *Multicast) _announce() { if time.Since(linfo.time) < linfo.interval { continue } - // Get the listener details and construct the multicast beacon - lladdr := linfo.listener.Listener.Addr().String() - if a, err := net.ResolveTCPAddr("tcp6", lladdr); err == nil { - a.Zone = "" - destAddr.Zone = iface.Name - msg := append([]byte(nil), m.core.GetSelf().Key...) - msg = append(msg, a.IP...) - pbs := make([]byte, 2) - binary.BigEndian.PutUint16(pbs, uint16(a.Port)) - msg = append(msg, pbs...) - _, _ = m.sock.WriteTo(msg, nil, destAddr) + addr := linfo.listener.Addr().(*net.TCPAddr) + adv := multicastAdvertisement{ + PublicKey: m.core.PublicKey(), + Port: uint16(addr.Port), + } + msg, err := adv.MarshalBinary() + if err != nil { + continue + } + destAddr.Zone = iface.Name + if _, err = m.sock.WriteTo(msg, nil, destAddr); err != nil { + m.log.Warn("Failed to send multicast beacon:", err) } if linfo.interval.Seconds() < 15 { linfo.interval += time.Second @@ -351,7 +349,7 @@ func (m *Multicast) listen() { } bs := make([]byte, 2048) for { - nBytes, rcm, fromAddr, err := m.sock.ReadFrom(bs) + n, rcm, fromAddr, err := m.sock.ReadFrom(bs) if err != nil { if !m.IsStarted() { return @@ -369,40 +367,27 @@ func (m *Multicast) listen() { continue } } - if nBytes < ed25519.PublicKeySize { + var adv multicastAdvertisement + if err := adv.UnmarshalBinary(bs[:n]); err != nil { continue } - var key ed25519.PublicKey - key = append(key, bs[:ed25519.PublicKeySize]...) - if bytes.Equal(key, m.core.GetSelf().Key) { - continue // don't bother trying to peer with self - } - begin := ed25519.PublicKeySize - end := nBytes - 2 - if end <= begin { - continue // malformed address - } - ip := bs[begin:end] - port := binary.BigEndian.Uint16(bs[end:nBytes]) - anAddr := net.TCPAddr{IP: ip, Port: int(port)} - addr, err := net.ResolveTCPAddr("tcp6", anAddr.String()) - if err != nil { + if adv.PublicKey.Equal(m.core.PublicKey()) { continue } from := fromAddr.(*net.UDPAddr) - if !from.IP.Equal(addr.IP) { - continue - } + from.Port = int(adv.Port) var interfaces map[string]*interfaceInfo phony.Block(m, func() { interfaces = m._interfaces }) if info, ok := interfaces[from.Zone]; ok && info.listen { - addr.Zone = "" - pin := fmt.Sprintf("/?key=%s&priority=%d", hex.EncodeToString(key), info.priority) - u, err := url.Parse("tls://" + addr.String() + pin) - if err != nil { - m.log.Debugln("Call from multicast failed, parse error:", addr.String(), err) + v := &url.Values{} + v.Add("key", hex.EncodeToString(adv.PublicKey)) + v.Add("priority", fmt.Sprintf("%d", info.priority)) + u := &url.URL{ + Scheme: "tls", + Host: from.String(), + RawQuery: v.Encode(), } if err := m.core.CallPeer(u, from.Zone); err != nil { m.log.Debugln("Call from multicast failed:", err) diff --git a/src/tun/tun.go b/src/tun/tun.go index fcd597b8..93cb433f 100644 --- a/src/tun/tun.go +++ b/src/tun/tun.go @@ -14,8 +14,8 @@ import ( "golang.zx2c4.com/wireguard/tun" "github.com/yggdrasil-network/yggdrasil-go/src/address" + "github.com/yggdrasil-network/yggdrasil-go/src/config" "github.com/yggdrasil-network/yggdrasil-go/src/core" - "github.com/yggdrasil-network/yggdrasil-go/src/defaults" "github.com/yggdrasil-network/yggdrasil-go/src/ipv6rwc" ) @@ -43,7 +43,7 @@ type TunAdapter struct { } // Gets the maximum supported MTU for the platform based on the defaults in -// defaults.GetDefaults(). +// config.GetDefaults(). func getSupportedMTU(mtu uint64) uint64 { if mtu < 1280 { return 1280 @@ -72,20 +72,20 @@ func (tun *TunAdapter) MTU() uint64 { // DefaultName gets the default TUN interface name for your platform. func DefaultName() string { - return defaults.GetDefaults().DefaultIfName + return config.GetDefaults().DefaultIfName } // DefaultMTU gets the default TUN interface MTU for your platform. This can // be as high as MaximumMTU(), depending on platform, but is never lower than 1280. func DefaultMTU() uint64 { - return defaults.GetDefaults().DefaultIfMTU + return config.GetDefaults().DefaultIfMTU } // MaximumMTU returns the maximum supported TUN interface MTU for your // platform. This can be as high as 65535, depending on platform, but is never // lower than 1280. func MaximumMTU() uint64 { - return defaults.GetDefaults().MaximumIfMTU + return config.GetDefaults().MaximumIfMTU } // Init initialises the TUN module. You must have acquired a Listener from From a9ec3877b5ff4619031a6995b3ac8eff436081a2 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 14 May 2023 15:59:52 +0100 Subject: [PATCH 108/293] Fix unit test --- src/core/core_test.go | 16 ++++++++++++---- src/core/link_tcp.go | 14 -------------- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/src/core/core_test.go b/src/core/core_test.go index 49901324..c38a750a 100644 --- a/src/core/core_test.go +++ b/src/core/core_test.go @@ -39,22 +39,30 @@ func CreateAndConnectTwo(t testing.TB, verbose bool) (nodeA *Core, nodeB *Core) } logger := GetLoggerWithPrefix("", false) + logger.EnableLevel("debug") - if nodeA, err = New(cfgA.Certificate, logger, ListenAddress("tcp://127.0.0.1:0")); err != nil { + if nodeA, err = New(cfgA.Certificate, logger); err != nil { t.Fatal(err) } - if nodeB, err = New(cfgB.Certificate, logger, ListenAddress("tcp://127.0.0.1:0")); err != nil { + if nodeB, err = New(cfgB.Certificate, logger); err != nil { t.Fatal(err) } - u, err := url.Parse("tcp://" + nodeA.links.tcp.getAddr().String()) + nodeAListenURL, err := url.Parse("tcp://localhost:0") if err != nil { t.Fatal(err) } - err = nodeB.CallPeer(u, "") + nodeAListener, err := nodeA.Listen(nodeAListenURL, "") if err != nil { t.Fatal(err) } + nodeAURL, err := url.Parse("tcp://" + nodeAListener.Addr().String()) + if err != nil { + t.Fatal(err) + } + if err = nodeB.CallPeer(nodeAURL, ""); err != nil { + t.Fatal(err) + } time.Sleep(100 * time.Millisecond) diff --git a/src/core/link_tcp.go b/src/core/link_tcp.go index e2e51c35..8ffb3124 100644 --- a/src/core/link_tcp.go +++ b/src/core/link_tcp.go @@ -104,20 +104,6 @@ func (l *linkTCP) listen(ctx context.Context, url *url.URL, sintf string) (net.L return l.listenconfig.Listen(ctx, "tcp", hostport) } -// Returns the address of the listener. -func (l *linkTCP) getAddr() *net.TCPAddr { - // TODO: Fix this, because this will currently only give a single address - // to multicast.go, which obviously is not great, but right now multicast.go - // doesn't have the ability to send more than one address in a packet either - var addr *net.TCPAddr - phony.Block(l, func() { - for li := range l._listeners { - addr = li.listener.Addr().(*net.TCPAddr) - } - }) - return addr -} - func (l *linkTCP) dialerFor(dst *net.TCPAddr, sintf string) (*net.Dialer, error) { if dst.IP.IsLinkLocalUnicast() { if sintf != "" { From 7b1635245f67d0717b8b638865392f562a9d38ce Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Fri, 19 May 2023 19:33:40 +0100 Subject: [PATCH 109/293] Add missing path notify and bloom transform --- src/core/core.go | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/core/core.go b/src/core/core.go index 39142add..748b7381 100644 --- a/src/core/core.go +++ b/src/core/core.go @@ -13,10 +13,12 @@ import ( "time" iwe "github.com/Arceliar/ironwood/encrypted" + iwn "github.com/Arceliar/ironwood/network" iwt "github.com/Arceliar/ironwood/types" "github.com/Arceliar/phony" "github.com/gologme/log" + "github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/version" ) @@ -93,7 +95,15 @@ func New(cert *tls.Certificate, logger Logger, opts ...SetupOption) (*Core, erro if c.config.tls, err = c.generateTLSConfig(cert); err != nil { return nil, fmt.Errorf("error generating TLS config: %w", err) } - if c.PacketConn, err = iwe.NewPacketConn(c.secret); err != nil { + keyXform := func(key ed25519.PublicKey) ed25519.PublicKey { + return address.SubnetForKey(key).GetKey() + } + if c.PacketConn, err = iwe.NewPacketConn( + c.secret, + iwn.WithBloomTransform(keyXform), + iwn.WithPeerMaxMessageSize(65535*2), + iwn.WithPathNotify(c.doPathNotify), + ); err != nil { return nil, fmt.Errorf("error creating encryption: %w", err) } address, subnet := c.Address(), c.Subnet() @@ -217,6 +227,14 @@ func (c *Core) WriteTo(p []byte, addr net.Addr) (n int, err error) { return } +func (c *Core) doPathNotify(key ed25519.PublicKey) { + c.Act(nil, func() { + if c.pathNotify != nil { + c.pathNotify(key) + } + }) +} + func (c *Core) SetPathNotify(notify func(ed25519.PublicKey)) { c.Act(nil, func() { c.pathNotify = notify From 6ac2fae845a628857a96e7c3715ec76a21822ac2 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Fri, 19 May 2023 20:34:51 +0100 Subject: [PATCH 110/293] Fix Windows build --- src/tun/tun_windows.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tun/tun_windows.go b/src/tun/tun_windows.go index c3e36596..1a8aa1f3 100644 --- a/src/tun/tun_windows.go +++ b/src/tun/tun_windows.go @@ -9,7 +9,7 @@ import ( "log" "net" - "github.com/yggdrasil-network/yggdrasil-go/src/defaults" + "github.com/yggdrasil-network/yggdrasil-go/src/config" "golang.org/x/sys/windows" wgtun "golang.zx2c4.com/wireguard/tun" @@ -22,7 +22,7 @@ import ( // Configures the TUN adapter with the correct IPv6 address and MTU. func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error { if ifname == "auto" { - ifname = defaults.GetDefaults().DefaultIfName + ifname = config.GetDefaults().DefaultIfName } return elevate.DoAsSystem(func() error { var err error From a233e775eb54852f311ff7cd40a1375f758e13d6 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Fri, 19 May 2023 20:57:14 +0100 Subject: [PATCH 111/293] `yggdrasilctl` tweaks --- cmd/yggdrasilctl/cmd_line_env.go | 1 - cmd/yggdrasilctl/main.go | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/cmd/yggdrasilctl/cmd_line_env.go b/cmd/yggdrasilctl/cmd_line_env.go index be24a558..d96d6950 100644 --- a/cmd/yggdrasilctl/cmd_line_env.go +++ b/cmd/yggdrasilctl/cmd_line_env.go @@ -38,7 +38,6 @@ func (cmdLineEnv *CmdLineEnv) parseFlagsAndArgs() { fmt.Println("Examples:") fmt.Println(" - ", os.Args[0], "list") fmt.Println(" - ", os.Args[0], "getPeers") - fmt.Println(" - ", os.Args[0], "-v getSelf") fmt.Println(" - ", os.Args[0], "setTunTap name=auto mtu=1500 tap_mode=false") fmt.Println(" - ", os.Args[0], "-endpoint=tcp://localhost:9001 getDHT") fmt.Println(" - ", os.Args[0], "-endpoint=unix:///var/run/ygg.sock getDHT") diff --git a/cmd/yggdrasilctl/main.go b/cmd/yggdrasilctl/main.go index 884fc052..8f99e0d2 100644 --- a/cmd/yggdrasilctl/main.go +++ b/cmd/yggdrasilctl/main.go @@ -176,9 +176,9 @@ func run() int { } table.SetHeader([]string{"URI", "State", "Dir", "IP Address", "Uptime", "RX", "TX", "Pr", "Last Error"}) for _, peer := range resp.Peers { - state, lasterr, dir := "Up", "(none)", "Out" + state, lasterr, dir := "Up", "-", "Out" if !peer.Up { - state, lasterr = "Down", fmt.Sprintf("%s (%s ago)", peer.LastError, peer.LastErrorTime.Round(time.Second)) + state, lasterr = "Down", fmt.Sprintf("%s ago: %s", peer.LastErrorTime.Round(time.Second), peer.LastError) } if peer.Inbound { dir = "In" From e290e744f4c8d729a8086854d16ecf16deeb7978 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 20 May 2023 10:54:49 +0100 Subject: [PATCH 112/293] Fix `-autoconf` --- src/config/config.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/config/config.go b/src/config/config.go index 76f7476b..1980cb21 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -84,7 +84,9 @@ func GenerateConfig() *NodeConfig { cfg.IfName = defaults.DefaultIfName cfg.IfMTU = defaults.DefaultIfMTU cfg.NodeInfoPrivacy = false - + if err := cfg.postprocessConfig(); err != nil { + panic(err) + } return cfg } @@ -122,6 +124,10 @@ func (cfg *NodeConfig) UnmarshalHJSON(b []byte) error { if err := hjson.Unmarshal(b, cfg); err != nil { return err } + return cfg.postprocessConfig() +} + +func (cfg *NodeConfig) postprocessConfig() error { if cfg.PrivateKeyPath != "" { cfg.PrivateKey = nil f, err := os.ReadFile(cfg.PrivateKeyPath) From 6e338b6f89481925a14cf8fffe6b46caa0fd9f36 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 20 May 2023 18:21:02 +0100 Subject: [PATCH 113/293] Fix con urrent map accesses --- src/core/link.go | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/core/link.go b/src/core/link.go index ed225904..e34f0d70 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -152,7 +152,12 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error { sintf: sintf, linkType: linkType, } - if state, ok := l._links[info]; ok { + var state *link + var ok bool + phony.Block(l, func() { + state, ok = l._links[info] + }) + if ok && state != nil { select { case state.kick <- struct{}{}: default: @@ -164,7 +169,7 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error { // in progress (if any), any error details and a context that // lets the link be cancelled later. ctx, cancel := context.WithCancel(l.core.ctx) - state := &link{ + state = &link{ info: info, linkProto: strings.ToUpper(u.Scheme), ctx: ctx, @@ -327,8 +332,12 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { _ = conn.Close() continue } - state := l._links[info] - if state == nil { + var state *link + var ok bool + phony.Block(l, func() { + state = l._links[info] + }) + if !ok || state == nil { state = &link{ info: info, } From 5ba9dadc490a5880c68db2519a8bc768c6ee002b Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 20 May 2023 18:31:01 +0100 Subject: [PATCH 114/293] Use `sync.Map` instead of link actor --- src/core/api.go | 47 ++++++++++++++++++----------------- src/core/link.go | 64 +++++++++++++++++++----------------------------- 2 files changed, 49 insertions(+), 62 deletions(-) diff --git a/src/core/api.go b/src/core/api.go index 8f448965..931ea197 100644 --- a/src/core/api.go +++ b/src/core/api.go @@ -71,31 +71,32 @@ func (c *Core) GetPeers() []PeerInfo { conns[p.Conn] = p } - phony.Block(&c.links, func() { - for info, state := range c.links._links { - var peerinfo PeerInfo - var conn net.Conn - phony.Block(state, func() { - peerinfo.URI = info.uri - peerinfo.LastError = state._err - peerinfo.LastErrorTime = state._errtime - if c := state._conn; c != nil { - conn = c - peerinfo.Up = true - peerinfo.Inbound = info.linkType == linkTypeIncoming - peerinfo.RXBytes = c.rx - peerinfo.TXBytes = c.tx - peerinfo.Uptime = time.Since(c.up) - } - }) - if p, ok := conns[conn]; ok { - peerinfo.Key = p.Key - peerinfo.Root = p.Root - peerinfo.Port = p.Port - peerinfo.Priority = p.Priority + c.links.links.Range(func(key, value any) bool { + info := key.(linkInfo) + state := value.(*link) + var peerinfo PeerInfo + var conn net.Conn + phony.Block(state, func() { + peerinfo.URI = info.uri + peerinfo.LastError = state._err + peerinfo.LastErrorTime = state._errtime + if c := state._conn; c != nil { + conn = c + peerinfo.Up = true + peerinfo.Inbound = info.linkType == linkTypeIncoming + peerinfo.RXBytes = c.rx + peerinfo.TXBytes = c.tx + peerinfo.Uptime = time.Since(c.up) } - peers = append(peers, peerinfo) + }) + if p, ok := conns[conn]; ok { + peerinfo.Key = p.Key + peerinfo.Root = p.Root + peerinfo.Port = p.Port + peerinfo.Priority = p.Priority } + peers = append(peers, peerinfo) + return true }) return peers diff --git a/src/core/link.go b/src/core/link.go index e34f0d70..114c8836 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -12,6 +12,7 @@ import ( "net/url" "strconv" "strings" + "sync" "sync/atomic" "time" @@ -28,13 +29,12 @@ const ( ) type links struct { - phony.Inbox - core *Core - tcp *linkTCP // TCP interface support - tls *linkTLS // TLS interface support - unix *linkUNIX // UNIX interface support - socks *linkSOCKS // SOCKS interface support - _links map[linkInfo]*link // *link is nil if connection in progress + core *Core + tcp *linkTCP // TCP interface support + tls *linkTLS // TLS interface support + unix *linkUNIX // UNIX interface support + socks *linkSOCKS // SOCKS interface support + links sync.Map // map[linkInfo]*link // *link is nil if connection in progress } type linkProtocol interface { @@ -92,7 +92,6 @@ func (l *links) init(c *Core) error { l.tls = l.newLinkTLS(l.tcp) l.unix = l.newLinkUNIX() l.socks = l.newLinkSOCKS() - l._links = make(map[linkInfo]*link) var listeners []ListenAddress phony.Block(c, func() { @@ -124,15 +123,11 @@ func (l *links) shutdown() { } func (l *links) isConnectedTo(info linkInfo) bool { - var isConnected bool - phony.Block(l, func() { - link, ok := l._links[info] - if !ok { - return - } - isConnected = link._conn != nil - }) - return isConnected + li, ok := l.links.Load(info) + if !ok || li == nil { + return false + } + return li.(*link)._conn != nil } type linkError string @@ -152,12 +147,12 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error { sintf: sintf, linkType: linkType, } + var state *link - var ok bool - phony.Block(l, func() { - state, ok = l._links[info] - }) - if ok && state != nil { + if s, ok := l.links.Load(info); ok { + state = s.(*link) + } + if state != nil { select { case state.kick <- struct{}{}: default: @@ -201,9 +196,7 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error { // Store the state of the link, try to connect and then run // the handler. - phony.Block(l, func() { - l._links[info] = state - }) + l.links.Store(info, state) // Track how many consecutive connection failures we have had, // as we will back off exponentially rather than hammering the @@ -234,9 +227,7 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error { // then the loop will run endlessly, using backoffs as needed. // Otherwise the loop will end, cleaning up the link entry. go func() { - defer phony.Block(l, func() { - delete(l._links, info) - }) + l.links.Delete(info) for { conn, err := l.connect(u, info, options) if err != nil { @@ -333,11 +324,10 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { continue } var state *link - var ok bool - phony.Block(l, func() { - state = l._links[info] - }) - if !ok || state == nil { + if s, ok := l.links.Load(info); ok { + state = s.(*link) + } + if state == nil { state = &link{ info: info, } @@ -352,9 +342,7 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { state._err = nil state.linkProto = strings.ToUpper(u.Scheme) }) - phony.Block(l, func() { - l._links[info] = state - }) + l.links.Store(info, state) if err = l.handler(&info, options, lc); err != nil && err != io.EOF { l.core.log.Debugf("Link %s error: %s\n", u.Host, err) } @@ -364,9 +352,7 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { state._errtime = time.Now() } }) - phony.Block(l, func() { - delete(l._links, info) - }) + l.links.Delete(info) } }() return li, nil From e0b39b303f4d852d7ffbf6e8e259a5a26ccf6edc Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 20 May 2023 18:36:44 +0100 Subject: [PATCH 115/293] Use regular mutex instead (less type assertions) This reverts commit 5ba9dadc490a5880c68db2519a8bc768c6ee002b. --- src/core/api.go | 9 ++++---- src/core/link.go | 59 ++++++++++++++++++++++++++++-------------------- 2 files changed, 39 insertions(+), 29 deletions(-) diff --git a/src/core/api.go b/src/core/api.go index 931ea197..c8d4515b 100644 --- a/src/core/api.go +++ b/src/core/api.go @@ -71,9 +71,9 @@ func (c *Core) GetPeers() []PeerInfo { conns[p.Conn] = p } - c.links.links.Range(func(key, value any) bool { - info := key.(linkInfo) - state := value.(*link) + c.links.RLock() + defer c.links.RUnlock() + for info, state := range c.links._links { var peerinfo PeerInfo var conn net.Conn phony.Block(state, func() { @@ -96,8 +96,7 @@ func (c *Core) GetPeers() []PeerInfo { peerinfo.Priority = p.Priority } peers = append(peers, peerinfo) - return true - }) + } return peers } diff --git a/src/core/link.go b/src/core/link.go index 114c8836..453b7bc1 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -29,12 +29,13 @@ const ( ) type links struct { - core *Core - tcp *linkTCP // TCP interface support - tls *linkTLS // TLS interface support - unix *linkUNIX // UNIX interface support - socks *linkSOCKS // SOCKS interface support - links sync.Map // map[linkInfo]*link // *link is nil if connection in progress + core *Core + tcp *linkTCP // TCP interface support + tls *linkTLS // TLS interface support + unix *linkUNIX // UNIX interface support + socks *linkSOCKS // SOCKS interface support + sync.RWMutex // Protects the below + _links map[linkInfo]*link // *link is nil if connection in progress } type linkProtocol interface { @@ -92,6 +93,7 @@ func (l *links) init(c *Core) error { l.tls = l.newLinkTLS(l.tcp) l.unix = l.newLinkUNIX() l.socks = l.newLinkSOCKS() + l._links = make(map[linkInfo]*link) var listeners []ListenAddress phony.Block(c, func() { @@ -123,11 +125,13 @@ func (l *links) shutdown() { } func (l *links) isConnectedTo(info linkInfo) bool { - li, ok := l.links.Load(info) - if !ok || li == nil { + l.RLock() + link, ok := l._links[info] + l.RUnlock() + if !ok { return false } - return li.(*link)._conn != nil + return link._conn != nil } type linkError string @@ -147,12 +151,10 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error { sintf: sintf, linkType: linkType, } - - var state *link - if s, ok := l.links.Load(info); ok { - state = s.(*link) - } - if state != nil { + l.RLock() + state, ok := l._links[info] + l.RUnlock() + if ok && state != nil { select { case state.kick <- struct{}{}: default: @@ -196,7 +198,9 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error { // Store the state of the link, try to connect and then run // the handler. - l.links.Store(info, state) + l.Lock() + l._links[info] = state + l.Unlock() // Track how many consecutive connection failures we have had, // as we will back off exponentially rather than hammering the @@ -227,7 +231,11 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error { // then the loop will run endlessly, using backoffs as needed. // Otherwise the loop will end, cleaning up the link entry. go func() { - l.links.Delete(info) + defer func() { + l.Lock() + defer l.Unlock() + delete(l._links, info) + }() for { conn, err := l.connect(u, info, options) if err != nil { @@ -323,11 +331,10 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { _ = conn.Close() continue } - var state *link - if s, ok := l.links.Load(info); ok { - state = s.(*link) - } - if state == nil { + l.RLock() + state, ok := l._links[info] + l.RUnlock() + if !ok || state == nil { state = &link{ info: info, } @@ -342,7 +349,9 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { state._err = nil state.linkProto = strings.ToUpper(u.Scheme) }) - l.links.Store(info, state) + l.Lock() + l._links[info] = state + l.Unlock() if err = l.handler(&info, options, lc); err != nil && err != io.EOF { l.core.log.Debugf("Link %s error: %s\n", u.Host, err) } @@ -352,7 +361,9 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { state._errtime = time.Now() } }) - l.links.Delete(info) + l.Lock() + delete(l._links, info) + l.Unlock() } }() return li, nil From c0188f56002789ad3017d4a29d0a4f75ef018fe9 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 20 May 2023 21:18:49 +0100 Subject: [PATCH 116/293] Discriminate multicast peers more loosely --- src/core/link.go | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/core/link.go b/src/core/link.go index 453b7bc1..8c18939e 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -9,6 +9,7 @@ import ( "io" "math" "net" + "net/netip" "net/url" "strconv" "strings" @@ -146,8 +147,9 @@ const ErrLinkUnrecognisedSchema = linkError("link schema unknown") func (l *links) add(u *url.URL, sintf string, linkType linkType) error { // Generate the link info and see whether we think we already // have an open peering to this peer. + lu := urlForLinkInfo(*u) info := linkInfo{ - uri: u.String(), + uri: lu.String(), sintf: sintf, linkType: linkType, } @@ -322,10 +324,11 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { } pu := *u pu.Host = conn.RemoteAddr().String() + lu := urlForLinkInfo(pu) info := linkInfo{ - uri: pu.String(), + uri: lu.String(), sintf: sintf, - linkType: linkTypeIncoming, + linkType: linkTypeEphemeral, // TODO: should be incoming } if l.isConnectedTo(info) { _ = conn.Close() @@ -480,6 +483,21 @@ func (l *links) handler(info *linkInfo, options linkOptions, conn net.Conn) erro return nil } +func urlForLinkInfo(u url.URL) url.URL { + u.RawQuery = "" + if host, _, err := net.SplitHostPort(u.Host); err == nil { + if addr, err := netip.ParseAddr(host); err == nil { + // For peers that look like multicast peers (i.e. + // link-local addresses), we will ignore the port number, + // otherwise we might open multiple connections to them. + if addr.IsLinkLocalUnicast() { + u.Host = fmt.Sprintf("[%s]", addr.String()) + } + } + } + return u +} + type linkConn struct { // tx and rx are at the beginning of the struct to ensure 64-bit alignment // on 32-bit platforms, see https://pkg.go.dev/sync/atomic#pkg-note-BUG From aff320108433e33246c22d0be4436127743c9930 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 20 May 2023 22:22:15 +0100 Subject: [PATCH 117/293] Fix incoming connection handlers --- src/core/link.go | 82 +++++++++++++++++++++++------------------------- 1 file changed, 39 insertions(+), 43 deletions(-) diff --git a/src/core/link.go b/src/core/link.go index 8c18939e..a432e089 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -322,51 +322,47 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { if err != nil { continue } - pu := *u - pu.Host = conn.RemoteAddr().String() - lu := urlForLinkInfo(pu) - info := linkInfo{ - uri: lu.String(), - sintf: sintf, - linkType: linkTypeEphemeral, // TODO: should be incoming - } - if l.isConnectedTo(info) { - _ = conn.Close() - continue - } - l.RLock() - state, ok := l._links[info] - l.RUnlock() - if !ok || state == nil { - state = &link{ - info: info, + go func(conn net.Conn) { + defer conn.Close() + pu := *u + pu.Host = conn.RemoteAddr().String() + lu := urlForLinkInfo(pu) + info := linkInfo{ + uri: lu.String(), + sintf: sintf, + linkType: linkTypeEphemeral, // TODO: should be incoming } - } - lc := &linkConn{ - Conn: conn, - up: time.Now(), - } - var options linkOptions - phony.Block(state, func() { - state._conn = lc - state._err = nil - state.linkProto = strings.ToUpper(u.Scheme) - }) - l.Lock() - l._links[info] = state - l.Unlock() - if err = l.handler(&info, options, lc); err != nil && err != io.EOF { - l.core.log.Debugf("Link %s error: %s\n", u.Host, err) - } - phony.Block(state, func() { - state._conn = nil - if state._err = err; state._err != nil { - state._errtime = time.Now() + if l.isConnectedTo(info) { + return } - }) - l.Lock() - delete(l._links, info) - l.Unlock() + l.RLock() + state, ok := l._links[info] + l.RUnlock() + if !ok || state == nil { + state = &link{ + info: info, + } + } + lc := &linkConn{ + Conn: conn, + up: time.Now(), + } + var options linkOptions + phony.Block(state, func() { + state._conn = lc + state._err = nil + state.linkProto = strings.ToUpper(u.Scheme) + }) + l.Lock() + l._links[info] = state + l.Unlock() + if err = l.handler(&info, options, lc); err != nil && err != io.EOF { + l.core.log.Debugf("Link %s error: %s\n", u.Host, err) + } + l.Lock() + delete(l._links, info) + l.Unlock() + }(conn) } }() return li, nil From 333561f4e17206f0949409e27f4cebf2a78fe1f7 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 20 May 2023 23:44:31 +0100 Subject: [PATCH 118/293] Tweak link state locking, add comments, listener priority, other fixes --- src/core/api.go | 27 ++++---- src/core/link.go | 173 ++++++++++++++++++++++++++++++++--------------- 2 files changed, 132 insertions(+), 68 deletions(-) diff --git a/src/core/api.go b/src/core/api.go index c8d4515b..c6e60381 100644 --- a/src/core/api.go +++ b/src/core/api.go @@ -9,7 +9,6 @@ import ( "time" "github.com/Arceliar/ironwood/network" - "github.com/Arceliar/phony" "github.com/yggdrasil-network/yggdrasil-go/src/address" ) @@ -76,19 +75,19 @@ func (c *Core) GetPeers() []PeerInfo { for info, state := range c.links._links { var peerinfo PeerInfo var conn net.Conn - phony.Block(state, func() { - peerinfo.URI = info.uri - peerinfo.LastError = state._err - peerinfo.LastErrorTime = state._errtime - if c := state._conn; c != nil { - conn = c - peerinfo.Up = true - peerinfo.Inbound = info.linkType == linkTypeIncoming - peerinfo.RXBytes = c.rx - peerinfo.TXBytes = c.tx - peerinfo.Uptime = time.Since(c.up) - } - }) + state.RLock() + peerinfo.URI = info.uri + peerinfo.LastError = state._err + peerinfo.LastErrorTime = state._errtime + if c := state._conn; c != nil { + conn = c + peerinfo.Up = true + peerinfo.Inbound = state.linkType == linkTypeIncoming + peerinfo.RXBytes = c.rx + peerinfo.TXBytes = c.tx + peerinfo.Uptime = time.Since(c.up) + } + state.RUnlock() if p, ok := conns[conn]; ok { peerinfo.Key = p.Key peerinfo.Root = p.Root diff --git a/src/core/link.go b/src/core/link.go index a432e089..d68d1bfe 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -46,23 +46,19 @@ type linkProtocol interface { // linkInfo is used as a map key type linkInfo struct { - uri string // Peering URI in complete form - sintf string // Peering source interface (i.e. from InterfacePeers) - linkType linkType // Type of link, i.e. outbound/inbound, persistent/ephemeral + uri string // Peering URI in complete form + sintf string // Peering source interface (i.e. from InterfacePeers) } // link tracks the state of a connection, either persistent or non-persistent type link struct { - phony.Inbox - ctx context.Context // - cancel context.CancelFunc // - kick chan struct{} // Attempt to reconnect now, if backing off - info linkInfo // - linkProto string // Protocol carrier of link, e.g. TCP, AWDL - _conn *linkConn // Connected link, if any, nil if not connected - _err error // Last error on the connection, if any - _errtime time.Time // Last time an error occured - + kick chan struct{} // Attempt to reconnect now, if backing off + linkType linkType // Type of link, i.e. outbound/inbound, persistent/ephemeral + linkProto string // Protocol carrier of link, e.g. TCP, AWDL + sync.RWMutex // Protects the below + _conn *linkConn // Connected link, if any, nil if not connected + _err error // Last error on the connection, if any + _errtime time.Time // Last time an error occured } type linkOptions struct { @@ -149,10 +145,14 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error { // have an open peering to this peer. lu := urlForLinkInfo(*u) info := linkInfo{ - uri: lu.String(), - sintf: sintf, - linkType: linkType, + uri: lu.String(), + sintf: sintf, } + + // If we think we're already connected to this peer, load up + // the existing peer state. Try to kick the peer if possible, + // which will cause an immediate connection attempt if it is + // backing off for some reason. l.RLock() state, ok := l._links[info] l.RUnlock() @@ -167,12 +167,10 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error { // Create the link entry. This will contain the connection // in progress (if any), any error details and a context that // lets the link be cancelled later. - ctx, cancel := context.WithCancel(l.core.ctx) state = &link{ - info: info, + linkType: linkType, linkProto: strings.ToUpper(u.Scheme), - ctx: ctx, - cancel: cancel, + kick: make(chan struct{}), } // Collect together the link options, these are global options @@ -198,8 +196,7 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error { options.priority = uint8(pi) } - // Store the state of the link, try to connect and then run - // the handler. + // Store the state of the link so that it can be queried later. l.Lock() l._links[info] = state l.Unlock() @@ -223,7 +220,7 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error { return true case <-state.kick: return true - case <-ctx.Done(): + case <-l.core.ctx.Done(): return false } } @@ -238,44 +235,75 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error { defer l.Unlock() delete(l._links, info) }() + + // This loop will run each and every time we want to attempt + // a connection to this peer. for { conn, err := l.connect(u, info, options) if err != nil { if linkType == linkTypePersistent { - phony.Block(state, func() { - state._err = err - state._errtime = time.Now() - }) + // If the link is a persistent configured peering, + // store information about the connection error so + // that we can report it through the admin socket. + state.Lock() + state._conn = nil + state._err = err + state._errtime = time.Now() + state.Unlock() + + // Back off for a bit. If true is returned here, we + // can continue onto the next loop iteration to try + // the next connection. if backoffNow() { continue } else { return } } else { + // Ephemeral and incoming connections don't remain + // after a connection failure, so exit out of the + // loop and clean up the link entry. break } } + + // The linkConn wrapper allows us to track the number of + // bytes written to and read from this connection without + // the help of ironwood. lc := &linkConn{ Conn: conn, up: time.Now(), } - phony.Block(state, func() { - state._conn = lc - state._err = nil - state._errtime = time.Time{} - }) - if err = l.handler(&info, options, lc); err != nil && err != io.EOF { + + // Update the link state with our newly wrapped connection. + // Clear the error state. + state.Lock() + state._conn = lc + state._err = nil + state._errtime = time.Time{} + state.Unlock() + + // Give the connection to the handler. The handler will block + // for the lifetime of the connection. + if err = l.handler(linkType, options, lc); err != nil && err != io.EOF { l.core.log.Debugf("Link %s error: %s\n", info.uri, err) } else { backoff = 0 } - _ = conn.Close() - phony.Block(state, func() { - state._conn = nil - if state._err = err; state._err != nil { - state._errtime = time.Now() - } - }) + + // The handler has stopped running so the connection is dead, + // try to close the underlying socket just in case and then + // update the link state. + _ = lc.Close() + state.Lock() + state._conn = nil + if state._err = err; state._err != nil { + state._errtime = time.Now() + } + state.Unlock() + + // If the link is persistently configured, back off if needed + // and then try reconnecting. Otherwise, exit out. if linkType == linkTypePersistent { if backoffNow() { continue @@ -314,6 +342,16 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { ctx: ctx, Cancel: cancel, } + + var options linkOptions + if p := u.Query().Get("priority"); p != "" { + pi, err := strconv.ParseUint(p, 10, 8) + if err != nil { + return nil, ErrLinkPriorityInvalid + } + options.priority = uint8(pi) + } + go func() { l.core.log.Printf("%s listener started on %s", strings.ToUpper(u.Scheme), listener.Addr()) defer l.core.log.Printf("%s listener stopped on %s", strings.ToUpper(u.Scheme), listener.Addr()) @@ -324,41 +362,68 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { } go func(conn net.Conn) { defer conn.Close() + + // In order to populate a somewhat sane looking connection + // URI in the admin socket, we need to replace the host in + // the listener URL with the remote address. pu := *u pu.Host = conn.RemoteAddr().String() lu := urlForLinkInfo(pu) info := linkInfo{ - uri: lu.String(), - sintf: sintf, - linkType: linkTypeEphemeral, // TODO: should be incoming + uri: lu.String(), + sintf: sintf, } + + // If this node is already connected to us, just drop the + // connection. This prevents duplicate peerings. if l.isConnectedTo(info) { return } + + // If there's an existing link state for this link, get it. + // Otherwise just create a new one. l.RLock() state, ok := l._links[info] l.RUnlock() if !ok || state == nil { state = &link{ - info: info, + linkType: linkTypeIncoming, + linkProto: strings.ToUpper(u.Scheme), + kick: make(chan struct{}), } } + + // The linkConn wrapper allows us to track the number of + // bytes written to and read from this connection without + // the help of ironwood. lc := &linkConn{ Conn: conn, up: time.Now(), } - var options linkOptions - phony.Block(state, func() { - state._conn = lc - state._err = nil - state.linkProto = strings.ToUpper(u.Scheme) - }) + + // Update the link state with our newly wrapped connection. + // Clear the error state. + state.Lock() + state._conn = lc + state._err = nil + state._errtime = time.Time{} + state.Unlock() + + // Store the state of the link so that it can be queried later. l.Lock() l._links[info] = state l.Unlock() - if err = l.handler(&info, options, lc); err != nil && err != io.EOF { + + // Give the connection to the handler. The handler will block + // for the lifetime of the connection. + if err = l.handler(linkTypeIncoming, options, lc); err != nil && err != io.EOF { l.core.log.Debugf("Link %s error: %s\n", u.Host, err) } + + // The handler has stopped running so the connection is dead, + // try to close the underlying socket just in case and then + // drop the link state. + _ = lc.Close() l.Lock() delete(l._links, info) l.Unlock() @@ -401,7 +466,7 @@ func (l *links) connect(u *url.URL, info linkInfo, options linkOptions) (net.Con return dialer.dial(u, info, options) } -func (l *links) handler(info *linkInfo, options linkOptions, conn net.Conn) error { +func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn) error { meta := version_getBaseMetadata() meta.publicKey = l.core.public metaBytes := meta.encode() @@ -453,12 +518,12 @@ func (l *links) handler(info *linkInfo, options linkOptions, conn net.Conn) erro break } } - if info.linkType == linkTypeIncoming && !isallowed { + if linkType == linkTypeIncoming && !isallowed { return fmt.Errorf("node public key %q is not in AllowedPublicKeys", hex.EncodeToString(meta.publicKey)) } dir := "outbound" - if info.linkType == linkTypeIncoming { + if linkType == linkTypeIncoming { dir = "inbound" } remoteAddr := net.IP(address.AddrForKey(meta.publicKey)[:]).String() From cb8333f9ffc0c7ccde3d512962d3c0133d8f027a Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 21 May 2023 00:02:04 +0100 Subject: [PATCH 119/293] Tweak lock behaviour --- src/core/link.go | 72 +++++++++++++++++++----------------------------- 1 file changed, 29 insertions(+), 43 deletions(-) diff --git a/src/core/link.go b/src/core/link.go index d68d1bfe..2bc11b2c 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -121,16 +121,6 @@ func (l *links) shutdown() { }) } -func (l *links) isConnectedTo(info linkInfo) bool { - l.RLock() - link, ok := l._links[info] - l.RUnlock() - if !ok { - return false - } - return link._conn != nil -} - type linkError string func (e linkError) Error() string { return string(e) } @@ -149,30 +139,6 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error { sintf: sintf, } - // If we think we're already connected to this peer, load up - // the existing peer state. Try to kick the peer if possible, - // which will cause an immediate connection attempt if it is - // backing off for some reason. - l.RLock() - state, ok := l._links[info] - l.RUnlock() - if ok && state != nil { - select { - case state.kick <- struct{}{}: - default: - } - return ErrLinkAlreadyConfigured - } - - // Create the link entry. This will contain the connection - // in progress (if any), any error details and a context that - // lets the link be cancelled later. - state = &link{ - linkType: linkType, - linkProto: strings.ToUpper(u.Scheme), - kick: make(chan struct{}), - } - // Collect together the link options, these are global options // that are not specific to any given protocol. var options linkOptions @@ -196,8 +162,31 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error { options.priority = uint8(pi) } - // Store the state of the link so that it can be queried later. + // If we think we're already connected to this peer, load up + // the existing peer state. Try to kick the peer if possible, + // which will cause an immediate connection attempt if it is + // backing off for some reason. l.Lock() + state, ok := l._links[info] + if ok && state != nil { + select { + case state.kick <- struct{}{}: + default: + } + l.Unlock() + return ErrLinkAlreadyConfigured + } + + // Create the link entry. This will contain the connection + // in progress (if any), any error details and a context that + // lets the link be cancelled later. + state = &link{ + linkType: linkType, + linkProto: strings.ToUpper(u.Scheme), + kick: make(chan struct{}), + } + + // Store the state of the link so that it can be queried later. l._links[info] = state l.Unlock() @@ -374,17 +363,15 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { sintf: sintf, } + // If there's an existing link state for this link, get it. // If this node is already connected to us, just drop the // connection. This prevents duplicate peerings. - if l.isConnectedTo(info) { + l.Lock() + state, ok := l._links[info] + if ok && state != nil && state._conn != nil { + l.Unlock() return } - - // If there's an existing link state for this link, get it. - // Otherwise just create a new one. - l.RLock() - state, ok := l._links[info] - l.RUnlock() if !ok || state == nil { state = &link{ linkType: linkTypeIncoming, @@ -410,7 +397,6 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { state.Unlock() // Store the state of the link so that it can be queried later. - l.Lock() l._links[info] = state l.Unlock() From 8b5add5301808a449f8b58f466cd3b85a2e72546 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sun, 21 May 2023 12:38:16 -0500 Subject: [PATCH 120/293] reduce allocations (also pulls in updated ironwood to do the same) --- go.mod | 2 +- go.sum | 4 ++-- src/core/core.go | 6 ++++-- src/core/pool.go | 17 +++++++++++++++++ 4 files changed, 24 insertions(+), 5 deletions(-) create mode 100644 src/core/pool.go diff --git a/go.mod b/go.mod index 4fd73400..6811e98a 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/yggdrasil-network/yggdrasil-go go 1.19 require ( - github.com/Arceliar/ironwood v0.0.0-20230515022317-31b976732ebe + github.com/Arceliar/ironwood v0.0.0-20230521173602-97ee6b09b8e0 github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d github.com/cheggaaa/pb/v3 v3.0.8 github.com/gologme/log v1.2.0 diff --git a/go.sum b/go.sum index ed5f7448..6eff958e 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Arceliar/ironwood v0.0.0-20230515022317-31b976732ebe h1:u69sr6Y9jqu6sk43Yyt+izLnLGgqCw3OYh2HU+jYUBw= -github.com/Arceliar/ironwood v0.0.0-20230515022317-31b976732ebe/go.mod h1:MIfrhR4b+U6gurd5pln622Zwaf2kzpIvXcnvRZMvlRI= +github.com/Arceliar/ironwood v0.0.0-20230521173602-97ee6b09b8e0 h1:u0BeMjhq0+jU+zaL6zlaBo9Z5KuG26bMtm+XM2e6dSQ= +github.com/Arceliar/ironwood v0.0.0-20230521173602-97ee6b09b8e0/go.mod h1:5x7fWW0mshe9WQ1lvSMmmHBYC3BeHH9gpwW5tz7cbfw= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q= github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= diff --git a/src/core/core.go b/src/core/core.go index 748b7381..dfc18704 100644 --- a/src/core/core.go +++ b/src/core/core.go @@ -183,7 +183,8 @@ func (c *Core) MTU() uint64 { } func (c *Core) ReadFrom(p []byte) (n int, from net.Addr, err error) { - buf := make([]byte, c.PacketConn.MTU()) + buf := allocBytes(int(c.PacketConn.MTU())) + defer freeBytes(buf) for { bs := buf n, from, err = c.PacketConn.ReadFrom(bs) @@ -217,7 +218,8 @@ func (c *Core) ReadFrom(p []byte) (n int, from net.Addr, err error) { } func (c *Core) WriteTo(p []byte, addr net.Addr) (n int, err error) { - buf := make([]byte, 0, 65535) + buf := allocBytes(0) + defer freeBytes(buf) buf = append(buf, typeSessionTraffic) buf = append(buf, p...) n, err = c.PacketConn.WriteTo(buf, addr) diff --git a/src/core/pool.go b/src/core/pool.go new file mode 100644 index 00000000..63c6253b --- /dev/null +++ b/src/core/pool.go @@ -0,0 +1,17 @@ +package core + +import "sync" + +var bytePool = sync.Pool{New: func() interface{} { return []byte(nil) }} + +func allocBytes(size int) []byte { + bs := bytePool.Get().([]byte) + if cap(bs) < size { + bs = make([]byte, size) + } + return bs[:size] +} + +func freeBytes(bs []byte) { + bytePool.Put(bs[:0]) +} From 5a6f27e732757e1b1468f89bbcf487d113b88457 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sun, 21 May 2023 12:43:03 -0500 Subject: [PATCH 121/293] cheer up the linter --- src/core/core.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/core.go b/src/core/core.go index dfc18704..8907dd05 100644 --- a/src/core/core.go +++ b/src/core/core.go @@ -184,7 +184,7 @@ func (c *Core) MTU() uint64 { func (c *Core) ReadFrom(p []byte) (n int, from net.Addr, err error) { buf := allocBytes(int(c.PacketConn.MTU())) - defer freeBytes(buf) + defer freeBytes(buf) //nolint:staticcheck for { bs := buf n, from, err = c.PacketConn.ReadFrom(bs) @@ -219,7 +219,7 @@ func (c *Core) ReadFrom(p []byte) (n int, from net.Addr, err error) { func (c *Core) WriteTo(p []byte, addr net.Addr) (n int, err error) { buf := allocBytes(0) - defer freeBytes(buf) + defer freeBytes(buf) //nolint:staticcheck buf = append(buf, typeSessionTraffic) buf = append(buf, p...) n, err = c.PacketConn.WriteTo(buf, addr) From e94985c583857cd3077dcc37132c7eb879d02292 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sun, 21 May 2023 12:49:49 -0500 Subject: [PATCH 122/293] try to cheer up the linter again --- go.mod | 2 +- go.sum | 4 ++-- src/core/core.go | 4 ++-- src/core/pool.go | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 6811e98a..0f4ee977 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/yggdrasil-network/yggdrasil-go go 1.19 require ( - github.com/Arceliar/ironwood v0.0.0-20230521173602-97ee6b09b8e0 + github.com/Arceliar/ironwood v0.0.0-20230521174855-fdfa6326d125 github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d github.com/cheggaaa/pb/v3 v3.0.8 github.com/gologme/log v1.2.0 diff --git a/go.sum b/go.sum index 6eff958e..d9cd440f 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Arceliar/ironwood v0.0.0-20230521173602-97ee6b09b8e0 h1:u0BeMjhq0+jU+zaL6zlaBo9Z5KuG26bMtm+XM2e6dSQ= -github.com/Arceliar/ironwood v0.0.0-20230521173602-97ee6b09b8e0/go.mod h1:5x7fWW0mshe9WQ1lvSMmmHBYC3BeHH9gpwW5tz7cbfw= +github.com/Arceliar/ironwood v0.0.0-20230521174855-fdfa6326d125 h1:l2elyrosw63mTqZzwR0Nv8vPZWZC/0Hvwl8Iuva5htM= +github.com/Arceliar/ironwood v0.0.0-20230521174855-fdfa6326d125/go.mod h1:5x7fWW0mshe9WQ1lvSMmmHBYC3BeHH9gpwW5tz7cbfw= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q= github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= diff --git a/src/core/core.go b/src/core/core.go index 8907dd05..dfc18704 100644 --- a/src/core/core.go +++ b/src/core/core.go @@ -184,7 +184,7 @@ func (c *Core) MTU() uint64 { func (c *Core) ReadFrom(p []byte) (n int, from net.Addr, err error) { buf := allocBytes(int(c.PacketConn.MTU())) - defer freeBytes(buf) //nolint:staticcheck + defer freeBytes(buf) for { bs := buf n, from, err = c.PacketConn.ReadFrom(bs) @@ -219,7 +219,7 @@ func (c *Core) ReadFrom(p []byte) (n int, from net.Addr, err error) { func (c *Core) WriteTo(p []byte, addr net.Addr) (n int, err error) { buf := allocBytes(0) - defer freeBytes(buf) //nolint:staticcheck + defer freeBytes(buf) buf = append(buf, typeSessionTraffic) buf = append(buf, p...) n, err = c.PacketConn.WriteTo(buf, addr) diff --git a/src/core/pool.go b/src/core/pool.go index 63c6253b..7b1e93ed 100644 --- a/src/core/pool.go +++ b/src/core/pool.go @@ -13,5 +13,5 @@ func allocBytes(size int) []byte { } func freeBytes(bs []byte) { - bytePool.Put(bs[:0]) + bytePool.Put(bs[:0]) //nolint:staticcheck } From 06ca8941c7fd4c2436cff0266fdbcfeaa9c7f9a9 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 22 May 2023 23:10:44 +0100 Subject: [PATCH 123/293] Fix race condition between incoming and outgoing connection setup --- src/core/link.go | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/core/link.go b/src/core/link.go index 2bc11b2c..7fa1317f 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -268,8 +268,6 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error { // Clear the error state. state.Lock() state._conn = lc - state._err = nil - state._errtime = time.Time{} state.Unlock() // Give the connection to the handler. The handler will block @@ -368,9 +366,17 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { // connection. This prevents duplicate peerings. l.Lock() state, ok := l._links[info] - if ok && state != nil && state._conn != nil { - l.Unlock() - return + if ok && state != nil { + switch { + case state._conn != nil: + // We are already connected to something. + case state._conn == nil && state._errtime == time.Time{}: + // We aren't connected yet, but the fact that there + // is no last error suggests we haven't yet attempted + // an outbound connection at all. + l.Unlock() + return + } } if !ok || state == nil { state = &link{ From 2eda59d9e4cd97ee9f784630c9ad2800afc4cefc Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 23 May 2023 22:39:10 +0100 Subject: [PATCH 124/293] Improve link setup locking and guards --- src/core/link.go | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/core/link.go b/src/core/link.go index 7fa1317f..713e4db8 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -267,6 +267,11 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error { // Update the link state with our newly wrapped connection. // Clear the error state. state.Lock() + if state._conn != nil { + // If a peering has come up in this time, abort this one. + state.Unlock() + return + } state._conn = lc state.Unlock() @@ -366,18 +371,6 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { // connection. This prevents duplicate peerings. l.Lock() state, ok := l._links[info] - if ok && state != nil { - switch { - case state._conn != nil: - // We are already connected to something. - case state._conn == nil && state._errtime == time.Time{}: - // We aren't connected yet, but the fact that there - // is no last error suggests we haven't yet attempted - // an outbound connection at all. - l.Unlock() - return - } - } if !ok || state == nil { state = &link{ linkType: linkTypeIncoming, @@ -385,6 +378,14 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { kick: make(chan struct{}), } } + state.Lock() + if state._conn != nil { + // If a connection has come up in this time, abort + // this one. + state.Unlock() + l.Unlock() + return + } // The linkConn wrapper allows us to track the number of // bytes written to and read from this connection without @@ -396,7 +397,6 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { // Update the link state with our newly wrapped connection. // Clear the error state. - state.Lock() state._conn = lc state._err = nil state._errtime = time.Time{} From db9b57c052e628fa7dcef909a24bc39e532df469 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 6 Jun 2023 22:11:49 +0100 Subject: [PATCH 125/293] Update `contrib/mobile` for the latest iOS build --- contrib/mobile/build | 2 +- contrib/mobile/mobile.go | 25 ++++++++++++++++++------- contrib/mobile/mobile_ios.go | 12 ++++++++++++ src/tun/options.go | 8 ++++++-- src/tun/tun.go | 9 ++++++++- src/tun/tun_darwin.go | 30 ++++++++++++++++++++++++++++-- 6 files changed, 73 insertions(+), 13 deletions(-) diff --git a/contrib/mobile/build b/contrib/mobile/build index 3c7b1d11..3f6b9bfc 100755 --- a/contrib/mobile/build +++ b/contrib/mobile/build @@ -37,7 +37,7 @@ if [ $IOS ]; then echo "Building framework for iOS" go get golang.org/x/mobile/bind gomobile bind \ - -target ios -tags mobile -o Yggdrasil.xcframework \ + -target ios,macos -tags mobile -o Yggdrasil.xcframework \ -ldflags="$LDFLAGS $STRIP" -gcflags="$GCFLAGS" \ ./contrib/mobile ./src/config; fi diff --git a/contrib/mobile/mobile.go b/contrib/mobile/mobile.go index 3b3227bd..f4f8c22f 100644 --- a/contrib/mobile/mobile.go +++ b/contrib/mobile/mobile.go @@ -6,6 +6,7 @@ import ( "fmt" "net" "regexp" + "runtime/debug" "github.com/gologme/log" @@ -15,6 +16,7 @@ import ( "github.com/yggdrasil-network/yggdrasil-go/src/defaults" "github.com/yggdrasil-network/yggdrasil-go/src/ipv6rwc" "github.com/yggdrasil-network/yggdrasil-go/src/multicast" + "github.com/yggdrasil-network/yggdrasil-go/src/tun" "github.com/yggdrasil-network/yggdrasil-go/src/version" _ "golang.org/x/mobile/bind" @@ -30,7 +32,9 @@ type Yggdrasil struct { iprwc *ipv6rwc.ReadWriteCloser config *config.NodeConfig multicast *multicast.Multicast + tun *tun.TunAdapter // optional log MobileLogger + logger *log.Logger } // StartAutoconfigure starts a node with a randomly generated config @@ -41,10 +45,12 @@ func (m *Yggdrasil) StartAutoconfigure() error { // StartJSON starts a node with the given JSON config. You can get JSON config // (rather than HJSON) by using the GenerateConfigJSON() function func (m *Yggdrasil) StartJSON(configjson []byte) error { - logger := log.New(m.log, "", 0) - logger.EnableLevel("error") - logger.EnableLevel("warn") - logger.EnableLevel("info") + debug.SetMemoryLimit(1024 * 1024 * 40) + + m.logger = log.New(m.log, "", 0) + m.logger.EnableLevel("error") + m.logger.EnableLevel("warn") + m.logger.EnableLevel("info") m.config = defaults.GenerateConfig() if err := json.Unmarshal(configjson, &m.config); err != nil { return err @@ -71,7 +77,7 @@ func (m *Yggdrasil) StartJSON(configjson []byte) error { } options = append(options, core.AllowedPublicKey(k[:])) } - m.core, err = core.New(sk[:], logger, options...) + m.core, err = core.New(sk[:], m.logger, options...) if err != nil { panic(err) } @@ -90,9 +96,9 @@ func (m *Yggdrasil) StartJSON(configjson []byte) error { Priority: uint8(intf.Priority), }) } - m.multicast, err = multicast.New(m.core, logger, options...) + m.multicast, err = multicast.New(m.core, m.logger, options...) if err != nil { - logger.Errorln("An error occurred starting multicast:", err) + m.logger.Errorln("An error occurred starting multicast:", err) } } @@ -155,6 +161,11 @@ func (m *Yggdrasil) Stop() error { if err := m.multicast.Stop(); err != nil { return err } + if m.tun != nil { + if err := m.tun.Stop(); err != nil { + return err + } + } m.core.Stop() return nil } diff --git a/contrib/mobile/mobile_ios.go b/contrib/mobile/mobile_ios.go index fedee2db..c7747ea3 100644 --- a/contrib/mobile/mobile_ios.go +++ b/contrib/mobile/mobile_ios.go @@ -15,6 +15,8 @@ void Log(const char *text) { import "C" import ( "unsafe" + + "github.com/yggdrasil-network/yggdrasil-go/src/tun" ) type MobileLogger struct { @@ -26,3 +28,13 @@ func (nsl MobileLogger) Write(p []byte) (n int, err error) { C.Log(cstr) return len(p), nil } + +func (m *Yggdrasil) TakeOverTUN(fd int32) error { + options := []tun.SetupOption{ + tun.FileDescriptor(fd), + tun.InterfaceMTU(m.iprwc.MTU()), + } + var err error + m.tun, err = tun.New(m.iprwc, m.logger, options...) + return err +} diff --git a/src/tun/options.go b/src/tun/options.go index 7be79211..58d3d80c 100644 --- a/src/tun/options.go +++ b/src/tun/options.go @@ -6,6 +6,8 @@ func (m *TunAdapter) _applyOption(opt SetupOption) { m.config.name = v case InterfaceMTU: m.config.mtu = v + case FileDescriptor: + m.config.fd = int32(v) } } @@ -15,6 +17,8 @@ type SetupOption interface { type InterfaceName string type InterfaceMTU uint64 +type FileDescriptor int32 -func (a InterfaceName) isSetupOption() {} -func (a InterfaceMTU) isSetupOption() {} +func (a InterfaceName) isSetupOption() {} +func (a InterfaceMTU) isSetupOption() {} +func (a FileDescriptor) isSetupOption() {} diff --git a/src/tun/tun.go b/src/tun/tun.go index fcd597b8..e97f87ef 100644 --- a/src/tun/tun.go +++ b/src/tun/tun.go @@ -37,6 +37,7 @@ type TunAdapter struct { isOpen bool isEnabled bool // Used by the writer to drop sessionTraffic if not enabled config struct { + fd int32 name InterfaceName mtu InterfaceMTU } @@ -119,7 +120,13 @@ func (tun *TunAdapter) _start() error { if tun.rwc.MaxMTU() < mtu { mtu = tun.rwc.MaxMTU() } - if err := tun.setup(string(tun.config.name), addr, mtu); err != nil { + var err error + if tun.config.fd > 0 { + err = tun.setupFD(tun.config.fd, addr, mtu) + } else { + err = tun.setup(string(tun.config.name), addr, mtu) + } + if err != nil { return err } if tun.MTU() != mtu { diff --git a/src/tun/tun_darwin.go b/src/tun/tun_darwin.go index a6d87a0c..cec5f794 100644 --- a/src/tun/tun_darwin.go +++ b/src/tun/tun_darwin.go @@ -1,5 +1,5 @@ -//go:build !mobile -// +build !mobile +//go:build darwin || ios +// +build darwin ios package tun @@ -7,6 +7,7 @@ package tun import ( "encoding/binary" + "os" "strconv" "strings" "unsafe" @@ -34,6 +35,31 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error { return tun.setupAddress(addr) } +// Configures the "utun" adapter from an existing file descriptor. +func (tun *TunAdapter) setupFD(fd int32, addr string, mtu uint64) error { + dfd, err := unix.Dup(int(fd)) + if err != nil { + return err + } + err = unix.SetNonblock(dfd, true) + if err != nil { + unix.Close(dfd) + return err + } + iface, err := wgtun.CreateTUNFromFile(os.NewFile(uintptr(dfd), "/dev/tun"), 0) + if err != nil { + unix.Close(dfd) + return err + } + tun.iface = iface + if m, err := iface.MTU(); err == nil { + tun.mtu = getSupportedMTU(uint64(m)) + } else { + tun.mtu = 0 + } + return nil // tun.setupAddress(addr) +} + const ( darwin_SIOCAIFADDR_IN6 = 2155899162 // netinet6/in6_var.h darwin_IN6_IFF_NODAD = 0x0020 // netinet6/in6_var.h From f6c0d8406d7bfad4bf76baf681750d85dbf7fcf9 Mon Sep 17 00:00:00 2001 From: Oleksandr Natalenko Date: Thu, 8 Jun 2023 21:44:46 +0200 Subject: [PATCH 126/293] cmd/yggdrasil: do not log timestamps to syslog It is expected a syslog implementation be it rsyslog or journald to have their own timestamping, so there's no point in duplicating that info. Signed-off-by: Oleksandr Natalenko --- cmd/yggdrasil/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 7ac45260..5afef604 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -69,7 +69,7 @@ func main() { case "syslog": if syslogger, err := gsyslog.NewLogger(gsyslog.LOG_NOTICE, "DAEMON", version.BuildName()); err == nil { - logger = log.New(syslogger, "", log.Flags()) + logger = log.New(syslogger, "", log.Flags() &^ (log.Ldate | log.Ltime)) } default: From c1ae9ea0d4c916c1e85dc90cedd038934d03af04 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sun, 18 Jun 2023 03:40:40 -0500 Subject: [PATCH 127/293] Switch back to using an actor to manage link state, and slighty randomize the delay between multicast announcements. This seems to fix the issue with duplicate connections (and breaks a livelock in the multicast code where both nodes keep closing the listen side of their connection, but that's kind of a hack, we need a better solution) --- src/core/api.go | 51 ++--- src/core/link.go | 443 +++++++++++++++++++------------------ src/multicast/multicast.go | 4 +- 3 files changed, 257 insertions(+), 241 deletions(-) diff --git a/src/core/api.go b/src/core/api.go index c6e60381..ebc818f5 100644 --- a/src/core/api.go +++ b/src/core/api.go @@ -6,8 +6,11 @@ import ( "fmt" "net" "net/url" + "sync/atomic" "time" + "github.com/Arceliar/phony" + "github.com/Arceliar/ironwood/network" "github.com/yggdrasil-network/yggdrasil-go/src/address" ) @@ -70,32 +73,30 @@ func (c *Core) GetPeers() []PeerInfo { conns[p.Conn] = p } - c.links.RLock() - defer c.links.RUnlock() - for info, state := range c.links._links { - var peerinfo PeerInfo - var conn net.Conn - state.RLock() - peerinfo.URI = info.uri - peerinfo.LastError = state._err - peerinfo.LastErrorTime = state._errtime - if c := state._conn; c != nil { - conn = c - peerinfo.Up = true - peerinfo.Inbound = state.linkType == linkTypeIncoming - peerinfo.RXBytes = c.rx - peerinfo.TXBytes = c.tx - peerinfo.Uptime = time.Since(c.up) + phony.Block(&c.links, func() { + for info, state := range c.links._links { + var peerinfo PeerInfo + var conn net.Conn + peerinfo.URI = info.uri + peerinfo.LastError = state._err + peerinfo.LastErrorTime = state._errtime + if c := state._conn; c != nil { + conn = c + peerinfo.Up = true + peerinfo.Inbound = state.linkType == linkTypeIncoming + peerinfo.RXBytes = atomic.LoadUint64(&c.rx) + peerinfo.TXBytes = atomic.LoadUint64(&c.tx) + peerinfo.Uptime = time.Since(c.up) + } + if p, ok := conns[conn]; ok { + peerinfo.Key = p.Key + peerinfo.Root = p.Root + peerinfo.Port = p.Port + peerinfo.Priority = p.Priority + } + peers = append(peers, peerinfo) } - state.RUnlock() - if p, ok := conns[conn]; ok { - peerinfo.Key = p.Key - peerinfo.Root = p.Root - peerinfo.Port = p.Port - peerinfo.Priority = p.Priority - } - peers = append(peers, peerinfo) - } + }) return peers } diff --git a/src/core/link.go b/src/core/link.go index 713e4db8..c13d6af3 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -13,7 +13,6 @@ import ( "net/url" "strconv" "strings" - "sync" "sync/atomic" "time" @@ -30,13 +29,14 @@ const ( ) type links struct { - core *Core - tcp *linkTCP // TCP interface support - tls *linkTLS // TLS interface support - unix *linkUNIX // UNIX interface support - socks *linkSOCKS // SOCKS interface support - sync.RWMutex // Protects the below - _links map[linkInfo]*link // *link is nil if connection in progress + phony.Inbox + core *Core + tcp *linkTCP // TCP interface support + tls *linkTLS // TLS interface support + unix *linkUNIX // UNIX interface support + socks *linkSOCKS // SOCKS interface support + // _links can only be modified safely from within the links actor + _links map[linkInfo]*link // *link is nil if connection in progress } type linkProtocol interface { @@ -52,13 +52,13 @@ type linkInfo struct { // link tracks the state of a connection, either persistent or non-persistent type link struct { - kick chan struct{} // Attempt to reconnect now, if backing off - linkType linkType // Type of link, i.e. outbound/inbound, persistent/ephemeral - linkProto string // Protocol carrier of link, e.g. TCP, AWDL - sync.RWMutex // Protects the below - _conn *linkConn // Connected link, if any, nil if not connected - _err error // Last error on the connection, if any - _errtime time.Time // Last time an error occured + kick chan struct{} // Attempt to reconnect now, if backing off + linkType linkType // Type of link, i.e. outbound/inbound, persistent/ephemeral + linkProto string // Protocol carrier of link, e.g. TCP, AWDL + // The remaining fields can only be modified safely from within the links actor + _conn *linkConn // Connected link, if any, nil if not connected + _err error // Last error on the connection, if any + _errtime time.Time // Last time an error occured } type linkOptions struct { @@ -131,183 +131,192 @@ const ErrLinkPinnedKeyInvalid = linkError("pinned public key is invalid") const ErrLinkUnrecognisedSchema = linkError("link schema unknown") func (l *links) add(u *url.URL, sintf string, linkType linkType) error { - // Generate the link info and see whether we think we already - // have an open peering to this peer. - lu := urlForLinkInfo(*u) - info := linkInfo{ - uri: lu.String(), - sintf: sintf, - } - - // Collect together the link options, these are global options - // that are not specific to any given protocol. - var options linkOptions - for _, pubkey := range u.Query()["key"] { - sigPub, err := hex.DecodeString(pubkey) - if err != nil { - return ErrLinkPinnedKeyInvalid + var retErr error + phony.Block(l, func() { + // Generate the link info and see whether we think we already + // have an open peering to this peer. + lu := urlForLinkInfo(*u) + info := linkInfo{ + uri: lu.String(), + sintf: sintf, } - var sigPubKey keyArray - copy(sigPubKey[:], sigPub) - if options.pinnedEd25519Keys == nil { - options.pinnedEd25519Keys = map[keyArray]struct{}{} - } - options.pinnedEd25519Keys[sigPubKey] = struct{}{} - } - if p := u.Query().Get("priority"); p != "" { - pi, err := strconv.ParseUint(p, 10, 8) - if err != nil { - return ErrLinkPriorityInvalid - } - options.priority = uint8(pi) - } - // If we think we're already connected to this peer, load up - // the existing peer state. Try to kick the peer if possible, - // which will cause an immediate connection attempt if it is - // backing off for some reason. - l.Lock() - state, ok := l._links[info] - if ok && state != nil { - select { - case state.kick <- struct{}{}: - default: - } - l.Unlock() - return ErrLinkAlreadyConfigured - } - - // Create the link entry. This will contain the connection - // in progress (if any), any error details and a context that - // lets the link be cancelled later. - state = &link{ - linkType: linkType, - linkProto: strings.ToUpper(u.Scheme), - kick: make(chan struct{}), - } - - // Store the state of the link so that it can be queried later. - l._links[info] = state - l.Unlock() - - // Track how many consecutive connection failures we have had, - // as we will back off exponentially rather than hammering the - // remote node endlessly. - var backoff int - - // backoffNow is called when there's a connection error. It - // will wait for the specified amount of time and then return - // true, unless the peering context was cancelled (due to a - // peer removal most likely), in which case it returns false. - // The caller should check the return value to decide whether - // or not to give up trying. - backoffNow := func() bool { - backoff++ - duration := time.Second * time.Duration(math.Exp2(float64(backoff))) - select { - case <-time.After(duration): - return true - case <-state.kick: - return true - case <-l.core.ctx.Done(): - return false - } - } - - // The goroutine is responsible for attempting the connection - // and then running the handler. If the connection is persistent - // then the loop will run endlessly, using backoffs as needed. - // Otherwise the loop will end, cleaning up the link entry. - go func() { - defer func() { - l.Lock() - defer l.Unlock() - delete(l._links, info) - }() - - // This loop will run each and every time we want to attempt - // a connection to this peer. - for { - conn, err := l.connect(u, info, options) + // Collect together the link options, these are global options + // that are not specific to any given protocol. + var options linkOptions + for _, pubkey := range u.Query()["key"] { + sigPub, err := hex.DecodeString(pubkey) if err != nil { - if linkType == linkTypePersistent { - // If the link is a persistent configured peering, - // store information about the connection error so - // that we can report it through the admin socket. - state.Lock() - state._conn = nil - state._err = err - state._errtime = time.Now() - state.Unlock() + retErr = ErrLinkPinnedKeyInvalid + return + } + var sigPubKey keyArray + copy(sigPubKey[:], sigPub) + if options.pinnedEd25519Keys == nil { + options.pinnedEd25519Keys = map[keyArray]struct{}{} + } + options.pinnedEd25519Keys[sigPubKey] = struct{}{} + } + if p := u.Query().Get("priority"); p != "" { + pi, err := strconv.ParseUint(p, 10, 8) + if err != nil { + retErr = ErrLinkPriorityInvalid + return + } + options.priority = uint8(pi) + } - // Back off for a bit. If true is returned here, we - // can continue onto the next loop iteration to try - // the next connection. + // If we think we're already connected to this peer, load up + // the existing peer state. Try to kick the peer if possible, + // which will cause an immediate connection attempt if it is + // backing off for some reason. + state, ok := l._links[info] + if ok && state != nil { + select { + case state.kick <- struct{}{}: + default: + } + retErr = ErrLinkAlreadyConfigured + return + } + + // Create the link entry. This will contain the connection + // in progress (if any), any error details and a context that + // lets the link be cancelled later. + state = &link{ + linkType: linkType, + linkProto: strings.ToUpper(u.Scheme), + kick: make(chan struct{}), + } + + // Store the state of the link so that it can be queried later. + l._links[info] = state + + // Track how many consecutive connection failures we have had, + // as we will back off exponentially rather than hammering the + // remote node endlessly. + var backoff int + + // backoffNow is called when there's a connection error. It + // will wait for the specified amount of time and then return + // true, unless the peering context was cancelled (due to a + // peer removal most likely), in which case it returns false. + // The caller should check the return value to decide whether + // or not to give up trying. + backoffNow := func() bool { + backoff++ + duration := time.Second * time.Duration(math.Exp2(float64(backoff))) + select { + case <-time.After(duration): + return true + case <-state.kick: + return true + case <-l.core.ctx.Done(): + return false + } + } + + // The goroutine is responsible for attempting the connection + // and then running the handler. If the connection is persistent + // then the loop will run endlessly, using backoffs as needed. + // Otherwise the loop will end, cleaning up the link entry. + go func() { + defer func() { + phony.Block(l, func() { + if l._links[info] == state { + delete(l._links, info) + } + }) + }() + + // This loop will run each and every time we want to attempt + // a connection to this peer. + // TODO get rid of this loop, this is *exactly* what time.AfterFunc is for, we should just send a signal to the links actor to kick off a goroutine as needed + for { + conn, err := l.connect(u, info, options) + if err != nil { + if linkType == linkTypePersistent { + // If the link is a persistent configured peering, + // store information about the connection error so + // that we can report it through the admin socket. + phony.Block(l, func() { + state._conn = nil + state._err = err + state._errtime = time.Now() + }) + + // Back off for a bit. If true is returned here, we + // can continue onto the next loop iteration to try + // the next connection. + if backoffNow() { + continue + } else { + return + } + } else { + // Ephemeral and incoming connections don't remain + // after a connection failure, so exit out of the + // loop and clean up the link entry. + break + } + } + + // The linkConn wrapper allows us to track the number of + // bytes written to and read from this connection without + // the help of ironwood. + lc := &linkConn{ + Conn: conn, + up: time.Now(), + } + + // Update the link state with our newly wrapped connection. + // Clear the error state. + var doRet bool + phony.Block(l, func() { + if state._conn != nil { + // If a peering has come up in this time, abort this one. + doRet = true + } + state._conn = lc + }) + if doRet { + return + } + + // Give the connection to the handler. The handler will block + // for the lifetime of the connection. + if err = l.handler(linkType, options, lc); err != nil && err != io.EOF { + l.core.log.Debugf("Link %s error: %s\n", info.uri, err) + } else { + backoff = 0 + } + + // The handler has stopped running so the connection is dead, + // try to close the underlying socket just in case and then + // update the link state. + _ = lc.Close() + phony.Block(l, func() { + state._conn = nil + if state._err = err; state._err != nil { + state._errtime = time.Now() + } + }) + + // If the link is persistently configured, back off if needed + // and then try reconnecting. Otherwise, exit out. + if linkType == linkTypePersistent { if backoffNow() { continue } else { return } } else { - // Ephemeral and incoming connections don't remain - // after a connection failure, so exit out of the - // loop and clean up the link entry. break } } - - // The linkConn wrapper allows us to track the number of - // bytes written to and read from this connection without - // the help of ironwood. - lc := &linkConn{ - Conn: conn, - up: time.Now(), - } - - // Update the link state with our newly wrapped connection. - // Clear the error state. - state.Lock() - if state._conn != nil { - // If a peering has come up in this time, abort this one. - state.Unlock() - return - } - state._conn = lc - state.Unlock() - - // Give the connection to the handler. The handler will block - // for the lifetime of the connection. - if err = l.handler(linkType, options, lc); err != nil && err != io.EOF { - l.core.log.Debugf("Link %s error: %s\n", info.uri, err) - } else { - backoff = 0 - } - - // The handler has stopped running so the connection is dead, - // try to close the underlying socket just in case and then - // update the link state. - _ = lc.Close() - state.Lock() - state._conn = nil - if state._err = err; state._err != nil { - state._errtime = time.Now() - } - state.Unlock() - - // If the link is persistently configured, back off if needed - // and then try reconnecting. Otherwise, exit out. - if linkType == linkTypePersistent { - if backoffNow() { - continue - } else { - return - } - } else { - break - } - } - }() - return nil + }() + }) + return retErr } func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { @@ -369,43 +378,45 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { // If there's an existing link state for this link, get it. // If this node is already connected to us, just drop the // connection. This prevents duplicate peerings. - l.Lock() - state, ok := l._links[info] - if !ok || state == nil { - state = &link{ - linkType: linkTypeIncoming, - linkProto: strings.ToUpper(u.Scheme), - kick: make(chan struct{}), + var lc *linkConn + var state *link + phony.Block(l, func() { + var ok bool + state, ok = l._links[info] + if !ok || state == nil { + state = &link{ + linkType: linkTypeIncoming, + linkProto: strings.ToUpper(u.Scheme), + kick: make(chan struct{}), + } } - } - state.Lock() - if state._conn != nil { - // If a connection has come up in this time, abort - // this one. - state.Unlock() - l.Unlock() + if state._conn != nil { + // If a connection has come up in this time, abort + // this one. + return + } + + // The linkConn wrapper allows us to track the number of + // bytes written to and read from this connection without + // the help of ironwood. + lc = &linkConn{ + Conn: conn, + up: time.Now(), + } + + // Update the link state with our newly wrapped connection. + // Clear the error state. + state._conn = lc + state._err = nil + state._errtime = time.Time{} + + // Store the state of the link so that it can be queried later. + l._links[info] = state + }) + if lc == nil { return } - // The linkConn wrapper allows us to track the number of - // bytes written to and read from this connection without - // the help of ironwood. - lc := &linkConn{ - Conn: conn, - up: time.Now(), - } - - // Update the link state with our newly wrapped connection. - // Clear the error state. - state._conn = lc - state._err = nil - state._errtime = time.Time{} - state.Unlock() - - // Store the state of the link so that it can be queried later. - l._links[info] = state - l.Unlock() - // Give the connection to the handler. The handler will block // for the lifetime of the connection. if err = l.handler(linkTypeIncoming, options, lc); err != nil && err != io.EOF { @@ -416,9 +427,11 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { // try to close the underlying socket just in case and then // drop the link state. _ = lc.Close() - l.Lock() - delete(l._links, info) - l.Unlock() + phony.Block(l, func() { + if l._links[info] == state { + delete(l._links, info) + } + }) }(conn) } }() diff --git a/src/multicast/multicast.go b/src/multicast/multicast.go index e6fdb80a..9cd67ff1 100644 --- a/src/multicast/multicast.go +++ b/src/multicast/multicast.go @@ -4,6 +4,7 @@ import ( "context" "encoding/hex" "fmt" + "math/rand" "net" "net/url" "time" @@ -337,7 +338,8 @@ func (m *Multicast) _announce() { break } } - m._timer = time.AfterFunc(time.Second, func() { + annInterval := time.Second + time.Microsecond*(time.Duration(rand.Intn(1048576))) // Randomize delay + m._timer = time.AfterFunc(annInterval, func() { m.Act(nil, m._announce) }) } From b0f8d8af1319878fefd375b193a41f4cb0b33c44 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 18 Jun 2023 15:36:14 +0100 Subject: [PATCH 128/293] Define interface for RWCs --- src/tun/tun.go | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/tun/tun.go b/src/tun/tun.go index 93cb433f..5768461d 100644 --- a/src/tun/tun.go +++ b/src/tun/tun.go @@ -8,6 +8,7 @@ package tun import ( "errors" "fmt" + "io" "net" "github.com/Arceliar/phony" @@ -21,22 +22,29 @@ import ( type MTU uint16 +type ReadWriteCloser interface { + io.ReadWriteCloser + Address() address.Address + Subnet() address.Subnet + MaxMTU() uint64 + SetMTU(uint64) +} + // TunAdapter represents a running TUN interface and extends the // yggdrasil.Adapter type. In order to use the TUN adapter with Yggdrasil, you // should pass this object to the yggdrasil.SetRouterAdapter() function before // calling yggdrasil.Start(). type TunAdapter struct { - rwc *ipv6rwc.ReadWriteCloser + rwc ReadWriteCloser log core.Logger addr address.Address subnet address.Subnet mtu uint64 iface tun.Device phony.Inbox // Currently only used for _handlePacket from the reader, TODO: all the stuff that currently needs a mutex below - //mutex sync.RWMutex // Protects the below - isOpen bool - isEnabled bool // Used by the writer to drop sessionTraffic if not enabled - config struct { + isOpen bool + isEnabled bool // Used by the writer to drop sessionTraffic if not enabled + config struct { name InterfaceName mtu InterfaceMTU } From 5e684550a87828edac22cc523406a5c5c92e1efa Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 18 Jun 2023 15:45:04 +0100 Subject: [PATCH 129/293] Take interface in `tun.New` --- src/tun/tun.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/tun/tun.go b/src/tun/tun.go index 5768461d..7964ab9d 100644 --- a/src/tun/tun.go +++ b/src/tun/tun.go @@ -17,7 +17,6 @@ import ( "github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/config" "github.com/yggdrasil-network/yggdrasil-go/src/core" - "github.com/yggdrasil-network/yggdrasil-go/src/ipv6rwc" ) type MTU uint16 @@ -98,7 +97,7 @@ func MaximumMTU() uint64 { // Init initialises the TUN module. You must have acquired a Listener from // the Yggdrasil core before this point and it must not be in use elsewhere. -func New(rwc *ipv6rwc.ReadWriteCloser, log core.Logger, opts ...SetupOption) (*TunAdapter, error) { +func New(rwc ReadWriteCloser, log core.Logger, opts ...SetupOption) (*TunAdapter, error) { tun := &TunAdapter{ rwc: rwc, log: log, From 002b984c0450ba8ff2f865127de38b27de8ddc0b Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 18 Jun 2023 18:10:27 +0100 Subject: [PATCH 130/293] Fix private key setup when certificate not specified --- src/config/config.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/config/config.go b/src/config/config.go index 1980cb21..fe55e827 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -151,7 +151,14 @@ func (cfg *NodeConfig) postprocessConfig() error { return err } } - if cfg.Certificate == nil { + switch { + case cfg.Certificate == nil: + // No self-signed certificate has been generated yet. + fallthrough + case !bytes.Equal(cfg.Certificate.PrivateKey.(ed25519.PrivateKey), cfg.PrivateKey): + // A self-signed certificate was generated but the private + // key has changed since then, possibly because a new config + // was parsed. if err := cfg.GenerateSelfSignedCertificate(); err != nil { return err } From 109f59c7dc48e1979492f527f05a8abe83914836 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 18 Jun 2023 20:28:14 +0100 Subject: [PATCH 131/293] Tweak link handshake --- src/core/link.go | 14 +++++--------- src/core/version.go | 19 +++++++++++++++---- src/core/version_test.go | 3 ++- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/core/link.go b/src/core/link.go index c13d6af3..c847e5ac 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -4,7 +4,6 @@ import ( "bytes" "context" "encoding/hex" - "errors" "fmt" "io" "math" @@ -485,16 +484,10 @@ func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn) e case err == nil && n != len(metaBytes): return fmt.Errorf("incomplete handshake send") } - if _, err = io.ReadFull(conn, metaBytes); err != nil { - return fmt.Errorf("read handshake: %w", err) - } - if err = conn.SetDeadline(time.Time{}); err != nil { - return fmt.Errorf("failed to clear handshake deadline: %w", err) - } meta = version_metadata{} base := version_getBaseMetadata() - if !meta.decode(metaBytes) { - return errors.New("failed to decode metadata") + if !meta.decode(conn) { + return conn.Close() } if !meta.check() { return fmt.Errorf("remote node incompatible version (local %s, remote %s)", @@ -502,6 +495,9 @@ func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn) e fmt.Sprintf("%d.%d", meta.majorVer, meta.minorVer), ) } + if err = conn.SetDeadline(time.Time{}); err != nil { + return fmt.Errorf("failed to clear handshake deadline: %w", err) + } // Check if the remote side matches the keys we expected. This is a bit of a weak // check - in future versions we really should check a signature or something like that. if pinned := options.pinnedEd25519Keys; len(pinned) > 0 { diff --git a/src/core/version.go b/src/core/version.go index 3787d1e0..0820fbdd 100644 --- a/src/core/version.go +++ b/src/core/version.go @@ -8,6 +8,7 @@ import ( "bytes" "crypto/ed25519" "encoding/binary" + "io" ) // This is the version-specific metadata exchanged at the start of a connection. @@ -44,6 +45,7 @@ func version_getBaseMetadata() version_metadata { func (m *version_metadata) encode() []byte { bs := make([]byte, 0, 64) bs = append(bs, 'm', 'e', 't', 'a') + bs = append(bs, 0, 0) // Remaining message length bs = binary.BigEndian.AppendUint16(bs, metaVersionMajor) bs = binary.BigEndian.AppendUint16(bs, 2) @@ -61,16 +63,25 @@ func (m *version_metadata) encode() []byte { bs = binary.BigEndian.AppendUint16(bs, 1) bs = append(bs, m.priority) + binary.BigEndian.PutUint16(bs[4:6], uint16(len(bs)-6)) return bs } // Decodes version metadata from its wire format into the struct. -func (m *version_metadata) decode(bs []byte) bool { - meta := [4]byte{'m', 'e', 't', 'a'} - if !bytes.Equal(bs[:4], meta[:]) { +func (m *version_metadata) decode(r io.Reader) bool { + bh := [6]byte{} + if _, err := io.ReadFull(r, bh[:]); err != nil { return false } - for bs = bs[4:]; len(bs) >= 4; { + meta := [4]byte{'m', 'e', 't', 'a'} + if !bytes.Equal(bh[:4], meta[:]) { + return false + } + bs := make([]byte, binary.BigEndian.Uint16(bh[4:6])) + if _, err := io.ReadFull(r, bs); err != nil { + return false + } + for len(bs) >= 4 { op := binary.BigEndian.Uint16(bs[:2]) oplen := binary.BigEndian.Uint16(bs[2:4]) if bs = bs[4:]; len(bs) < int(oplen) { diff --git a/src/core/version_test.go b/src/core/version_test.go index 6fb7895c..511cb358 100644 --- a/src/core/version_test.go +++ b/src/core/version_test.go @@ -1,6 +1,7 @@ package core import ( + "bytes" "crypto/ed25519" "math/rand" "reflect" @@ -22,7 +23,7 @@ func TestVersionRoundtrip(t *testing.T) { test.publicKey = make(ed25519.PublicKey, ed25519.PublicKeySize) rand.Read(test.publicKey) - encoded := test.encode() + encoded := bytes.NewBuffer(test.encode()) decoded := &version_metadata{} if !decoded.decode(encoded) { t.Fatalf("failed to decode") From d8dc6b2670297ee063969910555569e92998bea0 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 21 May 2023 11:29:05 +0100 Subject: [PATCH 132/293] QUIC interface support --- go.mod | 8 ++++ go.sum | 49 ++++++++++++++++++++++ src/core/link.go | 6 +++ src/core/link_quic.go | 96 +++++++++++++++++++++++++++++++++++++++++++ src/core/tls.go | 1 + 5 files changed, 160 insertions(+) create mode 100644 src/core/link_quic.go diff --git a/go.mod b/go.mod index 0f4ee977..f8dddc12 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/hashicorp/go-syslog v1.0.0 github.com/hjson/hjson-go/v4 v4.3.0 github.com/kardianos/minwinsvc v1.0.2 + github.com/quic-go/quic-go v0.34.0 github.com/vishvananda/netlink v1.1.0 golang.org/x/mobile v0.0.0-20221110043201-43a038452099 golang.org/x/net v0.9.0 @@ -22,9 +23,16 @@ require ( require ( github.com/bits-and-blooms/bitset v1.5.0 // indirect github.com/bits-and-blooms/bloom/v3 v3.3.1 // indirect + github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect + github.com/golang/mock v1.6.0 // indirect + github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/mattn/go-colorable v0.1.8 // indirect + github.com/onsi/ginkgo/v2 v2.2.0 // indirect + github.com/quic-go/qtls-go1-19 v0.3.2 // indirect + github.com/quic-go/qtls-go1-20 v0.2.2 // indirect github.com/rivo/uniseg v0.2.0 // indirect golang.org/x/crypto v0.8.0 // indirect + golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect golang.org/x/mod v0.8.0 // indirect golang.org/x/tools v0.6.0 // indirect ) diff --git a/go.sum b/go.sum index d9cd440f..78da1041 100644 --- a/go.sum +++ b/go.sum @@ -12,15 +12,30 @@ github.com/bits-and-blooms/bloom/v3 v3.3.1 h1:K2+A19bXT8gJR5mU7y+1yW6hsKfNCjcP2u github.com/bits-and-blooms/bloom/v3 v3.3.1/go.mod h1:bhUUknWd5khVbTe4UgMCSiOOVJzr3tMoijSK3WwvW90= github.com/cheggaaa/pb/v3 v3.0.8 h1:bC8oemdChbke2FHIIGy9mn4DPJ2caZYQnfbRqwmdCoA= github.com/cheggaaa/pb/v3 v3.0.8/go.mod h1:UICbiLec/XO6Hw6k+BHEtHeQFzzBH4i2/qk/ow1EJTA= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/gologme/log v1.2.0 h1:Ya5Ip/KD6FX7uH0S31QO87nCCSucKtF44TLbTtO7V4c= github.com/gologme/log v1.2.0/go.mod h1:gq31gQ8wEHkR+WekdWsqDuf8pXTUZA9BnnzTuPz1Y9U= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hjson/hjson-go/v4 v4.3.0 h1:dyrzJdqqFGhHt+FSrs5n9s6b0fPM8oSJdWo+oS3YnJw= github.com/hjson/hjson-go/v4 v4.3.0/go.mod h1:KaYt3bTw3zhBjYqnXkYywcYctk0A2nxeEFTse3rH13E= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/kardianos/minwinsvc v1.0.2 h1:JmZKFJQrmTGa/WiW+vkJXKmfzdjabuEW4Tirj5lLdR0= github.com/kardianos/minwinsvc v1.0.2/go.mod h1:LUZNYhNmxujx2tR7FbdxqYJ9XDDoCd3MQcl1o//FWl4= github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= @@ -34,9 +49,23 @@ github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4 github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI= +github.com/onsi/ginkgo/v2 v2.2.0/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= +github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/quic-go/qtls-go1-19 v0.3.2 h1:tFxjCFcTQzK+oMxG6Zcvp4Dq8dx4yD3dDiIiyc86Z5U= +github.com/quic-go/qtls-go1-19 v0.3.2/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI= +github.com/quic-go/qtls-go1-20 v0.2.2 h1:WLOPx6OY/hxtTxKV1Zrq20FtXtDEkeY00CGQm8GEa3E= +github.com/quic-go/qtls-go1-20 v0.2.2/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM= +github.com/quic-go/quic-go v0.34.0 h1:OvOJ9LFjTySgwOTYUZmNoq0FzVicP8YujpV0kB7m2lU= +github.com/quic-go/quic-go v0.34.0/go.mod h1:+4CVgVppm0FNjpG3UcX8Joi/frKOH7/ciD5yGcwOO1g= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= @@ -44,33 +73,45 @@ github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYp github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f h1:p4VB7kIXpOQvVn1ZaTIVp+3vuYAXFe3OJEvjbUYJLaA= github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= +golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o= +golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/mobile v0.0.0-20221110043201-43a038452099 h1:aIu0lKmfdgtn2uTj7JI2oN4TUrQvgB+wzTPO23bCKt8= golang.org/x/mobile v0.0.0-20221110043201-43a038452099/go.mod h1:aAjjkJNdrh3PMckS4B10TGS2nag27cbKR1y2BpUxsiY= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -89,11 +130,19 @@ golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.zx2c4.com/wireguard v0.0.0-20211017052713-f87e87af0d9a h1:tTbyylK9/D3u/wEP26Vx7L700UpY48nhioJWZM1vhZw= golang.zx2c4.com/wireguard v0.0.0-20211017052713-f87e87af0d9a/go.mod h1:id8Oh3eCCmpj9uVGWVjsUAl6UPX5ysMLzu6QxJU2UOU= golang.zx2c4.com/wireguard/windows v0.4.12 h1:CUmbdWKVNzTSsVb4yUAiEwL3KsabdJkEPdDjCHxBlhA= golang.zx2c4.com/wireguard/windows v0.4.12/go.mod h1:PW4y+d9oY83XU9rRwRwrJDwEMuhVjMxu2gfD1cfzS7w= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/src/core/link.go b/src/core/link.go index c13d6af3..80fc63ed 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -35,6 +35,7 @@ type links struct { tls *linkTLS // TLS interface support unix *linkUNIX // UNIX interface support socks *linkSOCKS // SOCKS interface support + quic *linkQUIC // QUIC interface support // _links can only be modified safely from within the links actor _links map[linkInfo]*link // *link is nil if connection in progress } @@ -90,6 +91,7 @@ func (l *links) init(c *Core) error { l.tls = l.newLinkTLS(l.tcp) l.unix = l.newLinkUNIX() l.socks = l.newLinkSOCKS() + l.quic = l.newLinkQUIC() l._links = make(map[linkInfo]*link) var listeners []ListenAddress @@ -329,6 +331,8 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { protocol = l.tls case "unix": protocol = l.unix + case "quic": + protocol = l.quic default: cancel() return nil, ErrLinkUnrecognisedSchema @@ -465,6 +469,8 @@ func (l *links) connect(u *url.URL, info linkInfo, options linkOptions) (net.Con dialer = l.socks case "unix": dialer = l.unix + case "quic": + dialer = l.quic default: return nil, ErrLinkUnrecognisedSchema } diff --git a/src/core/link_quic.go b/src/core/link_quic.go new file mode 100644 index 00000000..0ada7e47 --- /dev/null +++ b/src/core/link_quic.go @@ -0,0 +1,96 @@ +package core + +import ( + "context" + "crypto/tls" + "fmt" + "net" + "net/url" + + "github.com/Arceliar/phony" + "github.com/quic-go/quic-go" +) + +type linkQUIC struct { + phony.Inbox + *links + tlsconfig *tls.Config + quicconfig *quic.Config +} + +type linkQUICStream struct { + quic.Connection + quic.Stream +} + +type linkQUICListener struct { + quic.EarlyListener + ch <-chan *linkQUICStream +} + +func (l *linkQUICListener) Accept() (net.Conn, error) { + qs := <-l.ch + if qs == nil { + return nil, context.Canceled + } + return qs, nil +} + +func (l *links) newLinkQUIC() *linkQUIC { + lt := &linkQUIC{ + links: l, + tlsconfig: l.core.config.tls.Clone(), + quicconfig: &quic.Config{ + KeepAlivePeriod: 0, + }, + } + return lt +} + +func (l *linkQUIC) dial(url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) { + qc, err := quic.DialAddrEarly(url.Host, l.tlsconfig, l.quicconfig) + if err != nil { + fmt.Println("Dial error:", err) + return nil, err + } + qs, err := qc.OpenStream() + if err != nil { + fmt.Println("Stream error:", err) + return nil, err + } + return &linkQUICStream{ + Connection: qc, + Stream: qs, + }, nil +} + +func (l *linkQUIC) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) { + ql, err := quic.ListenAddrEarly(url.Host, l.tlsconfig, l.quicconfig) + if err != nil { + return nil, err + } + ch := make(chan *linkQUICStream) + lql := &linkQUICListener{ + EarlyListener: ql, + ch: ch, + } + go func() { + for { + qc, err := ql.Accept(ctx) + if err != nil { + ql.Close() + return + } + qs, err := qc.AcceptStream(ctx) + if err != nil { + ql.Close() + return + } + ch <- &linkQUICStream{ + Connection: qc, + Stream: qs, + } + } + }() + return lql, nil +} diff --git a/src/core/tls.go b/src/core/tls.go index 4a55ea35..08d0bd1e 100644 --- a/src/core/tls.go +++ b/src/core/tls.go @@ -17,6 +17,7 @@ func (c *Core) generateTLSConfig(cert *tls.Certificate) (*tls.Config, error) { VerifyConnection: c.verifyTLSConnection, InsecureSkipVerify: true, MinVersion: tls.VersionTLS13, + NextProtos: []string{"yggdrasil/0.5"}, } return config, nil } From 516fcce6b3d0160d4b4ae7c268c0bfcd17c25517 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 21 May 2023 11:39:49 +0100 Subject: [PATCH 133/293] Keepalives are needed to stop the connection inactivity timeout --- src/core/link_quic.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/link_quic.go b/src/core/link_quic.go index 0ada7e47..dd5d8818 100644 --- a/src/core/link_quic.go +++ b/src/core/link_quic.go @@ -6,6 +6,7 @@ import ( "fmt" "net" "net/url" + "time" "github.com/Arceliar/phony" "github.com/quic-go/quic-go" @@ -41,7 +42,9 @@ func (l *links) newLinkQUIC() *linkQUIC { links: l, tlsconfig: l.core.config.tls.Clone(), quicconfig: &quic.Config{ - KeepAlivePeriod: 0, + MaxIdleTimeout: time.Minute, + KeepAlivePeriod: time.Second * 20, + TokenStore: quic.NewLRUTokenStore(255, 255), }, } return lt From 423fc248d27f46c647cd07001c19c59722b1f366 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 21 May 2023 11:50:47 +0100 Subject: [PATCH 134/293] Remove debug lines --- src/core/link_quic.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/core/link_quic.go b/src/core/link_quic.go index dd5d8818..9745fb14 100644 --- a/src/core/link_quic.go +++ b/src/core/link_quic.go @@ -3,7 +3,6 @@ package core import ( "context" "crypto/tls" - "fmt" "net" "net/url" "time" @@ -53,12 +52,10 @@ func (l *links) newLinkQUIC() *linkQUIC { func (l *linkQUIC) dial(url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) { qc, err := quic.DialAddrEarly(url.Host, l.tlsconfig, l.quicconfig) if err != nil { - fmt.Println("Dial error:", err) return nil, err } qs, err := qc.OpenStream() if err != nil { - fmt.Println("Stream error:", err) return nil, err } return &linkQUICStream{ From 57d9a2399f3d89530ebec371e27b27dbd7edbb13 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 21 May 2023 15:24:31 +0100 Subject: [PATCH 135/293] Revise multicast format to include protocol version, discriminator for TLS roots --- cmd/yggdrasil/main.go | 29 ++++++++++++++++++++++ src/multicast/advertisement.go | 23 ++++++++++++----- src/multicast/advertisement_test.go | 38 +++++++++++++++++++++++++++++ src/multicast/multicast.go | 25 +++++++++++++++---- src/multicast/options.go | 8 ++++++ 5 files changed, 112 insertions(+), 11 deletions(-) create mode 100644 src/multicast/advertisement_test.go diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 7ac45260..8de2c868 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -3,6 +3,7 @@ package main import ( "context" "crypto/ed25519" + "crypto/sha1" "encoding/hex" "encoding/json" "flag" @@ -195,6 +196,10 @@ func main() { n := &node{} + // Track certificate fingerprints for configured roots, so + // that we can match them using the multicast discriminator. + fingerprints := map[[20]byte]struct{}{} + // Setup the Yggdrasil node itself. { options := []core.SetupOption{ @@ -214,6 +219,7 @@ func main() { } for _, root := range cfg.RootCertificates { options = append(options, core.RootCertificate(*root)) + fingerprints[sha1.Sum(root.Raw[:])] = struct{}{} } for _, allowed := range cfg.AllowedPublicKeys { k, err := hex.DecodeString(allowed) @@ -252,6 +258,29 @@ func main() { Priority: uint8(intf.Priority), }) } + if len(fingerprints) > 0 { + var matcher multicast.DiscriminatorMatch = func(b []byte) bool { + // Break apart the discriminator into 20-byte chunks and + // see whether any of them match the configured root CA + // fingerprints. If any of them match, we'll return true. + var f [20]byte + for len(b) >= len(f) { + b = b[copy(f[:], b):] + if _, ok := fingerprints[f]; ok { + return true + } + } + return false + } + // Populate our own discriminator with the fingerprints of our + // configured root CAs. + var discriminator multicast.Discriminator + for f := range fingerprints { + discriminator = append(discriminator, f[:]...) + } + options = append(options, matcher) + options = append(options, discriminator) + } if n.multicast, err = multicast.New(n.core, logger, options...); err != nil { panic(err) } diff --git a/src/multicast/advertisement.go b/src/multicast/advertisement.go index 4b65b609..69c29b6a 100644 --- a/src/multicast/advertisement.go +++ b/src/multicast/advertisement.go @@ -7,22 +7,33 @@ import ( ) type multicastAdvertisement struct { - PublicKey ed25519.PublicKey - Port uint16 + MajorVersion uint16 + MinorVersion uint16 + PublicKey ed25519.PublicKey + Port uint16 + Discriminator []byte } func (m *multicastAdvertisement) MarshalBinary() ([]byte, error) { - b := make([]byte, 0, ed25519.PublicKeySize+2) + b := make([]byte, 0, ed25519.PublicKeySize+8+len(m.Discriminator)) + b = binary.BigEndian.AppendUint16(b, m.MajorVersion) + b = binary.BigEndian.AppendUint16(b, m.MinorVersion) b = append(b, m.PublicKey...) b = binary.BigEndian.AppendUint16(b, m.Port) + b = binary.BigEndian.AppendUint16(b, uint16(len(m.Discriminator))) + b = append(b, m.Discriminator...) return b, nil } func (m *multicastAdvertisement) UnmarshalBinary(b []byte) error { - if len(b) < ed25519.PublicKeySize+2 { + if len(b) < ed25519.PublicKeySize+8 { return fmt.Errorf("invalid multicast beacon") } - m.PublicKey = b[:ed25519.PublicKeySize] - m.Port = binary.BigEndian.Uint16(b[ed25519.PublicKeySize:]) + m.MajorVersion = binary.BigEndian.Uint16(b[0:2]) + m.MinorVersion = binary.BigEndian.Uint16(b[2:4]) + m.PublicKey = append(m.PublicKey[:0], b[4:4+ed25519.PublicKeySize]...) + m.Port = binary.BigEndian.Uint16(b[4+ed25519.PublicKeySize : 6+ed25519.PublicKeySize]) + dl := binary.BigEndian.Uint16(b[6+ed25519.PublicKeySize : 8+ed25519.PublicKeySize]) + m.Discriminator = append(m.Discriminator[:0], b[8+ed25519.PublicKeySize:8+ed25519.PublicKeySize+dl]...) return nil } diff --git a/src/multicast/advertisement_test.go b/src/multicast/advertisement_test.go new file mode 100644 index 00000000..7132322d --- /dev/null +++ b/src/multicast/advertisement_test.go @@ -0,0 +1,38 @@ +package multicast + +import ( + "crypto/ed25519" + "reflect" + "testing" +) + +func TestMulticastAdvertisementRoundTrip(t *testing.T) { + pk, sk, err := ed25519.GenerateKey(nil) + if err != nil { + t.Fatal(err) + } + + orig := multicastAdvertisement{ + MajorVersion: 1, + MinorVersion: 2, + PublicKey: pk, + Port: 3, + Discriminator: sk, // any bytes will do + } + + ob, err := orig.MarshalBinary() + if err != nil { + t.Fatal(err) + } + + var new multicastAdvertisement + if err := new.UnmarshalBinary(ob); err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(orig, new) { + t.Logf("original: %+v", orig) + t.Logf("new: %+v", new) + t.Fatalf("differences found after round-trip") + } +} diff --git a/src/multicast/multicast.go b/src/multicast/multicast.go index 9cd67ff1..f58af934 100644 --- a/src/multicast/multicast.go +++ b/src/multicast/multicast.go @@ -1,6 +1,7 @@ package multicast import ( + "bytes" "context" "encoding/hex" "fmt" @@ -30,8 +31,10 @@ type Multicast struct { _interfaces map[string]*interfaceInfo _timer *time.Timer config struct { - _groupAddr GroupAddress - _interfaces map[MulticastInterface]struct{} + _discriminator []byte + _discriminatorMatch func([]byte) bool + _groupAddr GroupAddress + _interfaces map[MulticastInterface]struct{} } } @@ -321,8 +324,11 @@ func (m *Multicast) _announce() { } addr := linfo.listener.Addr().(*net.TCPAddr) adv := multicastAdvertisement{ - PublicKey: m.core.PublicKey(), - Port: uint16(addr.Port), + MajorVersion: core.ProtocolVersionMajor, + MinorVersion: core.ProtocolVersionMinor, + PublicKey: m.core.PublicKey(), + Port: uint16(addr.Port), + Discriminator: m.config._discriminator, } msg, err := adv.MarshalBinary() if err != nil { @@ -373,7 +379,16 @@ func (m *Multicast) listen() { if err := adv.UnmarshalBinary(bs[:n]); err != nil { continue } - if adv.PublicKey.Equal(m.core.PublicKey()) { + switch { + case adv.MajorVersion != core.ProtocolVersionMajor: + continue + case adv.MinorVersion != core.ProtocolVersionMinor: + continue + case adv.PublicKey.Equal(m.core.PublicKey()): + continue + case m.config._discriminatorMatch == nil && !bytes.Equal(adv.Discriminator, m.config._discriminator): + continue + case m.config._discriminatorMatch != nil && !m.config._discriminatorMatch(adv.Discriminator): continue } from := fromAddr.(*net.UDPAddr) diff --git a/src/multicast/options.go b/src/multicast/options.go index f36284ed..aa740605 100644 --- a/src/multicast/options.go +++ b/src/multicast/options.go @@ -8,6 +8,10 @@ func (m *Multicast) _applyOption(opt SetupOption) { m.config._interfaces[v] = struct{}{} case GroupAddress: m.config._groupAddr = v + case Discriminator: + m.config._discriminator = append(m.config._discriminator[:0], v...) + case DiscriminatorMatch: + m.config._discriminatorMatch = v } } @@ -24,6 +28,10 @@ type MulticastInterface struct { } type GroupAddress string +type Discriminator []byte +type DiscriminatorMatch func([]byte) bool func (a MulticastInterface) isSetupOption() {} func (a GroupAddress) isSetupOption() {} +func (a Discriminator) isSetupOption() {} +func (a DiscriminatorMatch) isSetupOption() {} From ff96740ac79567da816587672fd757a1fd9bdeb9 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 15 Jul 2023 20:12:14 +0100 Subject: [PATCH 136/293] Fail to start if no configuration provided --- cmd/yggdrasil/main.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 8de2c868..a770e4e2 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -135,6 +135,7 @@ func main() { if *getaddr || *getsnet { fmt.Println("\nError: You need to specify some config data using -useconf or -useconffile.") } + return } privateKey := ed25519.PrivateKey(cfg.PrivateKey) From 63b214f6b707f1f7ea837d495fa5ae97300b755b Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 15 Jul 2023 22:34:29 +0100 Subject: [PATCH 137/293] Fix negotiating priority on connection --- src/core/link.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/core/link.go b/src/core/link.go index f7afd8b8..1a168041 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -479,6 +479,7 @@ func (l *links) connect(u *url.URL, info linkInfo, options linkOptions) (net.Con func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn) error { meta := version_getBaseMetadata() meta.publicKey = l.core.public + meta.priority = options.priority metaBytes := meta.encode() if err := conn.SetDeadline(time.Now().Add(time.Second * 6)); err != nil { return fmt.Errorf("failed to set handshake deadline: %w", err) @@ -536,10 +537,14 @@ func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn) e remoteAddr := net.IP(address.AddrForKey(meta.publicKey)[:]).String() remoteStr := fmt.Sprintf("%s@%s", remoteAddr, conn.RemoteAddr()) localStr := conn.LocalAddr() + priority := options.priority + if meta.priority > priority { + priority = meta.priority + } l.core.log.Infof("Connected %s: %s, source %s", dir, remoteStr, localStr) - err = l.core.HandleConn(meta.publicKey, conn, options.priority) + err = l.core.HandleConn(meta.publicKey, conn, priority) switch err { case io.EOF, net.ErrClosed, nil: l.core.log.Infof("Disconnected %s: %s, source %s", From fe14981ddabf6628548cac0b002451ff0ab93652 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sat, 5 Aug 2023 04:01:15 -0500 Subject: [PATCH 138/293] update ironwood --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index f8dddc12..0d98a9c4 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/yggdrasil-network/yggdrasil-go go 1.19 require ( - github.com/Arceliar/ironwood v0.0.0-20230521174855-fdfa6326d125 + github.com/Arceliar/ironwood v0.0.0-20230805085300-86206813435f github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d github.com/cheggaaa/pb/v3 v3.0.8 github.com/gologme/log v1.2.0 diff --git a/go.sum b/go.sum index 78da1041..1c8f754e 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Arceliar/ironwood v0.0.0-20230521174855-fdfa6326d125 h1:l2elyrosw63mTqZzwR0Nv8vPZWZC/0Hvwl8Iuva5htM= -github.com/Arceliar/ironwood v0.0.0-20230521174855-fdfa6326d125/go.mod h1:5x7fWW0mshe9WQ1lvSMmmHBYC3BeHH9gpwW5tz7cbfw= +github.com/Arceliar/ironwood v0.0.0-20230805085300-86206813435f h1:Fz0zG7ZyQQqk+ROnmHuGrIZO250Lx/YHmp9o48XE+Vw= +github.com/Arceliar/ironwood v0.0.0-20230805085300-86206813435f/go.mod h1:5x7fWW0mshe9WQ1lvSMmmHBYC3BeHH9gpwW5tz7cbfw= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q= github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= From 5b203ad8c5f78d42e2ade2886f8107c4eeb337b4 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 12 Aug 2023 18:12:58 +0100 Subject: [PATCH 139/293] Use Go 1.21 in CI, update minimum version to Go 1.20, lint fixes, update `quic-go` --- .github/workflows/ci.yml | 8 +++---- go.mod | 19 ++++++++------- go.sum | 46 +++++++++++++++++++------------------ src/address/address_test.go | 6 ++--- src/core/core_test.go | 6 ++--- src/core/link.go | 4 ++-- src/core/link_quic.go | 6 ++--- src/core/link_socks.go | 2 +- src/core/link_tcp.go | 4 ++-- src/core/link_tls.go | 4 ++-- src/core/link_unix.go | 4 ++-- src/core/version_test.go | 4 ++-- src/tun/tun_darwin.go | 4 ++-- 13 files changed, 59 insertions(+), 58 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2f19f8c9..c7cb75f6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: steps: - uses: actions/setup-go@v3 with: - go-version: 1.19 + go-version: 1.21 - uses: actions/checkout@v3 - name: golangci-lint uses: golangci/golangci-lint-action@v3 @@ -51,7 +51,7 @@ jobs: strategy: fail-fast: false matrix: - goversion: ["1.19", "1.20"] + goversion: ["1.20", "1.21"] name: Build & Test (Linux, Go ${{ matrix.goversion }}) needs: [lint] @@ -75,7 +75,7 @@ jobs: strategy: fail-fast: false matrix: - goversion: ["1.19", "1.20"] + goversion: ["1.20", "1.21"] name: Build & Test (Windows, Go ${{ matrix.goversion }}) needs: [lint] @@ -99,7 +99,7 @@ jobs: strategy: fail-fast: false matrix: - goversion: ["1.19", "1.20"] + goversion: ["1.20", "1.21"] name: Build & Test (macOS, Go ${{ matrix.goversion }}) needs: [lint] diff --git a/go.mod b/go.mod index 0d98a9c4..6be4c7ad 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/yggdrasil-network/yggdrasil-go -go 1.19 +go 1.20 require ( github.com/Arceliar/ironwood v0.0.0-20230805085300-86206813435f @@ -10,11 +10,11 @@ require ( github.com/hashicorp/go-syslog v1.0.0 github.com/hjson/hjson-go/v4 v4.3.0 github.com/kardianos/minwinsvc v1.0.2 - github.com/quic-go/quic-go v0.34.0 + github.com/quic-go/quic-go v0.37.4 github.com/vishvananda/netlink v1.1.0 golang.org/x/mobile v0.0.0-20221110043201-43a038452099 - golang.org/x/net v0.9.0 - golang.org/x/sys v0.7.0 + golang.org/x/net v0.10.0 + golang.org/x/sys v0.8.0 golang.org/x/text v0.9.0 golang.zx2c4.com/wireguard v0.0.0-20211017052713-f87e87af0d9a golang.zx2c4.com/wireguard/windows v0.4.12 @@ -23,18 +23,17 @@ require ( require ( github.com/bits-and-blooms/bitset v1.5.0 // indirect github.com/bits-and-blooms/bloom/v3 v3.3.1 // indirect - github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect + github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/golang/mock v1.6.0 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/mattn/go-colorable v0.1.8 // indirect - github.com/onsi/ginkgo/v2 v2.2.0 // indirect - github.com/quic-go/qtls-go1-19 v0.3.2 // indirect - github.com/quic-go/qtls-go1-20 v0.2.2 // indirect + github.com/onsi/ginkgo/v2 v2.9.5 // indirect + github.com/quic-go/qtls-go1-20 v0.3.1 // indirect github.com/rivo/uniseg v0.2.0 // indirect golang.org/x/crypto v0.8.0 // indirect golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect - golang.org/x/mod v0.8.0 // indirect - golang.org/x/tools v0.6.0 // indirect + golang.org/x/mod v0.10.0 // indirect + golang.org/x/tools v0.9.1 // indirect ) require ( diff --git a/go.sum b/go.sum index 1c8f754e..e9446785 100644 --- a/go.sum +++ b/go.sum @@ -21,14 +21,15 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/gologme/log v1.2.0 h1:Ya5Ip/KD6FX7uH0S31QO87nCCSucKtF44TLbTtO7V4c= github.com/gologme/log v1.2.0/go.mod h1:gq31gQ8wEHkR+WekdWsqDuf8pXTUZA9BnnzTuPz1Y9U= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= @@ -49,23 +50,21 @@ github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4 github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI= -github.com/onsi/ginkgo/v2 v2.2.0/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= -github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q= +github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= +github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= +github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/quic-go/qtls-go1-19 v0.3.2 h1:tFxjCFcTQzK+oMxG6Zcvp4Dq8dx4yD3dDiIiyc86Z5U= -github.com/quic-go/qtls-go1-19 v0.3.2/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI= -github.com/quic-go/qtls-go1-20 v0.2.2 h1:WLOPx6OY/hxtTxKV1Zrq20FtXtDEkeY00CGQm8GEa3E= -github.com/quic-go/qtls-go1-20 v0.2.2/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM= -github.com/quic-go/quic-go v0.34.0 h1:OvOJ9LFjTySgwOTYUZmNoq0FzVicP8YujpV0kB7m2lU= -github.com/quic-go/quic-go v0.34.0/go.mod h1:+4CVgVppm0FNjpG3UcX8Joi/frKOH7/ciD5yGcwOO1g= +github.com/quic-go/qtls-go1-20 v0.3.1 h1:O4BLOM3hwfVF3AcktIylQXyl7Yi2iBNVy5QsV+ySxbg= +github.com/quic-go/qtls-go1-20 v0.3.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= +github.com/quic-go/quic-go v0.37.4 h1:ke8B73yMCWGq9MfrCCAw0Uzdm7GaViC3i39dsIdDlH4= +github.com/quic-go/quic-go v0.37.4/go.mod h1:YsbH1r4mSHPJcLF4k4zruUkLBqctEMBDR6VPvcYjIsU= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= @@ -86,21 +85,23 @@ golang.org/x/mobile v0.0.0-20221110043201-43a038452099 h1:aIu0lKmfdgtn2uTj7JI2oN golang.org/x/mobile v0.0.0-20221110043201-43a038452099/go.mod h1:aAjjkJNdrh3PMckS4B10TGS2nag27cbKR1y2BpUxsiY= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= +golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -116,8 +117,9 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -132,8 +134,9 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= +golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -143,6 +146,5 @@ golang.zx2c4.com/wireguard/windows v0.4.12 h1:CUmbdWKVNzTSsVb4yUAiEwL3KsabdJkEPd golang.zx2c4.com/wireguard/windows v0.4.12/go.mod h1:PW4y+d9oY83XU9rRwRwrJDwEMuhVjMxu2gfD1cfzS7w= google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/src/address/address_test.go b/src/address/address_test.go index a7939e0f..5aafd5a4 100644 --- a/src/address/address_test.go +++ b/src/address/address_test.go @@ -3,13 +3,13 @@ package address import ( "bytes" "crypto/ed25519" - "math/rand" + "crypto/rand" "testing" ) func TestAddress_Address_IsValid(t *testing.T) { var address Address - rand.Read(address[:]) + _, _ = rand.Read(address[:]) address[0] = 0 @@ -32,7 +32,7 @@ func TestAddress_Address_IsValid(t *testing.T) { func TestAddress_Subnet_IsValid(t *testing.T) { var subnet Subnet - rand.Read(subnet[:]) + _, _ = rand.Read(subnet[:]) subnet[0] = 0 diff --git a/src/core/core_test.go b/src/core/core_test.go index c38a750a..cece33c2 100644 --- a/src/core/core_test.go +++ b/src/core/core_test.go @@ -2,7 +2,7 @@ package core import ( "bytes" - "math/rand" + "crypto/rand" "net/url" "os" "testing" @@ -146,7 +146,7 @@ func TestCore_Start_Transfer(t *testing.T) { // Send msg := make([]byte, msgLen) - rand.Read(msg[40:]) + _, _ = rand.Read(msg[40:]) msg[0] = 0x60 copy(msg[8:24], nodeB.Address()) copy(msg[24:40], nodeA.Address()) @@ -178,7 +178,7 @@ func BenchmarkCore_Start_Transfer(b *testing.B) { // Send msg := make([]byte, msgLen) - rand.Read(msg[40:]) + _, _ = rand.Read(msg[40:]) msg[0] = 0x60 copy(msg[8:24], nodeB.Address()) copy(msg[24:40], nodeA.Address()) diff --git a/src/core/link.go b/src/core/link.go index 1a168041..e5911b13 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -40,7 +40,7 @@ type links struct { } type linkProtocol interface { - dial(url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) + dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) listen(ctx context.Context, url *url.URL, sintf string) (net.Listener, error) } @@ -473,7 +473,7 @@ func (l *links) connect(u *url.URL, info linkInfo, options linkOptions) (net.Con default: return nil, ErrLinkUnrecognisedSchema } - return dialer.dial(u, info, options) + return dialer.dial(l.core.ctx, u, info, options) } func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn) error { diff --git a/src/core/link_quic.go b/src/core/link_quic.go index 9745fb14..976aba6a 100644 --- a/src/core/link_quic.go +++ b/src/core/link_quic.go @@ -24,7 +24,7 @@ type linkQUICStream struct { } type linkQUICListener struct { - quic.EarlyListener + *quic.EarlyListener ch <-chan *linkQUICStream } @@ -49,8 +49,8 @@ func (l *links) newLinkQUIC() *linkQUIC { return lt } -func (l *linkQUIC) dial(url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) { - qc, err := quic.DialAddrEarly(url.Host, l.tlsconfig, l.quicconfig) +func (l *linkQUIC) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) { + qc, err := quic.DialAddrEarly(ctx, url.Host, l.tlsconfig, l.quicconfig) if err != nil { return nil, err } diff --git a/src/core/link_socks.go b/src/core/link_socks.go index 538394e5..1a038fa6 100644 --- a/src/core/link_socks.go +++ b/src/core/link_socks.go @@ -21,7 +21,7 @@ func (l *links) newLinkSOCKS() *linkSOCKS { return lt } -func (l *linkSOCKS) dial(url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) { +func (l *linkSOCKS) dial(_ context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) { var proxyAuth *proxy.Auth if url.User != nil && url.User.Username() != "" { proxyAuth = &proxy.Auth{ diff --git a/src/core/link_tcp.go b/src/core/link_tcp.go index 8ffb3124..889051b9 100644 --- a/src/core/link_tcp.go +++ b/src/core/link_tcp.go @@ -68,7 +68,7 @@ func (l *linkTCP) dialersFor(url *url.URL, info linkInfo) ([]*tcpDialer, error) return dialers, nil } -func (l *linkTCP) dial(url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) { +func (l *linkTCP) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) { if l.core.isTLSOnly() { return nil, fmt.Errorf("TCP peer prohibited in TLS-only mode") } @@ -81,7 +81,7 @@ func (l *linkTCP) dial(url *url.URL, info linkInfo, options linkOptions) (net.Co } for _, d := range dialers { var conn net.Conn - conn, err = d.dialer.DialContext(l.core.ctx, "tcp", d.addr.String()) + conn, err = d.dialer.DialContext(ctx, "tcp", d.addr.String()) if err != nil { l.core.log.Warnf("Failed to connect to %s: %s", d.addr, err) continue diff --git a/src/core/link_tls.go b/src/core/link_tls.go index b962d529..a93227f6 100644 --- a/src/core/link_tls.go +++ b/src/core/link_tls.go @@ -33,7 +33,7 @@ func (l *links) newLinkTLS(tcp *linkTCP) *linkTLS { return lt } -func (l *linkTLS) dial(url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) { +func (l *linkTLS) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) { dialers, err := l.tcp.dialersFor(url, info) if err != nil { return nil, err @@ -49,7 +49,7 @@ func (l *linkTLS) dial(url *url.URL, info linkInfo, options linkOptions) (net.Co Config: tlsconfig, } var conn net.Conn - conn, err = tlsdialer.DialContext(l.core.ctx, "tcp", d.addr.String()) + conn, err = tlsdialer.DialContext(ctx, "tcp", d.addr.String()) if err != nil { continue } diff --git a/src/core/link_unix.go b/src/core/link_unix.go index 501689ad..8dde8946 100644 --- a/src/core/link_unix.go +++ b/src/core/link_unix.go @@ -32,12 +32,12 @@ func (l *links) newLinkUNIX() *linkUNIX { return lt } -func (l *linkUNIX) dial(url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) { +func (l *linkUNIX) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) { addr, err := net.ResolveUnixAddr("unix", url.Path) if err != nil { return nil, err } - return l.dialer.DialContext(l.core.ctx, "unix", addr.String()) + return l.dialer.DialContext(ctx, "unix", addr.String()) } func (l *linkUNIX) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) { diff --git a/src/core/version_test.go b/src/core/version_test.go index 511cb358..1c1f673a 100644 --- a/src/core/version_test.go +++ b/src/core/version_test.go @@ -3,7 +3,7 @@ package core import ( "bytes" "crypto/ed25519" - "math/rand" + "crypto/rand" "reflect" "testing" ) @@ -21,7 +21,7 @@ func TestVersionRoundtrip(t *testing.T) { // Generate a random public key for each time, since it is // a required field. test.publicKey = make(ed25519.PublicKey, ed25519.PublicKeySize) - rand.Read(test.publicKey) + _, _ = rand.Read(test.publicKey) encoded := bytes.NewBuffer(test.encode()) decoded := &version_metadata{} diff --git a/src/tun/tun_darwin.go b/src/tun/tun_darwin.go index a6d87a0c..29cfe959 100644 --- a/src/tun/tun_darwin.go +++ b/src/tun/tun_darwin.go @@ -117,13 +117,13 @@ func (tun *TunAdapter) setupAddress(addr string) error { tun.log.Infof("Interface IPv6: %s", addr) tun.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 { + if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(darwin_SIOCAIFADDR_IN6), uintptr(unsafe.Pointer(&ar))); errno != 0 { // nolint:staticcheck err = errno tun.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 { + if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(unix.SIOCSIFMTU), uintptr(unsafe.Pointer(&ir))); errno != 0 { // nolint:staticcheck err = errno tun.log.Errorf("Error in SIOCSIFMTU: %v", errno) return err From fbc5f62add6ea6227cd8d904b3e3791041b9983f Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 17 Aug 2023 14:08:03 +0100 Subject: [PATCH 140/293] Fix missing `setupFD` stubs --- src/tun/tun_bsd.go | 6 ++++++ src/tun/tun_linux.go | 7 +++++++ src/tun/tun_other.go | 7 +++++++ src/tun/tun_windows.go | 6 ++++++ 4 files changed, 26 insertions(+) diff --git a/src/tun/tun_bsd.go b/src/tun/tun_bsd.go index 9a8f70ce..3910ccef 100644 --- a/src/tun/tun_bsd.go +++ b/src/tun/tun_bsd.go @@ -5,6 +5,7 @@ package tun import ( "encoding/binary" + "fmt" "os/exec" "strconv" "strings" @@ -88,6 +89,11 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error { return tun.setupAddress(addr) } +// Configures the "utun" adapter from an existing file descriptor. +func (tun *TunAdapter) setupFD(fd int32, addr string, mtu uint64) error { + return fmt.Errorf("setup via FD not supported on this platform") +} + func (tun *TunAdapter) setupAddress(addr string) error { var sfd int var err error diff --git a/src/tun/tun_linux.go b/src/tun/tun_linux.go index 1e42b7b8..16deb8e5 100644 --- a/src/tun/tun_linux.go +++ b/src/tun/tun_linux.go @@ -6,6 +6,8 @@ package tun // The linux platform specific tun parts import ( + "fmt" + "github.com/vishvananda/netlink" wgtun "golang.zx2c4.com/wireguard/tun" ) @@ -28,6 +30,11 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error { return tun.setupAddress(addr) } +// Configures the "utun" adapter from an existing file descriptor. +func (tun *TunAdapter) setupFD(fd int32, addr string, mtu uint64) error { + return fmt.Errorf("setup via FD not supported on this platform") +} + // Configures the TUN adapter with the correct IPv6 address and MTU. Netlink // is used to do this, so there is not a hard requirement on "ip" or "ifconfig" // to exist on the system, but this will fail if Netlink is not present in the diff --git a/src/tun/tun_other.go b/src/tun/tun_other.go index c618d837..dd33708e 100644 --- a/src/tun/tun_other.go +++ b/src/tun/tun_other.go @@ -7,6 +7,8 @@ package tun // If your platform supports tun devices, you could try configuring it manually import ( + "fmt" + wgtun "golang.zx2c4.com/wireguard/tun" ) @@ -25,6 +27,11 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error { return tun.setupAddress(addr) } +// Configures the "utun" adapter from an existing file descriptor. +func (tun *TunAdapter) setupFD(fd int32, addr string, mtu uint64) error { + return fmt.Errorf("setup via FD not supported on this platform") +} + // 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 { diff --git a/src/tun/tun_windows.go b/src/tun/tun_windows.go index c3e36596..2713f991 100644 --- a/src/tun/tun_windows.go +++ b/src/tun/tun_windows.go @@ -6,6 +6,7 @@ package tun import ( "bytes" "errors" + "fmt" "log" "net" @@ -50,6 +51,11 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error { }) } +// Configures the "utun" adapter from an existing file descriptor. +func (tun *TunAdapter) setupFD(fd int32, addr string, mtu uint64) error { + return fmt.Errorf("setup via FD not supported on this platform") +} + // Sets the MTU of the TUN adapter. func (tun *TunAdapter) setupMTU(mtu uint64) error { if tun.iface == nil || tun.Name() == "" { From 12a3a8c73bc4ce2bd27d74734af17fc559f435e0 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 3 Sep 2023 13:08:13 +0100 Subject: [PATCH 141/293] Fix build tags for `setupFD` --- src/tun/tun_linux.go | 4 ++-- src/tun/tun_other.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tun/tun_linux.go b/src/tun/tun_linux.go index 16deb8e5..d87552b7 100644 --- a/src/tun/tun_linux.go +++ b/src/tun/tun_linux.go @@ -1,5 +1,5 @@ -//go:build !mobile -// +build !mobile +//go:build linux || android +// +build linux android package tun diff --git a/src/tun/tun_other.go b/src/tun/tun_other.go index dd33708e..075ccfe1 100644 --- a/src/tun/tun_other.go +++ b/src/tun/tun_other.go @@ -1,5 +1,5 @@ -//go:build !linux && !darwin && !windows && !openbsd && !freebsd && !mobile -// +build !linux,!darwin,!windows,!openbsd,!freebsd,!mobile +//go:build !linux && !darwin && !ios && !android && !windows && !openbsd && !freebsd && !mobile +// +build !linux,!darwin,!ios,!android,!windows,!openbsd,!freebsd,!mobile package tun From c8b9aaeb67b051668bb1ef302891698d7fcc788a Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 3 Sep 2023 13:13:49 +0100 Subject: [PATCH 142/293] Only set mobile memory limit on supported Go versions --- contrib/mobile/mobile.go | 3 +-- contrib/mobile/mobile_mem_go120.go | 10 ++++++++++ contrib/mobile/mobile_mem_other.go | 8 ++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 contrib/mobile/mobile_mem_go120.go create mode 100644 contrib/mobile/mobile_mem_other.go diff --git a/contrib/mobile/mobile.go b/contrib/mobile/mobile.go index f4f8c22f..85ff63f1 100644 --- a/contrib/mobile/mobile.go +++ b/contrib/mobile/mobile.go @@ -6,7 +6,6 @@ import ( "fmt" "net" "regexp" - "runtime/debug" "github.com/gologme/log" @@ -45,7 +44,7 @@ func (m *Yggdrasil) StartAutoconfigure() error { // StartJSON starts a node with the given JSON config. You can get JSON config // (rather than HJSON) by using the GenerateConfigJSON() function func (m *Yggdrasil) StartJSON(configjson []byte) error { - debug.SetMemoryLimit(1024 * 1024 * 40) + setMemLimitIfPossible() m.logger = log.New(m.log, "", 0) m.logger.EnableLevel("error") diff --git a/contrib/mobile/mobile_mem_go120.go b/contrib/mobile/mobile_mem_go120.go new file mode 100644 index 00000000..853f1aba --- /dev/null +++ b/contrib/mobile/mobile_mem_go120.go @@ -0,0 +1,10 @@ +//go:build go1.20 +// +build go1.20 + +package mobile + +import "runtime/debug" + +func setMemLimitIfPossible() { + debug.SetMemoryLimit(1024 * 1024 * 40) +} diff --git a/contrib/mobile/mobile_mem_other.go b/contrib/mobile/mobile_mem_other.go new file mode 100644 index 00000000..729d9c2a --- /dev/null +++ b/contrib/mobile/mobile_mem_other.go @@ -0,0 +1,8 @@ +//go:build !go1.20 +// +build !go1.20 + +package mobile + +func setMemLimitIfPossible() { + // not supported by this Go version +} From fa3d943ba90f168d3cc591c276b944f1eeda1344 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 3 Sep 2023 13:30:41 +0100 Subject: [PATCH 143/293] Don't set BBR for TCP peerings --- src/core/link_tcp_linux.go | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/core/link_tcp_linux.go b/src/core/link_tcp_linux.go index 9e875fee..6c54f30b 100644 --- a/src/core/link_tcp_linux.go +++ b/src/core/link_tcp_linux.go @@ -12,22 +12,6 @@ import ( // WARNING: This context is used both by net.Dialer and net.Listen in tcp.go func (t *linkTCP) tcpContext(network, address string, c syscall.RawConn) error { - var control error - var bbr error - - control = c.Control(func(fd uintptr) { - bbr = unix.SetsockoptString(int(fd), unix.IPPROTO_TCP, unix.TCP_CONGESTION, "bbr") - }) - - // Log any errors - if bbr != nil { - t.links.core.log.Debugln("Failed to set tcp_congestion_control to bbr for socket, SetsockoptString error:", bbr) - } - if control != nil { - t.links.core.log.Debugln("Failed to set tcp_congestion_control to bbr for socket, Control error:", control) - } - - // Return nil because errors here are not considered fatal for the connection, it just means congestion control is suboptimal return nil } From 68d1036de861c2cdef3bfc55ef187450d138f211 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 3 Sep 2023 13:30:46 +0100 Subject: [PATCH 144/293] Fix mobile unit test --- contrib/mobile/mobile_test.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/contrib/mobile/mobile_test.go b/contrib/mobile/mobile_test.go index 880e5fc5..74689294 100644 --- a/contrib/mobile/mobile_test.go +++ b/contrib/mobile/mobile_test.go @@ -1,9 +1,21 @@ package mobile -import "testing" +import ( + "os" + "testing" + + "github.com/gologme/log" +) func TestStartYggdrasil(t *testing.T) { - ygg := &Yggdrasil{} + logger := log.New(os.Stdout, "", 0) + logger.EnableLevel("error") + logger.EnableLevel("warn") + logger.EnableLevel("info") + + ygg := &Yggdrasil{ + logger: logger, + } if err := ygg.StartAutoconfigure(); err != nil { t.Fatalf("Failed to start Yggdrasil: %s", err) } From 991ea8b876b7eb48a738015b1ffd97a60cf06ef9 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 3 Sep 2023 13:32:15 +0100 Subject: [PATCH 145/293] Fix codefactor suggestion --- src/core/link.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/core/link.go b/src/core/link.go index e5911b13..3b07e1db 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -308,9 +308,8 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error { if linkType == linkTypePersistent { if backoffNow() { continue - } else { - return } + return } else { break } From 490c11c29e54aca9a639b1a4243308a2b611efc5 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 3 Sep 2023 13:49:21 +0100 Subject: [PATCH 146/293] Fix more codefactor suggestions --- src/core/link.go | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/core/link.go b/src/core/link.go index 3b07e1db..9e0b15fd 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -251,15 +251,13 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error { // the next connection. if backoffNow() { continue - } else { - return } - } else { - // Ephemeral and incoming connections don't remain - // after a connection failure, so exit out of the - // loop and clean up the link entry. - break + return } + // Ephemeral and incoming connections don't remain + // after a connection failure, so exit out of the + // loop and clean up the link entry. + break } // The linkConn wrapper allows us to track the number of @@ -310,9 +308,8 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error { continue } return - } else { - break } + break } }() }) From 268ffbfd14baed7503e7be7cccc3cf19a6e0f7f5 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 9 Oct 2023 16:44:07 +0100 Subject: [PATCH 147/293] Add authenticated handshake, support for passwords --- src/core/link.go | 23 ++++++++++++++++-- src/core/version.go | 41 ++++++++++++++++++++++++++++---- src/core/version_test.go | 50 ++++++++++++++++++++++++---------------- 3 files changed, 88 insertions(+), 26 deletions(-) diff --git a/src/core/link.go b/src/core/link.go index 9e0b15fd..bc4f191b 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -17,6 +17,7 @@ import ( "github.com/Arceliar/phony" "github.com/yggdrasil-network/yggdrasil-go/src/address" + "golang.org/x/crypto/blake2b" ) type linkType int @@ -65,6 +66,7 @@ type linkOptions struct { pinnedEd25519Keys map[keyArray]struct{} priority uint8 tlsSNI string + password []byte } type Listener struct { @@ -129,6 +131,7 @@ func (e linkError) Error() string { return string(e) } const ErrLinkAlreadyConfigured = linkError("peer is already configured") const ErrLinkPriorityInvalid = linkError("priority value is invalid") const ErrLinkPinnedKeyInvalid = linkError("pinned public key is invalid") +const ErrLinkPasswordInvalid = linkError("password is invalid") const ErrLinkUnrecognisedSchema = linkError("link schema unknown") func (l *links) add(u *url.URL, sintf string, linkType linkType) error { @@ -166,6 +169,13 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error { } options.priority = uint8(pi) } + if p := u.Query().Get("password"); p != "" { + if len(p) > blake2b.Size { + retErr = ErrLinkPasswordInvalid + return + } + options.password = []byte(p) + } // If we think we're already connected to this peer, load up // the existing peer state. Try to kick the peer if possible, @@ -351,6 +361,12 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { } options.priority = uint8(pi) } + if p := u.Query().Get("password"); p != "" { + if len(p) > blake2b.Size { + return nil, ErrLinkPasswordInvalid + } + options.password = []byte(p) + } go func() { l.core.log.Printf("%s listener started on %s", strings.ToUpper(u.Scheme), listener.Addr()) @@ -476,7 +492,10 @@ func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn) e meta := version_getBaseMetadata() meta.publicKey = l.core.public meta.priority = options.priority - metaBytes := meta.encode() + metaBytes, err := meta.encode(l.core.secret, options.password) + if err != nil { + return fmt.Errorf("failed to generate handshake: %w", err) + } if err := conn.SetDeadline(time.Now().Add(time.Second * 6)); err != nil { return fmt.Errorf("failed to set handshake deadline: %w", err) } @@ -489,7 +508,7 @@ func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn) e } meta = version_metadata{} base := version_getBaseMetadata() - if !meta.decode(conn) { + if !meta.decode(conn, options.password) { return conn.Close() } if !meta.check() { diff --git a/src/core/version.go b/src/core/version.go index 0820fbdd..e01fe10c 100644 --- a/src/core/version.go +++ b/src/core/version.go @@ -8,7 +8,10 @@ import ( "bytes" "crypto/ed25519" "encoding/binary" + "fmt" "io" + + "golang.org/x/crypto/blake2b" ) // This is the version-specific metadata exchanged at the start of a connection. @@ -26,6 +29,8 @@ const ( ProtocolVersionMinor uint16 = 5 ) +// Once a major/minor version is released, it is not safe to change any of these +// (including their ordering), it is only safe to add new ones. const ( metaVersionMajor uint16 = iota // uint16 metaVersionMinor // uint16 @@ -42,7 +47,7 @@ func version_getBaseMetadata() version_metadata { } // Encodes version metadata into its wire format. -func (m *version_metadata) encode() []byte { +func (m *version_metadata) encode(privateKey ed25519.PrivateKey, password []byte) ([]byte, error) { bs := make([]byte, 0, 64) bs = append(bs, 'm', 'e', 't', 'a') bs = append(bs, 0, 0) // Remaining message length @@ -63,12 +68,26 @@ func (m *version_metadata) encode() []byte { bs = binary.BigEndian.AppendUint16(bs, 1) bs = append(bs, m.priority) + hasher, err := blake2b.New512(password) + if err != nil { + return nil, err + } + n, err := hasher.Write(m.publicKey) + if err != nil { + return nil, err + } + if n != ed25519.PublicKeySize { + return nil, fmt.Errorf("hash writer only wrote %d bytes", n) + } + hash := hasher.Sum(nil) + bs = append(bs, ed25519.Sign(privateKey, hash)...) + binary.BigEndian.PutUint16(bs[4:6], uint16(len(bs)-6)) - return bs + return bs, nil } // Decodes version metadata from its wire format into the struct. -func (m *version_metadata) decode(r io.Reader) bool { +func (m *version_metadata) decode(r io.Reader, password []byte) bool { bh := [6]byte{} if _, err := io.ReadFull(r, bh[:]); err != nil { return false @@ -81,6 +100,10 @@ func (m *version_metadata) decode(r io.Reader) bool { if _, err := io.ReadFull(r, bs); err != nil { return false } + + sig := bs[len(bs)-ed25519.SignatureSize:] + bs = bs[:len(bs)-ed25519.SignatureSize] + for len(bs) >= 4 { op := binary.BigEndian.Uint16(bs[:2]) oplen := binary.BigEndian.Uint16(bs[2:4]) @@ -103,7 +126,17 @@ func (m *version_metadata) decode(r io.Reader) bool { } bs = bs[oplen:] } - return true + + hasher, err := blake2b.New512(password) + if err != nil { + return false + } + n, err := hasher.Write(m.publicKey) + if err != nil || n != ed25519.PublicKeySize { + return false + } + hash := hasher.Sum(nil) + return ed25519.Verify(m.publicKey, hash, sig) } // Checks that the "meta" bytes and the version numbers are the expected values. diff --git a/src/core/version_test.go b/src/core/version_test.go index 1c1f673a..b71010fb 100644 --- a/src/core/version_test.go +++ b/src/core/version_test.go @@ -3,33 +3,43 @@ package core import ( "bytes" "crypto/ed25519" - "crypto/rand" "reflect" "testing" ) func TestVersionRoundtrip(t *testing.T) { - for _, test := range []*version_metadata{ - {majorVer: 1}, - {majorVer: 256}, - {majorVer: 2, minorVer: 4}, - {majorVer: 2, minorVer: 257}, - {majorVer: 258, minorVer: 259}, - {majorVer: 3, minorVer: 5, priority: 6}, - {majorVer: 260, minorVer: 261, priority: 7}, + for _, password := range [][]byte{ + nil, []byte(""), []byte("foo"), } { - // Generate a random public key for each time, since it is - // a required field. - test.publicKey = make(ed25519.PublicKey, ed25519.PublicKeySize) - _, _ = rand.Read(test.publicKey) + for _, test := range []*version_metadata{ + {majorVer: 1}, + {majorVer: 256}, + {majorVer: 2, minorVer: 4}, + {majorVer: 2, minorVer: 257}, + {majorVer: 258, minorVer: 259}, + {majorVer: 3, minorVer: 5, priority: 6}, + {majorVer: 260, minorVer: 261, priority: 7}, + } { + // Generate a random public key for each time, since it is + // a required field. + pk, sk, err := ed25519.GenerateKey(nil) + if err != nil { + t.Fatal(err) + } - encoded := bytes.NewBuffer(test.encode()) - decoded := &version_metadata{} - if !decoded.decode(encoded) { - t.Fatalf("failed to decode") - } - if !reflect.DeepEqual(test, decoded) { - t.Fatalf("round-trip failed\nwant: %+v\n got: %+v", test, decoded) + test.publicKey = pk + meta, err := test.encode(sk, password) + if err != nil { + t.Fatal(err) + } + encoded := bytes.NewBuffer(meta) + decoded := &version_metadata{} + if !decoded.decode(encoded, password) { + t.Fatalf("failed to decode") + } + if !reflect.DeepEqual(test, decoded) { + t.Fatalf("round-trip failed\nwant: %+v\n got: %+v", test, decoded) + } } } } From bd7e699130ef0c647eaec30f2c5fc8c5c55a7b14 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 9 Oct 2023 22:28:20 +0100 Subject: [PATCH 148/293] Add unit test for password auth --- src/core/version_test.go | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/core/version_test.go b/src/core/version_test.go index b71010fb..7efe2f35 100644 --- a/src/core/version_test.go +++ b/src/core/version_test.go @@ -7,6 +7,39 @@ import ( "testing" ) +func TestVersionPasswordAuth(t *testing.T) { + for _, tt := range []struct { + password1 []byte // The password on node 1 + password2 []byte // The password on node 2 + allowed bool // Should the connection have been allowed? + }{ + {nil, nil, true}, // Allow: No passwords (both nil) + {nil, []byte(""), true}, // Allow: No passwords (mixed nil and empty string) + {nil, []byte("foo"), false}, // Reject: One node has a password, the other doesn't + {[]byte("foo"), []byte(""), false}, // Reject: One node has a password, the other doesn't + {[]byte("foo"), []byte("foo"), true}, // Allow: Same password + {[]byte("foo"), []byte("bar"), false}, // Reject: Different passwords + } { + pk1, sk1, err := ed25519.GenerateKey(nil) + if err != nil { + t.Fatalf("Node 1 failed to generate key: %s", err) + } + + metadata1 := &version_metadata{ + publicKey: pk1, + } + encoded, err := metadata1.encode(sk1, tt.password1) + if err != nil { + t.Fatalf("Node 1 failed to encode metadata: %s", err) + } + + var decoded version_metadata + if allowed := decoded.decode(bytes.NewBuffer(encoded), tt.password2); allowed != tt.allowed { + t.Fatalf("Permutation %q -> %q should have been %v but was %v", tt.password1, tt.password2, tt.allowed, allowed) + } + } +} + func TestVersionRoundtrip(t *testing.T) { for _, password := range [][]byte{ nil, []byte(""), []byte("foo"), From 45b773eade3b10e45c532671550cce244b559090 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 11 Oct 2023 18:25:35 +0100 Subject: [PATCH 149/293] Remove TLS root validation This is just too complicated compared to the per-peer/per-listener/per-interface password approach. --- cmd/yggdrasil/main.go | 50 -------------------- contrib/mobile/mobile.go | 7 +-- src/config/config.go | 100 +++++---------------------------------- src/core/core.go | 11 +---- src/core/link_tcp.go | 6 --- src/core/options.go | 9 ---- src/core/tls.go | 42 +++++----------- 7 files changed, 29 insertions(+), 196 deletions(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index a770e4e2..09d2f315 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -3,7 +3,6 @@ package main import ( "context" "crypto/ed25519" - "crypto/sha1" "encoding/hex" "encoding/json" "flag" @@ -45,8 +44,6 @@ func main() { useconffile := flag.String("useconffile", "", "read HJSON/JSON config from specified file path") normaliseconf := flag.Bool("normaliseconf", false, "use in combination with either -useconf or -useconffile, outputs your configuration normalised") exportkey := flag.Bool("exportkey", false, "use in combination with either -useconf or -useconffile, outputs your private key in PEM format") - exportcsr := flag.Bool("exportcsr", false, "use in combination with either -useconf or -useconffile, outputs your self-signed certificate request in PEM format") - exportcert := flag.Bool("exportcert", false, "use in combination with either -useconf or -useconffile, outputs your self-signed certificate in PEM format") 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") @@ -177,30 +174,10 @@ func main() { } fmt.Println(string(pem)) return - - case *exportcsr: - pem, err := cfg.GenerateCertificateSigningRequest() - if err != nil { - panic(err) - } - fmt.Println(string(pem)) - return - - case *exportcert: - pem, err := cfg.MarshalPEMCertificate() - if err != nil { - panic(err) - } - fmt.Println(string(pem)) - return } n := &node{} - // Track certificate fingerprints for configured roots, so - // that we can match them using the multicast discriminator. - fingerprints := map[[20]byte]struct{}{} - // Setup the Yggdrasil node itself. { options := []core.SetupOption{ @@ -218,10 +195,6 @@ func main() { options = append(options, core.Peer{URI: peer, SourceInterface: intf}) } } - for _, root := range cfg.RootCertificates { - options = append(options, core.RootCertificate(*root)) - fingerprints[sha1.Sum(root.Raw[:])] = struct{}{} - } for _, allowed := range cfg.AllowedPublicKeys { k, err := hex.DecodeString(allowed) if err != nil { @@ -259,29 +232,6 @@ func main() { Priority: uint8(intf.Priority), }) } - if len(fingerprints) > 0 { - var matcher multicast.DiscriminatorMatch = func(b []byte) bool { - // Break apart the discriminator into 20-byte chunks and - // see whether any of them match the configured root CA - // fingerprints. If any of them match, we'll return true. - var f [20]byte - for len(b) >= len(f) { - b = b[copy(f[:], b):] - if _, ok := fingerprints[f]; ok { - return true - } - } - return false - } - // Populate our own discriminator with the fingerprints of our - // configured root CAs. - var discriminator multicast.Discriminator - for f := range fingerprints { - discriminator = append(discriminator, f[:]...) - } - options = append(options, matcher) - options = append(options, discriminator) - } if n.multicast, err = multicast.New(n.core, logger, options...); err != nil { panic(err) } diff --git a/contrib/mobile/mobile.go b/contrib/mobile/mobile.go index 6ceccb30..be1f5ff7 100644 --- a/contrib/mobile/mobile.go +++ b/contrib/mobile/mobile.go @@ -42,8 +42,8 @@ func (m *Yggdrasil) StartAutoconfigure() error { // StartJSON starts a node with the given JSON config. You can get JSON config // (rather than HJSON) by using the GenerateConfigJSON() function func (m *Yggdrasil) StartJSON(configjson []byte) error { - setMemLimitIfPossible() - + setMemLimitIfPossible() + logger := log.New(m.log, "", 0) logger.EnableLevel("error") logger.EnableLevel("warn") @@ -70,9 +70,6 @@ func (m *Yggdrasil) StartJSON(configjson []byte) error { } options = append(options, core.AllowedPublicKey(k[:])) } - for _, root := range m.config.RootCertificates { - options = append(options, core.RootCertificate(*root)) - } var err error m.core, err = core.New(m.config.Certificate, logger, options...) if err != nil { diff --git a/src/config/config.go b/src/config/config.go index fe55e827..e818f703 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -40,22 +40,19 @@ import ( // options that are necessary for an Yggdrasil node to run. You will need to // supply one of these structs to the Yggdrasil core when starting a node. type NodeConfig struct { - PrivateKey KeyBytes `comment:"Your private key. DO NOT share this with anyone!"` - PrivateKeyPath string `json:",omitempty"` - Certificate *tls.Certificate `json:"-"` - CertificatePath string `json:",omitempty"` - RootCertificates []*x509.Certificate `json:"-"` - RootCertificatePaths []string `json:",omitempty"` - Peers []string `comment:"List of connection strings for outbound peer connections in URI format,\ne.g. tls://a.b.c.d:e or socks://a.b.c.d:e/f.g.h.i:j. These connections\nwill obey the operating system routing table, therefore you should\nuse this section when you may connect via different interfaces."` - InterfacePeers map[string][]string `comment:"List of connection strings for outbound peer connections in URI format,\narranged by source interface, e.g. { \"eth0\": [ \"tls://a.b.c.d:e\" ] }.\nNote that SOCKS peerings will NOT be affected by this option and should\ngo in the \"Peers\" section instead."` - Listen []string `comment:"Listen addresses for incoming connections. You will need to add\nlisteners in order to accept incoming peerings from non-local nodes.\nMulticast peer discovery will work regardless of any listeners set\nhere. Each listener should be specified in URI format as above, e.g.\ntls://0.0.0.0:0 or tls://[::]:0 to listen on all interfaces."` - 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."` - MulticastInterfaces []MulticastInterfaceConfig `comment:"Configuration for which interfaces multicast peer discovery should be\nenabled on. Each entry in the list should be a json object which may\ncontain Regex, Beacon, Listen, and Port. Regex is a regular expression\nwhich is matched against an interface name, and interfaces use the\nfirst configuration that they match gainst. Beacon configures whether\nor not the node should send link-local multicast beacons to advertise\ntheir presence, while listening for incoming connections on Port.\nListen controls whether or not the node listens for multicast beacons\nand opens outgoing connections."` - AllowedPublicKeys []string `comment:"List of peer public keys to allow incoming peering connections\nfrom. If left empty/undefined then all connections will be allowed\nby default. This does not affect outgoing peerings, nor does it\naffect link-local peers discovered via multicast."` - IfName string `comment:"Local network interface name for TUN adapter, or \"auto\" to select\nan interface automatically, or \"none\" to run without TUN."` - IfMTU uint64 `comment:"Maximum Transmission Unit (MTU) size for your local TUN interface.\nDefault is the largest supported size for your platform. The lowest\npossible value is 1280."` - NodeInfoPrivacy bool `comment:"By default, nodeinfo contains some defaults including the platform,\narchitecture and Yggdrasil version. These can help when surveying\nthe network and diagnosing network routing problems. Enabling\nnodeinfo privacy prevents this, so that only items specified in\n\"NodeInfo\" are sent back if specified."` - NodeInfo map[string]interface{} `comment:"Optional node info. This must be a { \"key\": \"value\", ... } map\nor set as null. This is entirely optional but, if set, is visible\nto the whole network on request."` + PrivateKey KeyBytes `comment:"Your private key. DO NOT share this with anyone!"` + PrivateKeyPath string `json:",omitempty"` + Certificate *tls.Certificate `json:"-"` + Peers []string `comment:"List of connection strings for outbound peer connections in URI format,\ne.g. tls://a.b.c.d:e or socks://a.b.c.d:e/f.g.h.i:j. These connections\nwill obey the operating system routing table, therefore you should\nuse this section when you may connect via different interfaces."` + InterfacePeers map[string][]string `comment:"List of connection strings for outbound peer connections in URI format,\narranged by source interface, e.g. { \"eth0\": [ \"tls://a.b.c.d:e\" ] }.\nNote that SOCKS peerings will NOT be affected by this option and should\ngo in the \"Peers\" section instead."` + Listen []string `comment:"Listen addresses for incoming connections. You will need to add\nlisteners in order to accept incoming peerings from non-local nodes.\nMulticast peer discovery will work regardless of any listeners set\nhere. Each listener should be specified in URI format as above, e.g.\ntls://0.0.0.0:0 or tls://[::]:0 to listen on all interfaces."` + 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."` + MulticastInterfaces []MulticastInterfaceConfig `comment:"Configuration for which interfaces multicast peer discovery should be\nenabled on. Each entry in the list should be a json object which may\ncontain Regex, Beacon, Listen, and Port. Regex is a regular expression\nwhich is matched against an interface name, and interfaces use the\nfirst configuration that they match gainst. Beacon configures whether\nor not the node should send link-local multicast beacons to advertise\ntheir presence, while listening for incoming connections on Port.\nListen controls whether or not the node listens for multicast beacons\nand opens outgoing connections."` + AllowedPublicKeys []string `comment:"List of peer public keys to allow incoming peering connections\nfrom. If left empty/undefined then all connections will be allowed\nby default. This does not affect outgoing peerings, nor does it\naffect link-local peers discovered via multicast."` + IfName string `comment:"Local network interface name for TUN adapter, or \"auto\" to select\nan interface automatically, or \"none\" to run without TUN."` + IfMTU uint64 `comment:"Maximum Transmission Unit (MTU) size for your local TUN interface.\nDefault is the largest supported size for your platform. The lowest\npossible value is 1280."` + NodeInfoPrivacy bool `comment:"By default, nodeinfo contains some defaults including the platform,\narchitecture and Yggdrasil version. These can help when surveying\nthe network and diagnosing network routing problems. Enabling\nnodeinfo privacy prevents this, so that only items specified in\n\"NodeInfo\" are sent back if specified."` + NodeInfo map[string]interface{} `comment:"Optional node info. This must be a { \"key\": \"value\", ... } map\nor set as null. This is entirely optional but, if set, is visible\nto the whole network on request."` } type MulticastInterfaceConfig struct { @@ -138,19 +135,6 @@ func (cfg *NodeConfig) postprocessConfig() error { return err } } - if cfg.CertificatePath != "" { - if cfg.PrivateKeyPath == "" { - return fmt.Errorf("CertificatePath requires PrivateKeyPath") - } - cfg.Certificate = nil - f, err := os.ReadFile(cfg.CertificatePath) - if err != nil { - return err - } - if err := cfg.UnmarshalPEMCertificate(f); err != nil { - return err - } - } switch { case cfg.Certificate == nil: // No self-signed certificate has been generated yet. @@ -163,35 +147,6 @@ func (cfg *NodeConfig) postprocessConfig() error { return err } } - cfg.RootCertificates = cfg.RootCertificates[:0] - for _, path := range cfg.RootCertificatePaths { - f, err := os.ReadFile(path) - if err != nil { - return err - } - if err := cfg.UnmarshalRootCertificate(f); err != nil { - return err - } - } - return nil -} - -func (cfg *NodeConfig) UnmarshalRootCertificate(b []byte) error { - p, _ := pem.Decode(b) - if p == nil { - return fmt.Errorf("failed to parse PEM file") - } - if p.Type != "CERTIFICATE" { - return fmt.Errorf("unexpected PEM type %q", p.Type) - } - cert, err := x509.ParseCertificate(p.Bytes) - if err != nil { - return fmt.Errorf("failed to load X.509 keypair: %w", err) - } - if !cert.IsCA { - return fmt.Errorf("supplied root certificate is not a certificate authority") - } - cfg.RootCertificates = append(cfg.RootCertificates, cert) return nil } @@ -215,26 +170,6 @@ func (cfg *NodeConfig) GenerateSelfSignedCertificate() error { return nil } -func (cfg *NodeConfig) GenerateCertificateSigningRequest() ([]byte, error) { - template := &x509.CertificateRequest{ - Subject: pkix.Name{ - CommonName: hex.EncodeToString(cfg.PrivateKey), - }, - SignatureAlgorithm: x509.PureEd25519, - } - - csrBytes, err := x509.CreateCertificateRequest(rand.Reader, template, ed25519.PrivateKey(cfg.PrivateKey)) - if err != nil { - return nil, err - } - - pemBytes := bytes.NewBuffer(nil) - if err := pem.Encode(pemBytes, &pem.Block{Type: "CERTIFICATE REQUEST", Bytes: csrBytes}); err != nil { - return nil, err - } - return pemBytes.Bytes(), nil -} - func (cfg *NodeConfig) MarshalPEMCertificate() ([]byte, error) { privateKey := ed25519.PrivateKey(cfg.PrivateKey) publicKey := privateKey.Public().(ed25519.PublicKey) @@ -263,15 +198,6 @@ func (cfg *NodeConfig) MarshalPEMCertificate() ([]byte, error) { return pem.EncodeToMemory(block), nil } -func (cfg *NodeConfig) UnmarshalPEMCertificate(b []byte) error { - tlsCert, err := tls.LoadX509KeyPair(cfg.CertificatePath, cfg.PrivateKeyPath) - if err != nil { - return fmt.Errorf("failed to load X.509 keypair: %w", err) - } - cfg.Certificate = &tlsCert - return nil -} - func (cfg *NodeConfig) NewPrivateKey() { _, spriv, err := ed25519.GenerateKey(nil) if err != nil { diff --git a/src/core/core.go b/src/core/core.go index dfc18704..e641c8cc 100644 --- a/src/core/core.go +++ b/src/core/core.go @@ -4,7 +4,6 @@ import ( "context" "crypto/ed25519" "crypto/tls" - "crypto/x509" "encoding/hex" "fmt" "io" @@ -39,8 +38,7 @@ type Core struct { log Logger addPeerTimer *time.Timer config struct { - tls *tls.Config // immutable after startup - roots *x509.CertPool // immutable after startup + tls *tls.Config // immutable after startup //_peers map[Peer]*linkInfo // configurable after startup _listeners map[ListenAddress]struct{} // configurable after startup nodeinfo NodeInfo // immutable after startup @@ -110,9 +108,6 @@ func New(cert *tls.Certificate, logger Logger, opts ...SetupOption) (*Core, erro c.log.Infof("Your public key is %s", hex.EncodeToString(c.public)) c.log.Infof("Your IPv6 address is %s", address.String()) c.log.Infof("Your IPv6 subnet is %s", subnet.String()) - if c.config.roots != nil { - c.log.Println("Yggdrasil is running in TLS-only mode") - } c.proto.init(c) if err := c.links.init(c); err != nil { return nil, fmt.Errorf("error initialising links: %w", err) @@ -169,10 +164,6 @@ func (c *Core) _close() error { return err } -func (c *Core) isTLSOnly() bool { - return c.config.roots != nil -} - func (c *Core) MTU() uint64 { const sessionTypeOverhead = 1 MTU := c.PacketConn.MTU() - sessionTypeOverhead diff --git a/src/core/link_tcp.go b/src/core/link_tcp.go index 889051b9..f595aeb9 100644 --- a/src/core/link_tcp.go +++ b/src/core/link_tcp.go @@ -69,9 +69,6 @@ func (l *linkTCP) dialersFor(url *url.URL, info linkInfo) ([]*tcpDialer, error) } func (l *linkTCP) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) { - if l.core.isTLSOnly() { - return nil, fmt.Errorf("TCP peer prohibited in TLS-only mode") - } dialers, err := l.dialersFor(url, info) if err != nil { return nil, err @@ -92,9 +89,6 @@ func (l *linkTCP) dial(ctx context.Context, url *url.URL, info linkInfo, options } func (l *linkTCP) listen(ctx context.Context, url *url.URL, sintf string) (net.Listener, error) { - if l.core.isTLSOnly() { - return nil, fmt.Errorf("TCP listener prohibited in TLS-only mode") - } hostport := url.Host if sintf != "" { if host, port, err := net.SplitHostPort(hostport); err == nil { diff --git a/src/core/options.go b/src/core/options.go index e294896c..ffbdae73 100644 --- a/src/core/options.go +++ b/src/core/options.go @@ -2,19 +2,12 @@ package core import ( "crypto/ed25519" - "crypto/x509" "fmt" "net/url" ) func (c *Core) _applyOption(opt SetupOption) (err error) { switch v := opt.(type) { - case RootCertificate: - cert := x509.Certificate(v) - if c.config.roots == nil { - c.config.roots = x509.NewCertPool() - } - c.config.roots.AddCert(&cert) case Peer: u, err := url.Parse(v.URI) if err != nil { @@ -39,7 +32,6 @@ type SetupOption interface { isSetupOption() } -type RootCertificate x509.Certificate type ListenAddress string type Peer struct { URI string @@ -49,7 +41,6 @@ type NodeInfo map[string]interface{} type NodeInfoPrivacy bool type AllowedPublicKey ed25519.PublicKey -func (a RootCertificate) isSetupOption() {} func (a ListenAddress) isSetupOption() {} func (a Peer) isSetupOption() {} func (a NodeInfo) isSetupOption() {} diff --git a/src/core/tls.go b/src/core/tls.go index 08d0bd1e..3538334e 100644 --- a/src/core/tls.go +++ b/src/core/tls.go @@ -17,46 +17,30 @@ func (c *Core) generateTLSConfig(cert *tls.Certificate) (*tls.Config, error) { VerifyConnection: c.verifyTLSConnection, InsecureSkipVerify: true, MinVersion: tls.VersionTLS13, - NextProtos: []string{"yggdrasil/0.5"}, + NextProtos: []string{ + fmt.Sprintf("yggdrasil/%d.%d", ProtocolVersionMajor, ProtocolVersionMinor), + }, } return config, nil } func (c *Core) verifyTLSCertificate(rawCerts [][]byte, _ [][]*x509.Certificate) error { - if c.config.roots == nil { - // If there's no certificate pool configured then we will - // accept all TLS certificates. - return nil - } - if len(rawCerts) == 0 { - return fmt.Errorf("expected at least one certificate") + if len(rawCerts) != 1 { + return fmt.Errorf("expected one certificate") } - opts := x509.VerifyOptions{ - Roots: c.config.roots, - } - - for i, rawCert := range rawCerts { - if i == 0 { - // The first certificate is the leaf certificate. All other - // certificates in the list are intermediates, so add them - // into the VerifyOptions. - continue - } - cert, err := x509.ParseCertificate(rawCert) + /* + opts := x509.VerifyOptions{} + cert, err := x509.ParseCertificate(rawCerts[0]) if err != nil { - return fmt.Errorf("failed to parse intermediate certificate: %w", err) + return fmt.Errorf("failed to parse leaf certificate: %w", err) } - opts.Intermediates.AddCert(cert) - } - cert, err := x509.ParseCertificate(rawCerts[0]) - if err != nil { - return fmt.Errorf("failed to parse leaf certificate: %w", err) - } + _, err = cert.Verify(opts) + return err + */ - _, err = cert.Verify(opts) - return err + return nil } func (c *Core) verifyTLSConnection(cs tls.ConnectionState) error { From 2a212417389fcf8f41fd44c74864adfcc9b9e009 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 11 Oct 2023 19:28:28 +0100 Subject: [PATCH 150/293] Multicast passwords --- cmd/yggdrasil/main.go | 1 + contrib/mobile/mobile.go | 1 + src/config/config.go | 1 + src/multicast/advertisement.go | 18 ++++----- src/multicast/advertisement_test.go | 10 ++--- src/multicast/multicast.go | 61 +++++++++++++++++++++-------- src/multicast/options.go | 9 +---- 7 files changed, 62 insertions(+), 39 deletions(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 09d2f315..2f294765 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -230,6 +230,7 @@ func main() { Listen: intf.Listen, Port: intf.Port, Priority: uint8(intf.Priority), + Password: intf.Password, }) } if n.multicast, err = multicast.New(n.core, logger, options...); err != nil { diff --git a/contrib/mobile/mobile.go b/contrib/mobile/mobile.go index be1f5ff7..eb79430d 100644 --- a/contrib/mobile/mobile.go +++ b/contrib/mobile/mobile.go @@ -88,6 +88,7 @@ func (m *Yggdrasil) StartJSON(configjson []byte) error { Listen: intf.Listen, Port: intf.Port, Priority: uint8(intf.Priority), + Password: intf.Password, }) } m.multicast, err = multicast.New(m.core, m.logger, options...) diff --git a/src/config/config.go b/src/config/config.go index e818f703..bb94b674 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -61,6 +61,7 @@ type MulticastInterfaceConfig struct { Listen bool Port uint16 Priority uint64 // really uint8, but gobind won't export it + Password string } // Generates default configuration and returns a pointer to the resulting diff --git a/src/multicast/advertisement.go b/src/multicast/advertisement.go index 69c29b6a..d0db8b5a 100644 --- a/src/multicast/advertisement.go +++ b/src/multicast/advertisement.go @@ -7,21 +7,21 @@ import ( ) type multicastAdvertisement struct { - MajorVersion uint16 - MinorVersion uint16 - PublicKey ed25519.PublicKey - Port uint16 - Discriminator []byte + MajorVersion uint16 + MinorVersion uint16 + PublicKey ed25519.PublicKey + Port uint16 + Hash []byte } func (m *multicastAdvertisement) MarshalBinary() ([]byte, error) { - b := make([]byte, 0, ed25519.PublicKeySize+8+len(m.Discriminator)) + b := make([]byte, 0, ed25519.PublicKeySize+8+len(m.Hash)) b = binary.BigEndian.AppendUint16(b, m.MajorVersion) b = binary.BigEndian.AppendUint16(b, m.MinorVersion) b = append(b, m.PublicKey...) b = binary.BigEndian.AppendUint16(b, m.Port) - b = binary.BigEndian.AppendUint16(b, uint16(len(m.Discriminator))) - b = append(b, m.Discriminator...) + b = binary.BigEndian.AppendUint16(b, uint16(len(m.Hash))) + b = append(b, m.Hash...) return b, nil } @@ -34,6 +34,6 @@ func (m *multicastAdvertisement) UnmarshalBinary(b []byte) error { m.PublicKey = append(m.PublicKey[:0], b[4:4+ed25519.PublicKeySize]...) m.Port = binary.BigEndian.Uint16(b[4+ed25519.PublicKeySize : 6+ed25519.PublicKeySize]) dl := binary.BigEndian.Uint16(b[6+ed25519.PublicKeySize : 8+ed25519.PublicKeySize]) - m.Discriminator = append(m.Discriminator[:0], b[8+ed25519.PublicKeySize:8+ed25519.PublicKeySize+dl]...) + m.Hash = append(m.Hash[:0], b[8+ed25519.PublicKeySize:8+ed25519.PublicKeySize+dl]...) return nil } diff --git a/src/multicast/advertisement_test.go b/src/multicast/advertisement_test.go index 7132322d..9541da60 100644 --- a/src/multicast/advertisement_test.go +++ b/src/multicast/advertisement_test.go @@ -13,11 +13,11 @@ func TestMulticastAdvertisementRoundTrip(t *testing.T) { } orig := multicastAdvertisement{ - MajorVersion: 1, - MinorVersion: 2, - PublicKey: pk, - Port: 3, - Discriminator: sk, // any bytes will do + MajorVersion: 1, + MinorVersion: 2, + PublicKey: pk, + Port: 3, + Hash: sk, // any bytes will do } ob, err := orig.MarshalBinary() diff --git a/src/multicast/multicast.go b/src/multicast/multicast.go index f58af934..741c4315 100644 --- a/src/multicast/multicast.go +++ b/src/multicast/multicast.go @@ -3,6 +3,7 @@ package multicast import ( "bytes" "context" + "crypto/ed25519" "encoding/hex" "fmt" "math/rand" @@ -14,6 +15,7 @@ import ( "github.com/gologme/log" "github.com/yggdrasil-network/yggdrasil-go/src/core" + "golang.org/x/crypto/blake2b" "golang.org/x/net/ipv6" ) @@ -31,10 +33,8 @@ type Multicast struct { _interfaces map[string]*interfaceInfo _timer *time.Timer config struct { - _discriminator []byte - _discriminatorMatch func([]byte) bool - _groupAddr GroupAddress - _interfaces map[MulticastInterface]struct{} + _groupAddr GroupAddress + _interfaces map[MulticastInterface]struct{} } } @@ -45,6 +45,8 @@ type interfaceInfo struct { listen bool port uint16 priority uint8 + password []byte + hash []byte } type listenerInfo struct { @@ -178,6 +180,7 @@ func (m *Multicast) _getAllowedInterfaces() map[string]*interfaceInfo { return nil } // Work out which interfaces to announce on + pk := m.core.PublicKey() for _, iface := range allifaces { switch { case iface.Flags&net.FlagUp == 0: @@ -196,12 +199,23 @@ func (m *Multicast) _getAllowedInterfaces() map[string]*interfaceInfo { if !ifcfg.Regex.MatchString(iface.Name) { continue } + hasher, err := blake2b.New512([]byte(ifcfg.Password)) + if err != nil { + continue + } + if n, err := hasher.Write(pk); err != nil { + continue + } else if n != ed25519.PublicKeySize { + continue + } interfaces[iface.Name] = &interfaceInfo{ iface: iface, beacon: ifcfg.Beacon, listen: ifcfg.Listen, port: ifcfg.Port, priority: ifcfg.Priority, + password: []byte(ifcfg.Password), + hash: hasher.Sum(nil), } break } @@ -298,10 +312,13 @@ func (m *Multicast) _announce() { var linfo *listenerInfo if _, ok := m._listeners[iface.Name]; !ok { // No listener was found - let's create one - urlString := fmt.Sprintf("tls://[%s]:%d", addrIP, info.port) - u, err := url.Parse(urlString) - if err != nil { - panic(err) + v := &url.Values{} + v.Add("priority", fmt.Sprintf("%d", info.priority)) + v.Add("password", string(info.password)) + u := &url.URL{ + Scheme: "tls", + Host: net.JoinHostPort(addrIP.String(), fmt.Sprintf("%d", info.port)), + RawQuery: v.Encode(), } if li, err := m.core.Listen(u, iface.Name); err == nil { m.log.Debugln("Started multicasting on", iface.Name) @@ -324,11 +341,11 @@ func (m *Multicast) _announce() { } addr := linfo.listener.Addr().(*net.TCPAddr) adv := multicastAdvertisement{ - MajorVersion: core.ProtocolVersionMajor, - MinorVersion: core.ProtocolVersionMinor, - PublicKey: m.core.PublicKey(), - Port: uint16(addr.Port), - Discriminator: m.config._discriminator, + MajorVersion: core.ProtocolVersionMajor, + MinorVersion: core.ProtocolVersionMinor, + PublicKey: m.core.PublicKey(), + Port: uint16(addr.Port), + Hash: info.hash, } msg, err := adv.MarshalBinary() if err != nil { @@ -356,6 +373,7 @@ func (m *Multicast) listen() { panic(err) } bs := make([]byte, 2048) + hb := make([]byte, 0, blake2b.Size) // Reused to reduce hash allocations for { n, rcm, fromAddr, err := m.sock.ReadFrom(bs) if err != nil { @@ -386,10 +404,6 @@ func (m *Multicast) listen() { continue case adv.PublicKey.Equal(m.core.PublicKey()): continue - case m.config._discriminatorMatch == nil && !bytes.Equal(adv.Discriminator, m.config._discriminator): - continue - case m.config._discriminatorMatch != nil && !m.config._discriminatorMatch(adv.Discriminator): - continue } from := fromAddr.(*net.UDPAddr) from.Port = int(adv.Port) @@ -398,9 +412,22 @@ func (m *Multicast) listen() { interfaces = m._interfaces }) if info, ok := interfaces[from.Zone]; ok && info.listen { + hasher, err := blake2b.New512(info.password) + if err != nil { + continue + } + if n, err := hasher.Write(adv.PublicKey); err != nil { + continue + } else if n != ed25519.PublicKeySize { + continue + } + if !bytes.Equal(hasher.Sum(hb[:0]), adv.Hash) { + continue + } v := &url.Values{} v.Add("key", hex.EncodeToString(adv.PublicKey)) v.Add("priority", fmt.Sprintf("%d", info.priority)) + v.Add("password", string(info.password)) u := &url.URL{ Scheme: "tls", Host: from.String(), diff --git a/src/multicast/options.go b/src/multicast/options.go index aa740605..bd9fea5a 100644 --- a/src/multicast/options.go +++ b/src/multicast/options.go @@ -8,10 +8,6 @@ func (m *Multicast) _applyOption(opt SetupOption) { m.config._interfaces[v] = struct{}{} case GroupAddress: m.config._groupAddr = v - case Discriminator: - m.config._discriminator = append(m.config._discriminator[:0], v...) - case DiscriminatorMatch: - m.config._discriminatorMatch = v } } @@ -25,13 +21,10 @@ type MulticastInterface struct { Listen bool Port uint16 Priority uint8 + Password string } type GroupAddress string -type Discriminator []byte -type DiscriminatorMatch func([]byte) bool func (a MulticastInterface) isSetupOption() {} func (a GroupAddress) isSetupOption() {} -func (a Discriminator) isSetupOption() {} -func (a DiscriminatorMatch) isSetupOption() {} From ed8ba584e2da7b4c3501d498578b8c2c9ced5a1b Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 11 Oct 2023 23:42:37 +0100 Subject: [PATCH 151/293] Update dependencies --- go.mod | 37 ++++++++++----------- go.sum | 100 ++++++++++++++++++++++++++------------------------------- 2 files changed, 64 insertions(+), 73 deletions(-) diff --git a/go.mod b/go.mod index 6be4c7ad..ec0b02c9 100644 --- a/go.mod +++ b/go.mod @@ -5,42 +5,43 @@ go 1.20 require ( github.com/Arceliar/ironwood v0.0.0-20230805085300-86206813435f github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d - github.com/cheggaaa/pb/v3 v3.0.8 - github.com/gologme/log v1.2.0 + github.com/cheggaaa/pb/v3 v3.1.4 + github.com/gologme/log v1.3.0 github.com/hashicorp/go-syslog v1.0.0 github.com/hjson/hjson-go/v4 v4.3.0 github.com/kardianos/minwinsvc v1.0.2 - github.com/quic-go/quic-go v0.37.4 + github.com/quic-go/quic-go v0.39.0 github.com/vishvananda/netlink v1.1.0 - golang.org/x/mobile v0.0.0-20221110043201-43a038452099 - golang.org/x/net v0.10.0 - golang.org/x/sys v0.8.0 - golang.org/x/text v0.9.0 - golang.zx2c4.com/wireguard v0.0.0-20211017052713-f87e87af0d9a - golang.zx2c4.com/wireguard/windows v0.4.12 + golang.org/x/crypto v0.14.0 + golang.org/x/mobile v0.0.0-20231006135142-2b44d11868fe + golang.org/x/net v0.17.0 + golang.org/x/sys v0.13.0 + golang.org/x/text v0.13.0 + golang.zx2c4.com/wireguard v0.0.0-20231010133717-42ec952eadc2 + golang.zx2c4.com/wireguard/windows v0.5.3 ) require ( github.com/bits-and-blooms/bitset v1.5.0 // indirect github.com/bits-and-blooms/bloom/v3 v3.3.1 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect - github.com/golang/mock v1.6.0 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect - github.com/mattn/go-colorable v0.1.8 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect github.com/onsi/ginkgo/v2 v2.9.5 // indirect - github.com/quic-go/qtls-go1-20 v0.3.1 // indirect + github.com/quic-go/qtls-go1-20 v0.3.4 // indirect github.com/rivo/uniseg v0.2.0 // indirect - golang.org/x/crypto v0.8.0 // indirect + go.uber.org/mock v0.3.0 // indirect golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect - golang.org/x/mod v0.10.0 // indirect - golang.org/x/tools v0.9.1 // indirect + golang.org/x/mod v0.13.0 // indirect + golang.org/x/tools v0.14.0 // indirect + golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect ) require ( github.com/VividCortex/ewma v1.2.0 // indirect - github.com/fatih/color v1.12.0 // indirect - github.com/mattn/go-isatty v0.0.13 // indirect - github.com/mattn/go-runewidth v0.0.13 // indirect + github.com/fatih/color v1.15.0 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mattn/go-runewidth v0.0.14 // indirect github.com/olekukonko/tablewriter v0.0.5 github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f // indirect ) diff --git a/go.sum b/go.sum index e9446785..808b9f7d 100644 --- a/go.sum +++ b/go.sum @@ -2,7 +2,6 @@ github.com/Arceliar/ironwood v0.0.0-20230805085300-86206813435f h1:Fz0zG7ZyQQqk+ github.com/Arceliar/ironwood v0.0.0-20230805085300-86206813435f/go.mod h1:5x7fWW0mshe9WQ1lvSMmmHBYC3BeHH9gpwW5tz7cbfw= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q= -github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= github.com/bits-and-blooms/bitset v1.3.1/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= @@ -10,25 +9,23 @@ github.com/bits-and-blooms/bitset v1.5.0 h1:NpE8frKRLGHIcEzkR+gZhiioW1+WbYV6fKwD github.com/bits-and-blooms/bitset v1.5.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/bits-and-blooms/bloom/v3 v3.3.1 h1:K2+A19bXT8gJR5mU7y+1yW6hsKfNCjcP2uNfLFKncjQ= github.com/bits-and-blooms/bloom/v3 v3.3.1/go.mod h1:bhUUknWd5khVbTe4UgMCSiOOVJzr3tMoijSK3WwvW90= -github.com/cheggaaa/pb/v3 v3.0.8 h1:bC8oemdChbke2FHIIGy9mn4DPJ2caZYQnfbRqwmdCoA= -github.com/cheggaaa/pb/v3 v3.0.8/go.mod h1:UICbiLec/XO6Hw6k+BHEtHeQFzzBH4i2/qk/ow1EJTA= +github.com/cheggaaa/pb/v3 v3.1.4 h1:DN8j4TVVdKu3WxVwcRKu0sG00IIU6FewoABZzXbRQeo= +github.com/cheggaaa/pb/v3 v3.1.4/go.mod h1:6wVjILNBaXMs8c21qRiaUM8BR82erfgau1DQ4iUXmSA= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= -github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= -github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= +github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/gologme/log v1.2.0 h1:Ya5Ip/KD6FX7uH0S31QO87nCCSucKtF44TLbTtO7V4c= -github.com/gologme/log v1.2.0/go.mod h1:gq31gQ8wEHkR+WekdWsqDuf8pXTUZA9BnnzTuPz1Y9U= +github.com/gologme/log v1.3.0 h1:l781G4dE+pbigClDSDzSaaYKtiueHCILUa/qSDsmHAo= +github.com/gologme/log v1.3.0/go.mod h1:yKT+DvIPdDdDoPtqFrFxheooyVmoqi0BAsw+erN3wA4= +github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= @@ -39,15 +36,14 @@ github.com/hjson/hjson-go/v4 v4.3.0/go.mod h1:KaYt3bTw3zhBjYqnXkYywcYctk0A2nxeEF github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/kardianos/minwinsvc v1.0.2 h1:JmZKFJQrmTGa/WiW+vkJXKmfzdjabuEW4Tirj5lLdR0= github.com/kardianos/minwinsvc v1.0.2/go.mod h1:LUZNYhNmxujx2tR7FbdxqYJ9XDDoCd3MQcl1o//FWl4= -github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA= -github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= -github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= +github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= @@ -55,11 +51,10 @@ github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3Ro github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/quic-go/qtls-go1-20 v0.3.1 h1:O4BLOM3hwfVF3AcktIylQXyl7Yi2iBNVy5QsV+ySxbg= -github.com/quic-go/qtls-go1-20 v0.3.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= -github.com/quic-go/quic-go v0.37.4 h1:ke8B73yMCWGq9MfrCCAw0Uzdm7GaViC3i39dsIdDlH4= -github.com/quic-go/quic-go v0.37.4/go.mod h1:YsbH1r4mSHPJcLF4k4zruUkLBqctEMBDR6VPvcYjIsU= -github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/quic-go/qtls-go1-20 v0.3.4 h1:MfFAPULvst4yoMgY9QmtpYmfij/em7O8UUi+bNVm7Cg= +github.com/quic-go/qtls-go1-20 v0.3.4/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= +github.com/quic-go/quic-go v0.39.0 h1:AgP40iThFMY0bj8jGxROhw3S0FMGa8ryqsmi9tBH3So= +github.com/quic-go/quic-go v0.39.0/go.mod h1:T09QsDQWjLiQ74ZmacDfqZmhY/NLnw5BC40MANNNZ1Q= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -72,54 +67,47 @@ github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYp github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f h1:p4VB7kIXpOQvVn1ZaTIVp+3vuYAXFe3OJEvjbUYJLaA= github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo= +go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o= golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= -golang.org/x/mobile v0.0.0-20221110043201-43a038452099 h1:aIu0lKmfdgtn2uTj7JI2oN4TUrQvgB+wzTPO23bCKt8= -golang.org/x/mobile v0.0.0-20221110043201-43a038452099/go.mod h1:aAjjkJNdrh3PMckS4B10TGS2nag27cbKR1y2BpUxsiY= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mobile v0.0.0-20231006135142-2b44d11868fe h1:lrXv4yHeD9FA8PSJATWowP1QvexpyAPWmPia+Kbzql8= +golang.org/x/mobile v0.0.0-20231006135142-2b44d11868fe/go.mod h1:BrnXpEObnFxpaT75Jo9hsCazwOWcp7nVIa8NNuH5cuA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= -golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= +golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= +golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -128,23 +116,25 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= -golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= +golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= +golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.zx2c4.com/wireguard v0.0.0-20211017052713-f87e87af0d9a h1:tTbyylK9/D3u/wEP26Vx7L700UpY48nhioJWZM1vhZw= -golang.zx2c4.com/wireguard v0.0.0-20211017052713-f87e87af0d9a/go.mod h1:id8Oh3eCCmpj9uVGWVjsUAl6UPX5ysMLzu6QxJU2UOU= -golang.zx2c4.com/wireguard/windows v0.4.12 h1:CUmbdWKVNzTSsVb4yUAiEwL3KsabdJkEPdDjCHxBlhA= -golang.zx2c4.com/wireguard/windows v0.4.12/go.mod h1:PW4y+d9oY83XU9rRwRwrJDwEMuhVjMxu2gfD1cfzS7w= +golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= +golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= +golang.zx2c4.com/wireguard v0.0.0-20231010133717-42ec952eadc2 h1:I9wdPjBlr0efFqY7IHryfOXBdTxA4j8F0ynd227sYVU= +golang.zx2c4.com/wireguard v0.0.0-20231010133717-42ec952eadc2/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA= +golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE= +golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI= google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 h1:TbRPT0HtzFP3Cno1zZo7yPzEEnfu8EjLfl6IU9VfqkQ= From 4f656685ef3af2b5dea8aba9aa48d0cc30310097 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 11 Oct 2023 23:52:39 +0100 Subject: [PATCH 152/293] Revert Wireguard TUN upgrade (needs work for vectorised reads) --- go.mod | 2 +- go.sum | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index ec0b02c9..ed6c47c8 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( golang.org/x/net v0.17.0 golang.org/x/sys v0.13.0 golang.org/x/text v0.13.0 - golang.zx2c4.com/wireguard v0.0.0-20231010133717-42ec952eadc2 + golang.zx2c4.com/wireguard v0.0.0-20230223181233-21636207a675 golang.zx2c4.com/wireguard/windows v0.5.3 ) diff --git a/go.sum b/go.sum index 808b9f7d..6c0bdb11 100644 --- a/go.sum +++ b/go.sum @@ -25,7 +25,6 @@ github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4 github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/gologme/log v1.3.0 h1:l781G4dE+pbigClDSDzSaaYKtiueHCILUa/qSDsmHAo= github.com/gologme/log v1.3.0/go.mod h1:yKT+DvIPdDdDoPtqFrFxheooyVmoqi0BAsw+erN3wA4= -github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= @@ -119,7 +118,6 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= @@ -129,12 +127,11 @@ golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= -golang.zx2c4.com/wireguard v0.0.0-20231010133717-42ec952eadc2 h1:I9wdPjBlr0efFqY7IHryfOXBdTxA4j8F0ynd227sYVU= -golang.zx2c4.com/wireguard v0.0.0-20231010133717-42ec952eadc2/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA= +golang.zx2c4.com/wireguard v0.0.0-20230223181233-21636207a675 h1:/J/RVnr7ng4fWPRH3xa4WtBJ1Jp+Auu4YNLmGiPv5QU= +golang.zx2c4.com/wireguard v0.0.0-20230223181233-21636207a675/go.mod h1:whfbyDBt09xhCYQWtO2+3UVjlaq6/9hDZrjg2ZE6SyA= golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE= golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI= google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 h1:TbRPT0HtzFP3Cno1zZo7yPzEEnfu8EjLfl6IU9VfqkQ= From 4b48fd0b5f24ee8dbcb36bc7e12b9b720147840b Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 12 Oct 2023 00:08:16 +0100 Subject: [PATCH 153/293] Fix Windows TUN build --- src/tun/tun_windows.go | 39 +++++++++++---------------------------- 1 file changed, 11 insertions(+), 28 deletions(-) diff --git a/src/tun/tun_windows.go b/src/tun/tun_windows.go index b3bb0c78..10fb5f1a 100644 --- a/src/tun/tun_windows.go +++ b/src/tun/tun_windows.go @@ -4,11 +4,10 @@ package tun import ( - "bytes" "errors" "fmt" "log" - "net" + "net/netip" "github.com/yggdrasil-network/yggdrasil-go/src/config" "golang.org/x/sys/windows" @@ -89,13 +88,9 @@ func (tun *TunAdapter) setupAddress(addr string) error { return errors.New("Can't configure IPv6 address as TUN adapter is not present") } if intf, ok := tun.iface.(*wgtun.NativeTun); ok { - if ipaddr, ipnet, err := net.ParseCIDR(addr); err == nil { + if ipnet, err := netip.ParsePrefix(addr); err == nil { luid := winipcfg.LUID(intf.LUID()) - addresses := append([]net.IPNet{}, net.IPNet{ - IP: ipaddr, - Mask: ipnet.Mask, - }) - + addresses := []netip.Prefix{ipnet} err := luid.SetIPAddressesForFamily(windows.AF_INET6, addresses) if err == windows.ERROR_OBJECT_ALREADY_EXISTS { cleanupAddressesOnDisconnectedInterfaces(windows.AF_INET6, addresses) @@ -118,24 +113,13 @@ func (tun *TunAdapter) setupAddress(addr string) error { * SPDX-License-Identifier: MIT * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. */ -func cleanupAddressesOnDisconnectedInterfaces(family winipcfg.AddressFamily, addresses []net.IPNet) { +func cleanupAddressesOnDisconnectedInterfaces(family winipcfg.AddressFamily, addresses []netip.Prefix) { if len(addresses) == 0 { return } - includedInAddresses := func(a net.IPNet) bool { - // TODO: this makes the whole algorithm O(n^2). But we can't stick net.IPNet in a Go hashmap. Bummer! - for _, addr := range addresses { - ip := addr.IP - if ip4 := ip.To4(); ip4 != nil { - ip = ip4 - } - mA, _ := addr.Mask.Size() - mB, _ := a.Mask.Size() - if bytes.Equal(ip, a.IP) && mA == mB { - return true - } - } - return false + addrHash := make(map[netip.Addr]bool, len(addresses)) + for i := range addresses { + addrHash[addresses[i].Addr()] = true } interfaces, err := winipcfg.GetAdaptersAddresses(family, winipcfg.GAAFlagDefault) if err != nil { @@ -146,11 +130,10 @@ func cleanupAddressesOnDisconnectedInterfaces(family winipcfg.AddressFamily, add continue } for address := iface.FirstUnicastAddress; address != nil; address = address.Next { - ip := address.Address.IP() - ipnet := net.IPNet{IP: ip, Mask: net.CIDRMask(int(address.OnLinkPrefixLength), 8*len(ip))} - if includedInAddresses(ipnet) { - log.Printf("Cleaning up stale address %s from interface ‘%s’", ipnet.String(), iface.FriendlyName()) - iface.LUID.DeleteIPAddress(ipnet) + if ip, _ := netip.AddrFromSlice(address.Address.IP()); addrHash[ip] { + prefix := netip.PrefixFrom(ip, int(address.OnLinkPrefixLength)) + log.Printf("Cleaning up stale address %s from interface ‘%s’", prefix.String(), iface.FriendlyName()) + iface.LUID.DeleteIPAddress(prefix) } } } From 117e4b88f8bd520948049c9482936fa490c64b1f Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 12 Oct 2023 19:12:17 +0100 Subject: [PATCH 154/293] Fix panic on invalid handshake length --- src/core/version.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/version.go b/src/core/version.go index e01fe10c..332e18c8 100644 --- a/src/core/version.go +++ b/src/core/version.go @@ -101,6 +101,9 @@ func (m *version_metadata) decode(r io.Reader, password []byte) bool { return false } + if len(bs) < ed25519.SignatureSize { + return false + } sig := bs[len(bs)-ed25519.SignatureSize:] bs = bs[:len(bs)-ed25519.SignatureSize] From efb4b4635d0496d9775624a0ae8ff217d932ef5a Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 14 Oct 2023 20:26:30 +0100 Subject: [PATCH 155/293] Don't send a TLS ALPN name --- src/core/tls.go | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/src/core/tls.go b/src/core/tls.go index 3538334e..8a17b40e 100644 --- a/src/core/tls.go +++ b/src/core/tls.go @@ -3,7 +3,6 @@ package core import ( "crypto/tls" "crypto/x509" - "fmt" ) func (c *Core) generateTLSConfig(cert *tls.Certificate) (*tls.Config, error) { @@ -17,32 +16,14 @@ func (c *Core) generateTLSConfig(cert *tls.Certificate) (*tls.Config, error) { VerifyConnection: c.verifyTLSConnection, InsecureSkipVerify: true, MinVersion: tls.VersionTLS13, - NextProtos: []string{ - fmt.Sprintf("yggdrasil/%d.%d", ProtocolVersionMajor, ProtocolVersionMinor), - }, } return config, nil } -func (c *Core) verifyTLSCertificate(rawCerts [][]byte, _ [][]*x509.Certificate) error { - if len(rawCerts) != 1 { - return fmt.Errorf("expected one certificate") - } - - /* - opts := x509.VerifyOptions{} - cert, err := x509.ParseCertificate(rawCerts[0]) - if err != nil { - return fmt.Errorf("failed to parse leaf certificate: %w", err) - } - - _, err = cert.Verify(opts) - return err - */ - +func (c *Core) verifyTLSCertificate(_ [][]byte, _ [][]*x509.Certificate) error { return nil } -func (c *Core) verifyTLSConnection(cs tls.ConnectionState) error { +func (c *Core) verifyTLSConnection(_ tls.ConnectionState) error { return nil } From 88b773cd0a061f992832ad57cdb8cc87699e3231 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 15 Oct 2023 17:09:12 +0100 Subject: [PATCH 156/293] Version 0.5 RC1 release notes --- CHANGELOG.md | 753 +++++++++++++++++++++++++++------------------------ 1 file changed, 393 insertions(+), 360 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a43a4d1..249faf86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,708 +26,741 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - in case of vulnerabilities. --> +## [0.5.0] - Release Candidate 1 + +### Added + +* Authenticated peering handshake with optional password, i.e. + * For listeners: `tls://[::]:12345?password=123456abcdef` + * For peers: `tls://a.b.c.d:12345?password=123456abcdef` + * For multicast interfaces with the new `Password` option in each `MulticastInterfaces` section + * Maximum password length is 64 characters +* QUIC support for peerings, by using the new `quic://` scheme in `Listen` and `Peers` + * This has not been extensively tested and may perform worse than TCP or TLS peers +* The private key can now be stored in PEM format separately to the main configuration file with the new `PrivateKeyPath` configuration file option + * Use the `-exportkey` flag to export the key to a file from an existing config + +### Changed + +* New routing scheme, which is backwards incompatible with previous versions of Yggdrasil + * The wire protocol version number, exchanged as part of the peer setup handshake, has been increased to 0.5 + * Nodes running this new version **will not** be able to peer with earlier versions of Yggdrasil + * A DHT is no longer used to map public keys and routes through treespace + * Bloom filters are used to track on-tree links and nodes reachable via that link + * Nodes now gossip separate per-link information which is tracked in CRDT structures, forcing local consistency and preventing unnecessary flapping when a route to the root node has changed or is broken + * Greedy routing is once again used instead of source routing + * Per-link keepalives have been replaced with periodic acknowledgements, reducing idle bandwidth +* The link handshake and multicast beacon formats have been revised for better future extensibility +* The link code has been refactored for more robust tracking of peering states + * As a result, the admin socket is now able to report information about configured peerings that are down + * Reconnect intervals are now tracked separately for each configured peer with exponential backoffs + +### Removed + +* Yggdrasil will no longer request BBR congestion control for TCP and TLS peerings on Linux + ## [0.4.7] - 2022-11-20 ### Added -- Dropped outbound peerings will now try to reconnect after a single second, rather than waiting up to 60 seconds for the normal peer timer +* Dropped outbound peerings will now try to reconnect after a single second, rather than waiting up to 60 seconds for the normal peer timer ### Changed -- Session encryption keys are now rotated at most once per minute, which reduces CPU usage and improves throughput on fast low latency links -- Buffers are now reused in the session encryption handler, which improves session throughput and reduces memory allocations -- Buffers are now reused in the router for DHT and path traffic, which improves overall routing throughput and reduces memory allocations +* Session encryption keys are now rotated at most once per minute, which reduces CPU usage and improves throughput on fast low latency links +* Buffers are now reused in the session encryption handler, which improves session throughput and reduces memory allocations +* Buffers are now reused in the router for DHT and path traffic, which improves overall routing throughput and reduces memory allocations ### Fixed -- A bug in the admin socket where requests fail unless `arguments` is specified has been fixed -- Certificates on TLS listeners will no longer expire after a year -- The `-address` and `-subnet` command line options now return a useful warning when no configuration is specified +* A bug in the admin socket where requests fail unless `arguments` is specified has been fixed +* Certificates on TLS listeners will no longer expire after a year +* The `-address` and `-subnet` command line options now return a useful warning when no configuration is specified ## [0.4.6] - 2022-10-25 ### Added -- Support for prioritising multiple peerings to the same node has been added, useful for nodes with multiple network interfaces - - The priority can be configured by specifying `?priority=X` in a `Peers` or `Listen` URI, or by specifying `Priority` within a `MulticastInterfaces` configuration entry - - Priorities are values between 0 and 254 (default is 0), lower numbers are prioritised and nodes will automatically negotiate the higher of the two values +* Support for prioritising multiple peerings to the same node has been added, useful for nodes with multiple network interfaces + * The priority can be configured by specifying `?priority=X` in a `Peers` or `Listen` URI, or by specifying `Priority` within a `MulticastInterfaces` configuration entry + * Priorities are values between 0 and 254 (default is 0), lower numbers are prioritised and nodes will automatically negotiate the higher of the two values ### Changed -- On Linux, `SO_REUSEADDR` is now used on the multicast port instead of `SO_REUSEPORT`, which should allow processes running under different users to run simultaneously +* On Linux, `SO_REUSEADDR` is now used on the multicast port instead of `SO_REUSEPORT`, which should allow processes running under different users to run simultaneously ### Fixed -- Adding peers using the `InterfacePeers` configuration option should now work correctly again -- Multiple connections from the same remote IP address will no longer be incorrectly dropped -- The admin socket will no longer incorrectly claim TCP connections as TLS -- A panic that could occur when calling `GetPeers` while a peering link is being set up has been fixed +* Adding peers using the `InterfacePeers` configuration option should now work correctly again +* Multiple connections from the same remote IP address will no longer be incorrectly dropped +* The admin socket will no longer incorrectly claim TCP connections as TLS +* A panic that could occur when calling `GetPeers` while a peering link is being set up has been fixed ## [0.4.5] - 2022-10-15 ### Added -- Support for peering over UNIX sockets is now available, by configuring `Listen` and peering URIs in the `unix:///path/to/socket.sock` format +* Support for peering over UNIX sockets is now available, by configuring `Listen` and peering URIs in the `unix:///path/to/socket.sock` format ### Changed -- `yggdrasilctl` has been refactored and now has cleaner output -- It is now possible to `addPeer` and `removePeer` using the admin socket again -- The `getSessions` admin socket call reports number of bytes received and transmitted again -- The link setup code has been refactored, making it easier to support new peering types in the future -- Yggdrasil now maintains configuration internally, rather than relying on a shared and potentially mutable structure +* `yggdrasilctl` has been refactored and now has cleaner output +* It is now possible to `addPeer` and `removePeer` using the admin socket again +* The `getSessions` admin socket call reports number of bytes received and transmitted again +* The link setup code has been refactored, making it easier to support new peering types in the future +* Yggdrasil now maintains configuration internally, rather than relying on a shared and potentially mutable structure ### Fixed -- Tracking information about expired root nodes has been fixed, which should hopefully resolve issues with reparenting and connection failures when the root node disappears -- A bug in the mobile framework code which caused a crash on Android when multicast failed to set up has been fixed -- Yggdrasil should now shut down gracefully and clean up correctly when running as a Windows service +* Tracking information about expired root nodes has been fixed, which should hopefully resolve issues with reparenting and connection failures when the root node disappears +* A bug in the mobile framework code which caused a crash on Android when multicast failed to set up has been fixed +* Yggdrasil should now shut down gracefully and clean up correctly when running as a Windows service ## [0.4.4] - 2022-07-07 ### Fixed -- ICMPv6 "Packet Too Big" payload size has been increased, which should fix Path MTU Discovery (PMTUD) when two nodes have different `IfMTU` values configured -- A crash has been fixed when handling debug packet responses -- `yggdrasilctl getSelf` should now report coordinates correctly again +* ICMPv6 "Packet Too Big" payload size has been increased, which should fix Path MTU Discovery (PMTUD) when two nodes have different `IfMTU` values configured +* A crash has been fixed when handling debug packet responses +* `yggdrasilctl getSelf` should now report coordinates correctly again ### Changed -- Go 1.17 is now required to build Yggdrasil +* Go 1.17 is now required to build Yggdrasil ## [0.4.3] - 2022-02-06 ### Added -- `bytes_sent`, `bytes_recvd` and `uptime` have been added to `getPeers` -- Clearer logging when connections are rejected due to incompatible peer versions +* `bytes_sent`, `bytes_recvd` and `uptime` have been added to `getPeers` +* Clearer logging when connections are rejected due to incompatible peer versions ### Fixed -- Latency-based parent selection tiebreak is now reliable on platforms even with low timer resolution -- Tree distance calculation offsets have been corrected +* Latency-based parent selection tiebreak is now reliable on platforms even with low timer resolution +* Tree distance calculation offsets have been corrected ## [0.4.2] - 2021-11-03 ### Fixed -- Reverted a dependency update which resulted in problems building with Go 1.16 and running on Windows +* Reverted a dependency update which resulted in problems building with Go 1.16 and running on Windows ## [0.4.1] - 2021-11-03 ### Added -- TLS peerings now support Server Name Indication (SNI) - - The SNI is sent automatically if the peering URI contains a DNS name - - A custom SNI can be specified by adding the `?sni=domain.com` parameter to the peering URI -- A new `ipv6rwc` API package now implements the IPv6-specific logic separate from the `tun` package +* TLS peerings now support Server Name Indication (SNI) + * The SNI is sent automatically if the peering URI contains a DNS name + * A custom SNI can be specified by adding the `?sni=domain.com` parameter to the peering URI +* A new `ipv6rwc` API package now implements the IPv6-specific logic separate from the `tun` package ### Fixed -- A crash when calculating the partial public key for very high IPv6 addresses has been fixed -- A crash due to a concurrent map write has been fixed -- A crash due to missing TUN configuration has been fixed -- A race condition in the keystore code has been fixed +* A crash when calculating the partial public key for very high IPv6 addresses has been fixed +* A crash due to a concurrent map write has been fixed +* A crash due to missing TUN configuration has been fixed +* A race condition in the keystore code has been fixed ## [0.4.0] - 2021-07-04 ### Added -- New routing scheme, which is backwards incompatible with previous versions of Yggdrasil - - The wire protocol version number, exchanged as part of the peer setup handshake, has been increased to 0.4 - - Nodes running this new version **will not** be able to peer with earlier versions of Yggdrasil - - Please note that **the network may be temporarily unstable** while infrastructure is being upgraded to the new release -- TLS connections now use public key pinning - - If no public key was already pinned, then the public key received as part of the TLS handshake is pinned to the connection - - The public key received as part of the handshake is checked against the pinned keys, and if no match is found, the connection is rejected +* New routing scheme, which is backwards incompatible with previous versions of Yggdrasil + * The wire protocol version number, exchanged as part of the peer setup handshake, has been increased to 0.4 + * Nodes running this new version **will not** be able to peer with earlier versions of Yggdrasil + * Please note that **the network may be temporarily unstable** while infrastructure is being upgraded to the new release +* TLS connections now use public key pinning + * If no public key was already pinned, then the public key received as part of the TLS handshake is pinned to the connection + * The public key received as part of the handshake is checked against the pinned keys, and if no match is found, the connection is rejected ### Changed -- IP addresses are now derived from ed25519 public (signing) keys - - Previously, addresses were derived from a hash of X25519 (Diffie-Hellman) keys - - Importantly, this means that **all internal IPv6 addresses will change with this release** — this will affect anyone running public services or relying on Yggdrasil for remote access -- It is now recommended to peer over TLS - - Link-local peers from multicast peer discovery will now connect over TLS, with the key from the multicast beacon pinned to the connection - - `socks://` peers now expect the destination endpoint to be a `tls://` listener, instead of a `tcp://` listener -- Multicast peer discovery is now more configurable - - There are separate configuration options to control if beacons are sent, what port to listen on for incoming connections (if sending beacons), and whether or not to listen for beacons from other nodes (and open connections when receiving a beacon) - - Each configuration entry in the list specifies a regular expression to match against interface names - - If an interface matches multiple regex in the list, it will use the settings for the first entry in the list that it matches with -- The session and routing code has been entirely redesigned and rewritten - - This is still an early work-in-progress, so the code hasn't been as well tested or optimized as the old code base — please bear with us for these next few releases as we work through any bugs or issues - - Generally speaking, we expect to see reduced bandwidth use and improved reliability with the new design, especially in cases where nodes move around or change peerings frequently - - Cryptographic sessions no longer use a single shared (ephemeral) secret for the entire life of the session. Keys are now rotated regularly for ongoing sessions (currently rotated at least once per round trip exchange of traffic, subject to change in future releases) - - Source routing has been added. Under normal circumstances, this is what is used to forward session traffic (e.g. the user's IPv6 traffic) - - DHT-based routing has been added. This is used when the sender does not know a source route to the destination. Forwarding through the DHT is less efficient, but the only information that it requires the sender to know is the destination node's (static) key. This is primarily used during the key exchange at session setup, or as a temporary fallback when a source route fails due to changes in the network - - The new DHT design is no longer RPC-based, does not support crawling and does not inherently allow nodes to look up the owner of an arbitrary key. Responding to lookups is now implemented at the application level and a response is only sent if the destination key matches the node's `/128` IP or `/64` prefix - - The greedy routing scheme, used to forward all traffic in previous releases, is now only used for protocol traffic (i.e. DHT setup and source route discovery) - - The routing logic now lives in a [standalone library](https://github.com/Arceliar/ironwood). You are encouraged **not** to use it, as it's still considered pre-alpha, but it's available for those who want to experiment with the new routing algorithm in other contexts - - Session MTUs may be slightly lower now, in order to accommodate large packet headers if required -- Many of the admin functions available over `yggdrasilctl` have been changed or removed as part of rewrites to the code - - Several remote `debug` functions have been added temporarily, to allow for crawling and census gathering during the transition to the new version, but we intend to remove this at some point in the (possibly distant) future - - The list of available functions will likely be expanded in future releases -- The configuration file format has been updated in response to the changed/removed features +* IP addresses are now derived from ed25519 public (signing) keys + * Previously, addresses were derived from a hash of X25519 (Diffie-Hellman) keys + * Importantly, this means that **all internal IPv6 addresses will change with this release** — this will affect anyone running public services or relying on Yggdrasil for remote access +* It is now recommended to peer over TLS + * Link-local peers from multicast peer discovery will now connect over TLS, with the key from the multicast beacon pinned to the connection + * `socks://` peers now expect the destination endpoint to be a `tls://` listener, instead of a `tcp://` listener +* Multicast peer discovery is now more configurable + * There are separate configuration options to control if beacons are sent, what port to listen on for incoming connections (if sending beacons), and whether or not to listen for beacons from other nodes (and open connections when receiving a beacon) + * Each configuration entry in the list specifies a regular expression to match against interface names + * If an interface matches multiple regex in the list, it will use the settings for the first entry in the list that it matches with +* The session and routing code has been entirely redesigned and rewritten + * This is still an early work-in-progress, so the code hasn't been as well tested or optimized as the old code base — please bear with us for these next few releases as we work through any bugs or issues + * Generally speaking, we expect to see reduced bandwidth use and improved reliability with the new design, especially in cases where nodes move around or change peerings frequently + * Cryptographic sessions no longer use a single shared (ephemeral) secret for the entire life of the session. Keys are now rotated regularly for ongoing sessions (currently rotated at least once per round trip exchange of traffic, subject to change in future releases) + * Source routing has been added. Under normal circumstances, this is what is used to forward session traffic (e.g. the user's IPv6 traffic) + * DHT-based routing has been added. This is used when the sender does not know a source route to the destination. Forwarding through the DHT is less efficient, but the only information that it requires the sender to know is the destination node's (static) key. This is primarily used during the key exchange at session setup, or as a temporary fallback when a source route fails due to changes in the network + * The new DHT design is no longer RPC-based, does not support crawling and does not inherently allow nodes to look up the owner of an arbitrary key. Responding to lookups is now implemented at the application level and a response is only sent if the destination key matches the node's `/128` IP or `/64` prefix + * The greedy routing scheme, used to forward all traffic in previous releases, is now only used for protocol traffic (i.e. DHT setup and source route discovery) + * The routing logic now lives in a [standalone library](https://github.com/Arceliar/ironwood). You are encouraged **not** to use it, as it's still considered pre-alpha, but it's available for those who want to experiment with the new routing algorithm in other contexts + * Session MTUs may be slightly lower now, in order to accommodate large packet headers if required +* Many of the admin functions available over `yggdrasilctl` have been changed or removed as part of rewrites to the code + * Several remote `debug` functions have been added temporarily, to allow for crawling and census gathering during the transition to the new version, but we intend to remove this at some point in the (possibly distant) future + * The list of available functions will likely be expanded in future releases +* The configuration file format has been updated in response to the changed/removed features ### Removed -- Tunnel routing (a.k.a. crypto-key routing or "CKR") has been removed - - It was far too easy to accidentally break routing altogether by capturing the route to peers with the TUN adapter - - We recommend tunnelling an existing standard over Yggdrasil instead (e.g. `ip6gre`, `ip6gretap` or other similar encapsulations, using Yggdrasil IPv6 addresses as the tunnel endpoints) - - All `TunnelRouting` configuration options will no longer take effect -- Session firewall has been removed - - This was never a true firewall — it didn't behave like a stateful IP firewall, often allowed return traffic unexpectedly and was simply a way to prevent a node from being flooded with unwanted sessions, so the name could be misleading and usually lead to a false sense of security - - Due to design changes, the new code needs to address the possible memory exhaustion attacks in other ways and a single configurable list no longer makes sense - - Users who want a firewall or other packet filter mechansim should configure something supported by their OS instead (e.g. `ip6tables`) - - All `SessionFirewall` configuration options will no longer take effect -- `SIGHUP` handling to reload the configuration at runtime has been removed - - It was not obvious which parts of the configuration could be reloaded at runtime, and which required the application to be killed and restarted to take effect - - Reloading the config without restarting was also a delicate and bug-prone process, and was distracting from more important developments - - `SIGHUP` will be handled normally (i.e. by exiting) -- `cmd/yggrasilsim` has been removed, and is unlikely to return to this repository +* Tunnel routing (a.k.a. crypto-key routing or "CKR") has been removed + * It was far too easy to accidentally break routing altogether by capturing the route to peers with the TUN adapter + * We recommend tunnelling an existing standard over Yggdrasil instead (e.g. `ip6gre`, `ip6gretap` or other similar encapsulations, using Yggdrasil IPv6 addresses as the tunnel endpoints) + * All `TunnelRouting` configuration options will no longer take effect +* Session firewall has been removed + * This was never a true firewall — it didn't behave like a stateful IP firewall, often allowed return traffic unexpectedly and was simply a way to prevent a node from being flooded with unwanted sessions, so the name could be misleading and usually lead to a false sense of security + * Due to design changes, the new code needs to address the possible memory exhaustion attacks in other ways and a single configurable list no longer makes sense + * Users who want a firewall or other packet filter mechansim should configure something supported by their OS instead (e.g. `ip6tables`) + * All `SessionFirewall` configuration options will no longer take effect +* `SIGHUP` handling to reload the configuration at runtime has been removed + * It was not obvious which parts of the configuration could be reloaded at runtime, and which required the application to be killed and restarted to take effect + * Reloading the config without restarting was also a delicate and bug-prone process, and was distracting from more important developments + * `SIGHUP` will be handled normally (i.e. by exiting) +* `cmd/yggrasilsim` has been removed, and is unlikely to return to this repository ## [0.3.16] - 2021-03-18 ### Added -- New simulation code under `cmd/yggdrasilsim` (work-in-progress) +* New simulation code under `cmd/yggdrasilsim` (work-in-progress) ### Changed -- Multi-threading in the switch - - Swich lookups happen independently for each (incoming) peer connection, instead of being funneled to a single dedicated switch worker - - Packets are queued for each (outgoing) peer connection, instead of being handled by a single dedicated switch worker -- Queue logic rewritten - - Heap structure per peer that traffic is routed to, with one FIFO queue per traffic flow - - The total size of each heap is configured automatically (we basically queue packets until we think we're blocked on a socket write) - - When adding to a full heap, the oldest packet from the largest queue is dropped - - Packets are popped from the queue in FIFO order (oldest packet from among all queues in the heap) to prevent packet reordering at the session level -- Removed global `sync.Pool` of `[]byte` - - Local `sync.Pool`s are used in the hot loops, but not exported, to avoid memory corruption if libraries are reused by other projects - - This may increase allocations (and slightly reduce speed in CPU-bound benchmarks) when interacting with the tun/tap device, but traffic forwarded at the switch layer should be unaffected -- Upgrade dependencies -- Upgrade build to Go 1.16 +* Multi-threading in the switch + * Swich lookups happen independently for each (incoming) peer connection, instead of being funneled to a single dedicated switch worker + * Packets are queued for each (outgoing) peer connection, instead of being handled by a single dedicated switch worker +* Queue logic rewritten + * Heap structure per peer that traffic is routed to, with one FIFO queue per traffic flow + * The total size of each heap is configured automatically (we basically queue packets until we think we're blocked on a socket write) + * When adding to a full heap, the oldest packet from the largest queue is dropped + * Packets are popped from the queue in FIFO order (oldest packet from among all queues in the heap) to prevent packet reordering at the session level +* Removed global `sync.Pool` of `[]byte` + * Local `sync.Pool`s are used in the hot loops, but not exported, to avoid memory corruption if libraries are reused by other projects + * This may increase allocations (and slightly reduce speed in CPU-bound benchmarks) when interacting with the tun/tap device, but traffic forwarded at the switch layer should be unaffected +* Upgrade dependencies +* Upgrade build to Go 1.16 ### Fixed -- Fixed a bug where the connection listener could exit prematurely due to resoruce exhaustion (if e.g. too many connections were opened) -- Fixed DefaultIfName for OpenBSD (`/dev/tun0` -> `tun0`) -- Fixed an issue where a peer could sometimes never be added to the switch -- Fixed a goroutine leak that could occur if a peer with an open connection continued to spam additional connection attempts +* Fixed a bug where the connection listener could exit prematurely due to resoruce exhaustion (if e.g. too many connections were opened) +* Fixed DefaultIfName for OpenBSD (`/dev/tun0` -> `tun0`) +* Fixed an issue where a peer could sometimes never be added to the switch +* Fixed a goroutine leak that could occur if a peer with an open connection continued to spam additional connection attempts ## [0.3.15] - 2020-09-27 ### Added -- Support for pinning remote public keys in peering strings has been added, e.g. - - By signing public key: `tcp://host:port?ed25519=key` - - By encryption public key: `tcp://host:port?curve25519=key` - - By both: `tcp://host:port?ed25519=key&curve25519=key` - - By multiple, in case of DNS round-robin or similar: `tcp://host:port?curve25519=key&curve25519=key&ed25519=key&ed25519=key` -- Some checks to prevent Yggdrasil-over-Yggdrasil peerings have been added -- Added support for SOCKS proxy authentication, e.g. `socks://user@password:host/...` +* Support for pinning remote public keys in peering strings has been added, e.g. + * By signing public key: `tcp://host:port?ed25519=key` + * By encryption public key: `tcp://host:port?curve25519=key` + * By both: `tcp://host:port?ed25519=key&curve25519=key` + * By multiple, in case of DNS round-robin or similar: `tcp://host:port?curve25519=key&curve25519=key&ed25519=key&ed25519=key` +* Some checks to prevent Yggdrasil-over-Yggdrasil peerings have been added +* Added support for SOCKS proxy authentication, e.g. `socks://user@password:host/...` ### Fixed -- Some bugs in the multicast code that could cause unnecessary CPU usage have been fixed -- A possible multicast deadlock on macOS when enumerating interfaces has been fixed -- A deadlock in the connection code has been fixed -- Updated HJSON dependency that caused some build problems +* Some bugs in the multicast code that could cause unnecessary CPU usage have been fixed +* A possible multicast deadlock on macOS when enumerating interfaces has been fixed +* A deadlock in the connection code has been fixed +* Updated HJSON dependency that caused some build problems ### Changed -- `DisconnectPeer` and `RemovePeer` have been separated and implemented properly now -- Less nodes are stored in the DHT now, reducing ambient network traffic and possible instability -- Default config file for FreeBSD is now at `/usr/local/etc/yggdrasil.conf` instead of `/etc/yggdrasil.conf` +* `DisconnectPeer` and `RemovePeer` have been separated and implemented properly now +* Less nodes are stored in the DHT now, reducing ambient network traffic and possible instability +* Default config file for FreeBSD is now at `/usr/local/etc/yggdrasil.conf` instead of `/etc/yggdrasil.conf` ## [0.3.14] - 2020-03-28 ### Fixed -- Fixes a memory leak that may occur if packets are incorrectly never removed from a switch queue +* Fixes a memory leak that may occur if packets are incorrectly never removed from a switch queue ### Changed -- Make DHT searches a bit more reliable by tracking the 16 most recently visited nodes +* Make DHT searches a bit more reliable by tracking the 16 most recently visited nodes ## [0.3.13] - 2020-02-21 ### Added -- Support for the Wireguard TUN driver, which now replaces Water and provides far better support and performance on Windows -- Windows `.msi` installer files are now supported (bundling the Wireguard TUN driver) -- NodeInfo code is now actorised, should be more reliable -- The DHT now tries to store the two closest nodes in either direction instead of one, such that if a node goes offline, the replacement is already known -- The Yggdrasil API now supports dialing a remote node using the public key instead of the Node ID +* Support for the Wireguard TUN driver, which now replaces Water and provides far better support and performance on Windows +* Windows `.msi` installer files are now supported (bundling the Wireguard TUN driver) +* NodeInfo code is now actorised, should be more reliable +* The DHT now tries to store the two closest nodes in either direction instead of one, such that if a node goes offline, the replacement is already known +* The Yggdrasil API now supports dialing a remote node using the public key instead of the Node ID ### Changed -- The `-loglevel` command line parameter is now cumulative and automatically includes all levels below the one specified -- DHT search code has been significantly simplified and processes rumoured nodes in parallel, speeding up search time -- DHT search results are now sorted -- The systemd service now handles configuration generation in a different unit -- The Yggdrasil API now returns public keys instead of node IDs when querying for local and remote addresses +* The `-loglevel` command line parameter is now cumulative and automatically includes all levels below the one specified +* DHT search code has been significantly simplified and processes rumoured nodes in parallel, speeding up search time +* DHT search results are now sorted +* The systemd service now handles configuration generation in a different unit +* The Yggdrasil API now returns public keys instead of node IDs when querying for local and remote addresses ### Fixed -- The multicast code no longer panics when shutting down the node -- A potential OOB error when calculating IPv4 flow labels (when tunnel routing is enabled) has been fixed -- A bug resulting in incorrect idle notifications in the switch should now be fixed -- MTUs are now using a common datatype throughout the codebase +* The multicast code no longer panics when shutting down the node +* A potential OOB error when calculating IPv4 flow labels (when tunnel routing is enabled) has been fixed +* A bug resulting in incorrect idle notifications in the switch should now be fixed +* MTUs are now using a common datatype throughout the codebase ### Removed -- TAP mode has been removed entirely, since it is no longer supported with the Wireguard TUN package. Please note that if you are using TAP mode, you may need to revise your config! -- NetBSD support has been removed until the Wireguard TUN package supports NetBSD +* TAP mode has been removed entirely, since it is no longer supported with the Wireguard TUN package. Please note that if you are using TAP mode, you may need to revise your config! +* NetBSD support has been removed until the Wireguard TUN package supports NetBSD ## [0.3.12] - 2019-11-24 ### Added -- New API functions `SetMaximumSessionMTU` and `GetMaximumSessionMTU` -- New command line parameters `-address` and `-subnet` for getting the address/subnet from the config file, for use with `-useconffile` or `-useconf` -- A warning is now produced in the Yggdrasil output at startup when the MTU in the config is invalid or has been adjusted for some reason +* New API functions `SetMaximumSessionMTU` and `GetMaximumSessionMTU` +* New command line parameters `-address` and `-subnet` for getting the address/subnet from the config file, for use with `-useconffile` or `-useconf` +* A warning is now produced in the Yggdrasil output at startup when the MTU in the config is invalid or has been adjusted for some reason ### Changed -- On Linux, outgoing `InterfacePeers` connections now use `SO_BINDTODEVICE` to prefer an outgoing interface -- The `genkeys` utility is now in `cmd` rather than `misc` +* On Linux, outgoing `InterfacePeers` connections now use `SO_BINDTODEVICE` to prefer an outgoing interface +* The `genkeys` utility is now in `cmd` rather than `misc` ### Fixed -- A data race condition has been fixed when updating session coordinates -- A crash when shutting down when no multicast interfaces are configured has been fixed -- A deadlock when calling `AddPeer` multiple times has been fixed -- A typo in the systemd unit file (for some Linux packages) has been fixed -- The NodeInfo and admin socket now report `unknown` correctly when no build name/version is available in the environment at build time -- The MTU calculation now correctly accounts for ethernet headers when running in TAP mode +* A data race condition has been fixed when updating session coordinates +* A crash when shutting down when no multicast interfaces are configured has been fixed +* A deadlock when calling `AddPeer` multiple times has been fixed +* A typo in the systemd unit file (for some Linux packages) has been fixed +* The NodeInfo and admin socket now report `unknown` correctly when no build name/version is available in the environment at build time +* The MTU calculation now correctly accounts for ethernet headers when running in TAP mode ## [0.3.11] - 2019-10-25 ### Added -- Support for TLS listeners and peers has been added, allowing the use of `tls://host:port` in `Peers`, `InterfacePeers` and `Listen` configuration settings - this allows hiding Yggdrasil peerings inside regular TLS connections +* Support for TLS listeners and peers has been added, allowing the use of `tls://host:port` in `Peers`, `InterfacePeers` and `Listen` configuration settings - this allows hiding Yggdrasil peerings inside regular TLS connections ### Changed -- Go 1.13 or later is now required for building Yggdrasil -- Some exported API functions have been updated to work with standard Go interfaces: - - `net.Conn` instead of `yggdrasil.Conn` - - `net.Dialer` (the interface it would satisfy if it wasn't a concrete type) instead of `yggdrasil.Dialer` - - `net.Listener` instead of `yggdrasil.Listener` -- Session metadata is now updated correctly when a search completes for a node to which we already have an open session -- Multicast module reloading behaviour has been improved +* Go 1.13 or later is now required for building Yggdrasil +* Some exported API functions have been updated to work with standard Go interfaces: + * `net.Conn` instead of `yggdrasil.Conn` + * `net.Dialer` (the interface it would satisfy if it wasn't a concrete type) instead of `yggdrasil.Dialer` + * `net.Listener` instead of `yggdrasil.Listener` +* Session metadata is now updated correctly when a search completes for a node to which we already have an open session +* Multicast module reloading behaviour has been improved ### Fixed -- An incorrectly held mutex in the crypto-key routing code has been fixed -- Multicast module no longer opens a listener socket if no multicast interfaces are configured +* An incorrectly held mutex in the crypto-key routing code has been fixed +* Multicast module no longer opens a listener socket if no multicast interfaces are configured ## [0.3.10] - 2019-10-10 ### Added -- The core library now includes several unit tests for peering and `yggdrasil.Conn` connections +* The core library now includes several unit tests for peering and `yggdrasil.Conn` connections ### Changed -- On recent Linux kernels, Yggdrasil will now set the `tcp_congestion_control` algorithm used for its own TCP sockets to [BBR](https://github.com/google/bbr), which reduces latency under load -- The systemd service configuration in `contrib` (and, by extension, some of our packages) now attempts to load the `tun` module, in case TUN/TAP support is available but not loaded, and it restricts Yggdrasil to the `CAP_NET_ADMIN` capability for managing the TUN/TAP adapter, rather than letting it do whatever the (typically `root`) user can do +* On recent Linux kernels, Yggdrasil will now set the `tcp_congestion_control` algorithm used for its own TCP sockets to [BBR](https://github.com/google/bbr), which reduces latency under load +* The systemd service configuration in `contrib` (and, by extension, some of our packages) now attempts to load the `tun` module, in case TUN/TAP support is available but not loaded, and it restricts Yggdrasil to the `CAP_NET_ADMIN` capability for managing the TUN/TAP adapter, rather than letting it do whatever the (typically `root`) user can do ### Fixed -- The `yggdrasil.Conn.RemoteAddr()` function no longer blocks, fixing a deadlock when CKR is used while under heavy load +* The `yggdrasil.Conn.RemoteAddr()` function no longer blocks, fixing a deadlock when CKR is used while under heavy load ## [0.3.9] - 2019-09-27 ### Added -- Yggdrasil will now complain more verbosely when a peer URI is incorrectly formatted -- Soft-shutdown methods have been added, allowing a node to shut down gracefully when terminated -- New multicast interval logic which sends multicast beacons more often when Yggdrasil is first started to increase the chance of finding nearby nodes quickly after startup +* Yggdrasil will now complain more verbosely when a peer URI is incorrectly formatted +* Soft-shutdown methods have been added, allowing a node to shut down gracefully when terminated +* New multicast interval logic which sends multicast beacons more often when Yggdrasil is first started to increase the chance of finding nearby nodes quickly after startup ### Changed -- The switch now buffers packets more eagerly in an attempt to give the best link a chance to send, which appears to reduce packet reordering when crossing aggregate sets of peerings -- Substantial amounts of the codebase have been refactored to use the actor model, which should substantially reduce the chance of deadlocks -- Nonce tracking in sessions has been modified so that memory usage is reduced whilst still only allowing duplicate packets within a small window -- Soft-reconfiguration support has been simplified using new actor functions -- The garbage collector threshold has been adjusted for mobile builds -- The maximum queue size is now managed exclusively by the switch rather than by the core +* The switch now buffers packets more eagerly in an attempt to give the best link a chance to send, which appears to reduce packet reordering when crossing aggregate sets of peerings +* Substantial amounts of the codebase have been refactored to use the actor model, which should substantially reduce the chance of deadlocks +* Nonce tracking in sessions has been modified so that memory usage is reduced whilst still only allowing duplicate packets within a small window +* Soft-reconfiguration support has been simplified using new actor functions +* The garbage collector threshold has been adjusted for mobile builds +* The maximum queue size is now managed exclusively by the switch rather than by the core ### Fixed -- The broken `hjson-go` dependency which affected builds of the previous version has now been resolved in the module manifest -- Some minor memory leaks in the switch have been fixed, which improves memory usage on mobile builds -- A memory leak in the add-peer loop has been fixed -- The admin socket now reports the correct URI strings for SOCKS peers in `getPeers` -- A race condition when dialing a remote node by both the node address and routed prefix simultaneously has been fixed -- A race condition between the router and the dial code resulting in a panic has been fixed -- A panic which could occur when the TUN/TAP interface disappears (e.g. during soft-shutdown) has been fixed -- A bug in the semantic versioning script which accompanies Yggdrasil for builds has been fixed -- A panic which could occur when the TUN/TAP interface reads an undersized/corrupted packet has been fixed +* The broken `hjson-go` dependency which affected builds of the previous version has now been resolved in the module manifest +* Some minor memory leaks in the switch have been fixed, which improves memory usage on mobile builds +* A memory leak in the add-peer loop has been fixed +* The admin socket now reports the correct URI strings for SOCKS peers in `getPeers` +* A race condition when dialing a remote node by both the node address and routed prefix simultaneously has been fixed +* A race condition between the router and the dial code resulting in a panic has been fixed +* A panic which could occur when the TUN/TAP interface disappears (e.g. during soft-shutdown) has been fixed +* A bug in the semantic versioning script which accompanies Yggdrasil for builds has been fixed +* A panic which could occur when the TUN/TAP interface reads an undersized/corrupted packet has been fixed ### Removed -- A number of legacy debug functions have now been removed and a number of exported API functions are now better documented +* A number of legacy debug functions have now been removed and a number of exported API functions are now better documented ## [0.3.8] - 2019-08-21 ### Changed -- Yggdrasil can now send multiple packets from the switch at once, which results in improved throughput with smaller packets or lower MTUs -- Performance has been slightly improved by not allocating cancellations where not necessary -- Crypto-key routing options have been renamed for clarity - - `IPv4Sources` is now named `IPv4LocalSubnets` - - `IPv6Sources` is now named `IPv6LocalSubnets` - - `IPv4Destinations` is now named `IPv4RemoteSubnets` - - `IPv6Destinations` is now named `IPv6RemoteSubnets` - - The old option names will continue to be accepted by the configuration parser for now but may not be indefinitely -- When presented with multiple paths between two nodes, the switch now prefers the most recently used port when possible instead of the least recently used, helping to reduce packet reordering -- New nonce tracking should help to reduce the number of packets dropped as a result of multiple/aggregate paths or congestion control in the switch +* Yggdrasil can now send multiple packets from the switch at once, which results in improved throughput with smaller packets or lower MTUs +* Performance has been slightly improved by not allocating cancellations where not necessary +* Crypto-key routing options have been renamed for clarity + * `IPv4Sources` is now named `IPv4LocalSubnets` + * `IPv6Sources` is now named `IPv6LocalSubnets` + * `IPv4Destinations` is now named `IPv4RemoteSubnets` + * `IPv6Destinations` is now named `IPv6RemoteSubnets` + * The old option names will continue to be accepted by the configuration parser for now but may not be indefinitely +* When presented with multiple paths between two nodes, the switch now prefers the most recently used port when possible instead of the least recently used, helping to reduce packet reordering +* New nonce tracking should help to reduce the number of packets dropped as a result of multiple/aggregate paths or congestion control in the switch ### Fixed -- A deadlock was fixed in the session code which could result in Yggdrasil failing to pass traffic after some time +* A deadlock was fixed in the session code which could result in Yggdrasil failing to pass traffic after some time ### Security -- Address verification was not strict enough, which could result in a malicious session sending traffic with unexpected or spoofed source or destination addresses which Yggdrasil could fail to reject - - Versions `0.3.6` and `0.3.7` are vulnerable - users of these versions should upgrade as soon as possible - - Versions `0.3.5` and earlier are not affected +* Address verification was not strict enough, which could result in a malicious session sending traffic with unexpected or spoofed source or destination addresses which Yggdrasil could fail to reject + * Versions `0.3.6` and `0.3.7` are vulnerable - users of these versions should upgrade as soon as possible + * Versions `0.3.5` and earlier are not affected ## [0.3.7] - 2019-08-14 ### Changed -- The switch should now forward packets along a single path more consistently in cases where congestion is low and multiple equal-length paths exist, which should improve stability and result in fewer out-of-order packets -- Sessions should now be more tolerant of out-of-order packets, by replacing a bitmask with a variable sized heap+map structure to track recently received nonces, which should reduce the number of packets dropped due to reordering when multiple paths are used or multiple independent flows are transmitted through the same session -- The admin socket can no longer return a dotfile representation of the known parts of the network, this could be rebuilt by clients using information from `getSwitchPeers`,`getDHT` and `getSessions` +* The switch should now forward packets along a single path more consistently in cases where congestion is low and multiple equal-length paths exist, which should improve stability and result in fewer out-of-order packets +* Sessions should now be more tolerant of out-of-order packets, by replacing a bitmask with a variable sized heap+map structure to track recently received nonces, which should reduce the number of packets dropped due to reordering when multiple paths are used or multiple independent flows are transmitted through the same session +* The admin socket can no longer return a dotfile representation of the known parts of the network, this could be rebuilt by clients using information from `getSwitchPeers`,`getDHT` and `getSessions` ### Fixed -- A number of significant performance regressions introduced in version 0.3.6 have been fixed, resulting in better performance -- Flow labels are now used to prioritise traffic flows again correctly -- In low-traffic scenarios where there are multiple peerings between a pair of nodes, Yggdrasil now prefers the most active peering instead of the least active, helping to reduce packet reordering -- The `Listen` statement, when configured as a string rather than an array, will now be parsed correctly -- The admin socket now returns `coords` as a correct array of unsigned 64-bit integers, rather than the internal representation -- The admin socket now returns `box_pub_key` in string format again -- Sessions no longer leak/block when no listener (e.g. TUN/TAP) is configured -- Incoming session connections no longer block when a session already exists, which results in less leaked goroutines -- Flooded sessions will no longer block other sessions -- Searches are now cleaned up properly and a couple of edge-cases with duplicate searches have been fixed -- A number of minor allocation and pointer fixes +* A number of significant performance regressions introduced in version 0.3.6 have been fixed, resulting in better performance +* Flow labels are now used to prioritise traffic flows again correctly +* In low-traffic scenarios where there are multiple peerings between a pair of nodes, Yggdrasil now prefers the most active peering instead of the least active, helping to reduce packet reordering +* The `Listen` statement, when configured as a string rather than an array, will now be parsed correctly +* The admin socket now returns `coords` as a correct array of unsigned 64-bit integers, rather than the internal representation +* The admin socket now returns `box_pub_key` in string format again +* Sessions no longer leak/block when no listener (e.g. TUN/TAP) is configured +* Incoming session connections no longer block when a session already exists, which results in less leaked goroutines +* Flooded sessions will no longer block other sessions +* Searches are now cleaned up properly and a couple of edge-cases with duplicate searches have been fixed +* A number of minor allocation and pointer fixes ## [0.3.6] - 2019-08-03 ### Added -- Yggdrasil now has a public API with interfaces such as `yggdrasil.ConnDialer`, `yggdrasil.ConnListener` and `yggdrasil.Conn` for using Yggdrasil as a transport directly within applications -- Session gatekeeper functions, part of the API, which can be used to control whether to allow or reject incoming or outgoing sessions dynamically (compared to the previous fixed whitelist/blacklist approach) -- Support for logging to files or syslog (where supported) -- Platform defaults now include the ability to set sane defaults for multicast interfaces +* Yggdrasil now has a public API with interfaces such as `yggdrasil.ConnDialer`, `yggdrasil.ConnListener` and `yggdrasil.Conn` for using Yggdrasil as a transport directly within applications +* Session gatekeeper functions, part of the API, which can be used to control whether to allow or reject incoming or outgoing sessions dynamically (compared to the previous fixed whitelist/blacklist approach) +* Support for logging to files or syslog (where supported) +* Platform defaults now include the ability to set sane defaults for multicast interfaces ### Changed -- Following a massive refactoring exercise, Yggdrasil's codebase has now been broken out into modules -- Core node functionality in the `yggdrasil` package with a public API - - This allows Yggdrasil to be integrated directly into other applications and used as a transport - - IP-specific code has now been moved out of the core `yggdrasil` package, making Yggdrasil effectively protocol-agnostic -- Multicast peer discovery functionality is now in the `multicast` package -- Admin socket functionality is now in the `admin` package and uses the Yggdrasil public API -- TUN/TAP, ICMPv6 and all IP-specific functionality is now in the `tuntap` package -- `PPROF` debug output is now sent to `stderr` instead of `stdout` -- Node IPv6 addresses on macOS are now configured as `secured` -- Upstream dependency references have been updated, which includes a number of fixes in the Water library +* Following a massive refactoring exercise, Yggdrasil's codebase has now been broken out into modules +* Core node functionality in the `yggdrasil` package with a public API + * This allows Yggdrasil to be integrated directly into other applications and used as a transport + * IP-specific code has now been moved out of the core `yggdrasil` package, making Yggdrasil effectively protocol-agnostic +* Multicast peer discovery functionality is now in the `multicast` package +* Admin socket functionality is now in the `admin` package and uses the Yggdrasil public API +* TUN/TAP, ICMPv6 and all IP-specific functionality is now in the `tuntap` package +* `PPROF` debug output is now sent to `stderr` instead of `stdout` +* Node IPv6 addresses on macOS are now configured as `secured` +* Upstream dependency references have been updated, which includes a number of fixes in the Water library ### Fixed -- Multicast discovery is no longer disabled if the nominated interfaces aren't available on the system yet, e.g. during boot -- Multicast interfaces are now re-evaluated more frequently so that Yggdrasil doesn't need to be restarted to use interfaces that have become available since startup -- Admin socket error cases are now handled better -- Various fixes in the TUN/TAP module, particularly surrounding Windows platform support -- Invalid keys will now cause the node to fail to start, rather than starting but silently not working as before -- Session MTUs are now always calculated correctly, in some cases they were incorrectly defaulting to 1280 before -- Multiple searches now don't take place for a single connection -- Concurrency bugs fixed -- Fixed a number of bugs in the ICMPv6 neighbor solicitation in the TUN/TAP code -- A case where peers weren't always added correctly if one or more peers were unreachable has been fixed -- Searches which include the local node are now handled correctly -- Lots of small bug tweaks and clean-ups throughout the codebase +* Multicast discovery is no longer disabled if the nominated interfaces aren't available on the system yet, e.g. during boot +* Multicast interfaces are now re-evaluated more frequently so that Yggdrasil doesn't need to be restarted to use interfaces that have become available since startup +* Admin socket error cases are now handled better +* Various fixes in the TUN/TAP module, particularly surrounding Windows platform support +* Invalid keys will now cause the node to fail to start, rather than starting but silently not working as before +* Session MTUs are now always calculated correctly, in some cases they were incorrectly defaulting to 1280 before +* Multiple searches now don't take place for a single connection +* Concurrency bugs fixed +* Fixed a number of bugs in the ICMPv6 neighbor solicitation in the TUN/TAP code +* A case where peers weren't always added correctly if one or more peers were unreachable has been fixed +* Searches which include the local node are now handled correctly +* Lots of small bug tweaks and clean-ups throughout the codebase ## [0.3.5] - 2019-03-13 ### Fixed -- The `AllowedEncryptionPublicKeys` option has now been fixed to handle incoming connections properly and no longer blocks outgoing connections (this was broken in v0.3.4) -- Multicast TCP listeners will now be stopped correctly when the link-local address on the interface changes or disappears altogether +* The `AllowedEncryptionPublicKeys` option has now been fixed to handle incoming connections properly and no longer blocks outgoing connections (this was broken in v0.3.4) +* Multicast TCP listeners will now be stopped correctly when the link-local address on the interface changes or disappears altogether ## [0.3.4] - 2019-03-12 ### Added -- Support for multiple listeners (although currently only TCP listeners are supported) -- New multicast behaviour where each multicast interface is given its own link-local listener and does not depend on the `Listen` configuration -- Blocking detection in the switch to avoid parenting a blocked peer -- Support for adding and removing listeners and multicast interfaces when reloading configuration during runtime -- Yggdrasil will now attempt to clean up UNIX admin sockets on startup if left behind by a previous crash -- Admin socket `getTunnelRouting` and `setTunnelRouting` calls for enabling and disabling crypto-key routing during runtime -- On macOS, Yggdrasil will now try to wake up AWDL on start-up when `awdl0` is a configured multicast interface, to keep it awake after system sleep, and to stop waking it when no longer needed -- Added `LinkLocalTCPPort` option for controlling the port number that link-local TCP listeners will listen on by default when setting up `MulticastInterfaces` (a node restart is currently required for changes to `LinkLocalTCPPort` to take effect - it cannot be updated by reloading config during runtime) +* Support for multiple listeners (although currently only TCP listeners are supported) +* New multicast behaviour where each multicast interface is given its own link-local listener and does not depend on the `Listen` configuration +* Blocking detection in the switch to avoid parenting a blocked peer +* Support for adding and removing listeners and multicast interfaces when reloading configuration during runtime +* Yggdrasil will now attempt to clean up UNIX admin sockets on startup if left behind by a previous crash +* Admin socket `getTunnelRouting` and `setTunnelRouting` calls for enabling and disabling crypto-key routing during runtime +* On macOS, Yggdrasil will now try to wake up AWDL on start-up when `awdl0` is a configured multicast interface, to keep it awake after system sleep, and to stop waking it when no longer needed +* Added `LinkLocalTCPPort` option for controlling the port number that link-local TCP listeners will listen on by default when setting up `MulticastInterfaces` (a node restart is currently required for changes to `LinkLocalTCPPort` to take effect - it cannot be updated by reloading config during runtime) ### Changed -- The `Listen` configuration statement is now an array instead of a string -- The `Listen` configuration statement should now conform to the same formatting as peers with the protocol prefix, e.g. `tcp://[::]:0` -- Session workers are now non-blocking -- Multicast interval is now fixed at every 15 seconds and network interfaces are reevaluated for eligibility on each interval (where before the interval depended upon the number of configured multicast interfaces and evaluation only took place at startup) -- Dead connections are now closed in the link handler as opposed to the switch -- Peer forwarding is now prioritised instead of randomised +* The `Listen` configuration statement is now an array instead of a string +* The `Listen` configuration statement should now conform to the same formatting as peers with the protocol prefix, e.g. `tcp://[::]:0` +* Session workers are now non-blocking +* Multicast interval is now fixed at every 15 seconds and network interfaces are reevaluated for eligibility on each interval (where before the interval depended upon the number of configured multicast interfaces and evaluation only took place at startup) +* Dead connections are now closed in the link handler as opposed to the switch +* Peer forwarding is now prioritised instead of randomised ### Fixed -- Admin socket `getTunTap` call now returns properly instead of claiming no interface is enabled in all cases -- Handling of `getRoutes` etc in `yggdrasilctl` is now working -- Local interface names are no longer leaked in multicast packets -- Link-local TCP connections, particularly those initiated because of multicast beacons, are now always correctly scoped for the target interface -- Yggdrasil now correctly responds to multicast interfaces going up and down during runtime +* Admin socket `getTunTap` call now returns properly instead of claiming no interface is enabled in all cases +* Handling of `getRoutes` etc in `yggdrasilctl` is now working +* Local interface names are no longer leaked in multicast packets +* Link-local TCP connections, particularly those initiated because of multicast beacons, are now always correctly scoped for the target interface +* Yggdrasil now correctly responds to multicast interfaces going up and down during runtime ## [0.3.3] - 2019-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 +* 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 +* 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` +* Simplified `systemd` service now in `contrib` ### Removed -- `ReadTimeout` option is now deprecated +* `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 -- The ability to hide NodeInfo defaults through either setting the `NodeInfoPrivacy` option or through setting individual `NodeInfo` attributes to `null` +* The admin socket is now multithreaded, greatly improving performance of the crawler and allowing concurrent lookups to take place +* The ability to hide NodeInfo defaults through either setting the `NodeInfoPrivacy` option or through setting individual `NodeInfo` attributes to `null` ### Changed -- The `armhf` build now targets ARMv6 instead of ARMv7, adding support for Raspberry Pi Zero and other older models, amongst others +* The `armhf` build now targets ARMv6 instead of ARMv7, adding support for Raspberry Pi Zero and other older models, amongst others ### Fixed -- DHT entries are now populated using a copy in memory to fix various potential DHT bugs -- DHT traffic should now throttle back exponentially to reduce idle traffic -- Adjust how nodes are inserted into the DHT which should help to reduce some incorrect DHT traffic -- In TAP mode, the NDP target address is now correctly used when populating the peer MAC table. This fixes serious connectivity problems when in TAP mode, particularly on BSD -- In TUN mode, ICMPv6 packets are now ignored whereas they were incorrectly processed before +* DHT entries are now populated using a copy in memory to fix various potential DHT bugs +* DHT traffic should now throttle back exponentially to reduce idle traffic +* Adjust how nodes are inserted into the DHT which should help to reduce some incorrect DHT traffic +* In TAP mode, the NDP target address is now correctly used when populating the peer MAC table. This fixes serious connectivity problems when in TAP mode, particularly on BSD +* In TUN mode, ICMPv6 packets are now ignored whereas they were incorrectly processed before ## [0.3.1] - 2018-12-17 ### Added -- Build name and version is now imprinted onto the binaries if available/specified during build -- Ability to disable admin socket with `AdminListen: none` -- `AF_UNIX` domain sockets for the admin socket -- Cache size restriction for crypto-key routes -- `NodeInfo` support for specifying node information, e.g. node name or contact, which can be used in network crawls or surveys -- `getNodeInfo` request added to admin socket -- Adds flags `-c`, `-l` and `-t` to `build` script for specifying `GCFLAGS`, `LDFLAGS` or whether to keep symbol/DWARF tables +* Build name and version is now imprinted onto the binaries if available/specified during build +* Ability to disable admin socket with `AdminListen: none` +* `AF_UNIX` domain sockets for the admin socket +* Cache size restriction for crypto-key routes +* `NodeInfo` support for specifying node information, e.g. node name or contact, which can be used in network crawls or surveys +* `getNodeInfo` request added to admin socket +* Adds flags `-c`, `-l` and `-t` to `build` script for specifying `GCFLAGS`, `LDFLAGS` or whether to keep symbol/DWARF tables ### Changed -- Default `AdminListen` in newly generated config is now `unix:///var/run/yggdrasil.sock` -- Formatting of `getRoutes` in the admin socket has been improved -- Debian package now adds `yggdrasil` group to assist with `AF_UNIX` admin socket permissions -- Crypto, address and other utility code refactored into separate Go packages +* Default `AdminListen` in newly generated config is now `unix:///var/run/yggdrasil.sock` +* Formatting of `getRoutes` in the admin socket has been improved +* Debian package now adds `yggdrasil` group to assist with `AF_UNIX` admin socket permissions +* Crypto, address and other utility code refactored into separate Go packages ### Fixed -- Switch peer convergence is now much faster again (previously it was taking up to a minute once the peering was established) -- `yggdrasilctl` is now less prone to crashing when parameters are specified incorrectly -- Panic fixed when `Peers` or `InterfacePeers` was commented out +* Switch peer convergence is now much faster again (previously it was taking up to a minute once the peering was established) +* `yggdrasilctl` is now less prone to crashing when parameters are specified incorrectly +* Panic fixed when `Peers` or `InterfacePeers` was commented out ## [0.3.0] - 2018-12-12 ### Added -- Crypto-key routing support for tunnelling both IPv4 and IPv6 over Yggdrasil -- Add advanced `SwitchOptions` in configuration file for tuning the switch -- Add `dhtPing` to the admin socket to aid in crawling the network -- New macOS .pkgs built automatically by CircleCI -- Add Dockerfile to repository for Docker support -- Add `-json` command line flag for generating and normalising configuration in plain JSON instead of HJSON -- Build name and version numbers are now imprinted onto the build, accessible through `yggdrasil -version` and `yggdrasilctl getSelf` -- Add ability to disable admin socket by setting `AdminListen` to `"none"` -- `yggdrasilctl` now tries to look for the default configuration file to find `AdminListen` if `-endpoint` is not specified -- `yggdrasilctl` now returns more useful logging in the event of a fatal error +* Crypto-key routing support for tunnelling both IPv4 and IPv6 over Yggdrasil +* Add advanced `SwitchOptions` in configuration file for tuning the switch +* Add `dhtPing` to the admin socket to aid in crawling the network +* New macOS .pkgs built automatically by CircleCI +* Add Dockerfile to repository for Docker support +* Add `-json` command line flag for generating and normalising configuration in plain JSON instead of HJSON +* Build name and version numbers are now imprinted onto the build, accessible through `yggdrasil -version` and `yggdrasilctl getSelf` +* Add ability to disable admin socket by setting `AdminListen` to `"none"` +* `yggdrasilctl` now tries to look for the default configuration file to find `AdminListen` if `-endpoint` is not specified +* `yggdrasilctl` now returns more useful logging in the event of a fatal error ### Changed -- Switched to Chord DHT (instead of Kademlia, although still compatible at the protocol level) -- The `AdminListen` option and `yggdrasilctl` now default to `unix:///var/run/yggdrasil.sock` on BSDs, macOS and Linux -- Cleaned up some of the parameter naming in the admin socket -- Latency-based parent selection for the switch instead of uptime-based (should help to avoid high latency links somewhat) -- Real peering endpoints now shown in the admin socket `getPeers` call to help identify peerings -- Reuse the multicast port on supported platforms so that multiple Yggdrasil processes can run -- `yggdrasilctl` now has more useful help text (with `-help` or when no arguments passed) +* Switched to Chord DHT (instead of Kademlia, although still compatible at the protocol level) +* The `AdminListen` option and `yggdrasilctl` now default to `unix:///var/run/yggdrasil.sock` on BSDs, macOS and Linux +* Cleaned up some of the parameter naming in the admin socket +* Latency-based parent selection for the switch instead of uptime-based (should help to avoid high latency links somewhat) +* Real peering endpoints now shown in the admin socket `getPeers` call to help identify peerings +* Reuse the multicast port on supported platforms so that multiple Yggdrasil processes can run +* `yggdrasilctl` now has more useful help text (with `-help` or when no arguments passed) ### Fixed -- Memory leaks in the DHT fixed -- Crash fixed where the ICMPv6 NDP goroutine would incorrectly start in TUN mode -- Removing peers from the switch table if they stop sending switch messages but keep the TCP connection alive +* Memory leaks in the DHT fixed +* Crash fixed where the ICMPv6 NDP goroutine would incorrectly start in TUN mode +* Removing peers from the switch table if they stop sending switch messages but keep the TCP connection alive ## [0.2.7] - 2018-10-13 ### Added -- Session firewall, which makes it possible to control who can open sessions with your node -- Add `getSwitchQueues` to admin socket -- Add `InterfacePeers` for configuring static peerings via specific network interfaces -- More output shown in `getSwitchPeers` -- FreeBSD service script in `contrib` +* Session firewall, which makes it possible to control who can open sessions with your node +* Add `getSwitchQueues` to admin socket +* Add `InterfacePeers` for configuring static peerings via specific network interfaces +* More output shown in `getSwitchPeers` +* FreeBSD service script in `contrib` ## Changed -- CircleCI builds are now built with Go 1.11 instead of Go 1.9 +* CircleCI builds are now built with Go 1.11 instead of Go 1.9 ## Fixed -- Race condition in the switch table, reported by trn -- Debug builds are now tested by CircleCI as well as platform release builds -- Port number fixed on admin graph from unknown nodes +* Race condition in the switch table, reported by trn +* Debug builds are now tested by CircleCI as well as platform release builds +* Port number fixed on admin graph from unknown nodes ## [0.2.6] - 2018-07-31 ### Added -- Configurable TCP timeouts to assist in peering over Tor/I2P -- Prefer IPv6 flow label when extending coordinates to sort backpressure queues -- `arm64` builds through CircleCI +* Configurable TCP timeouts to assist in peering over Tor/I2P +* Prefer IPv6 flow label when extending coordinates to sort backpressure queues +* `arm64` builds through CircleCI ### Changed -- Sort dot graph links by integer value +* Sort dot graph links by integer value ## [0.2.5] - 2018-07-19 ### Changed -- Make `yggdrasilctl` less case sensitive -- More verbose TCP disconnect messages +* Make `yggdrasilctl` less case sensitive +* More verbose TCP disconnect messages ### Fixed -- Fixed debug builds -- Cap maximum MTU on Linux in TAP mode -- Process successfully-read TCP traffic before checking for / handling errors (fixes EOF behavior) +* Fixed debug builds +* Cap maximum MTU on Linux in TAP mode +* Process successfully-read TCP traffic before checking for / handling errors (fixes EOF behavior) ## [0.2.4] - 2018-07-08 ### Added -- Support for UNIX domain sockets for the admin socket using `unix:///path/to/file.sock` -- Centralised platform-specific defaults +* Support for UNIX domain sockets for the admin socket using `unix:///path/to/file.sock` +* Centralised platform-specific defaults ### Changed -- Backpressure tuning, including reducing resource consumption +* Backpressure tuning, including reducing resource consumption ### Fixed -- macOS local ping bug, which previously prevented you from pinging your own `utun` adapter's IPv6 address +* macOS local ping bug, which previously prevented you from pinging your own `utun` adapter's IPv6 address ## [0.2.3] - 2018-06-29 ### Added -- Begin keeping changelog (incomplete and possibly inaccurate information before this point). -- Build RPMs in CircleCI using alien. This provides package support for Fedora, Red Hat Enterprise Linux, CentOS and other RPM-based distributions. +* Begin keeping changelog (incomplete and possibly inaccurate information before this point). +* Build RPMs in CircleCI using alien. This provides package support for Fedora, Red Hat Enterprise Linux, CentOS and other RPM-based distributions. ### Changed -- Local backpressure improvements. -- Change `box_pub_key` to `key` in admin API for simplicity. -- Session cleanup. +* Local backpressure improvements. +* Change `box_pub_key` to `key` in admin API for simplicity. +* Session cleanup. ## [0.2.2] - 2018-06-21 ### Added -- Add `yggdrasilconf` utility for testing with the `vyatta-yggdrasil` package. -- Add a randomized retry delay after TCP disconnects, to prevent synchronization livelocks. +* Add `yggdrasilconf` utility for testing with the `vyatta-yggdrasil` package. +* Add a randomized retry delay after TCP disconnects, to prevent synchronization livelocks. ### Changed -- Update build script to strip by default, which significantly reduces the size of the binary. -- Add debug `-d` and UPX `-u` flags to the `build` script. -- Start pprof in debug builds based on an environment variable (e.g. `PPROFLISTEN=localhost:6060`), instead of a flag. +* Update build script to strip by default, which significantly reduces the size of the binary. +* Add debug `-d` and UPX `-u` flags to the `build` script. +* Start pprof in debug builds based on an environment variable (e.g. `PPROFLISTEN=localhost:6060`), instead of a flag. ### Fixed -- Fix typo in big-endian BOM so that both little-endian and big-endian UTF-16 files are detected correctly. +* Fix typo in big-endian BOM so that both little-endian and big-endian UTF-16 files are detected correctly. ## [0.2.1] - 2018-06-15 ### Changed -- The address range was moved from `fd00::/8` to `200::/7`. This range was chosen as it is marked as deprecated. The change prevents overlap with other ULA privately assigned ranges. +* The address range was moved from `fd00::/8` to `200::/7`. This range was chosen as it is marked as deprecated. The change prevents overlap with other ULA privately assigned ranges. ### Fixed -- UTF-16 detection conversion for configuration files, which can particularly be a problem on Windows 10 if a configuration file is generated from within PowerShell. -- Fixes to the Debian package control file. -- Fixes to the launchd service for macOS. -- Fixes to the DHT and switch. +* UTF-16 detection conversion for configuration files, which can particularly be a problem on Windows 10 if a configuration file is generated from within PowerShell. +* Fixes to the Debian package control file. +* Fixes to the launchd service for macOS. +* Fixes to the DHT and switch. ## [0.2.0] - 2018-06-13 ### Added -- Exchange version information during connection setup, to prevent connections with incompatible versions. +* Exchange version information during connection setup, to prevent connections with incompatible versions. ### Changed -- Wire format changes (backwards incompatible). -- Less maintenance traffic per peer. -- Exponential back-off for DHT maintenance traffic (less maintenance traffic for known good peers). -- Iterative DHT (added sometime between v0.1.0 and here). -- Use local queue sizes for a sort of local-only backpressure routing, instead of the removed bandwidth estimates, when deciding where to send a packet. +* Wire format changes (backwards incompatible). +* Less maintenance traffic per peer. +* Exponential back-off for DHT maintenance traffic (less maintenance traffic for known good peers). +* Iterative DHT (added sometime between v0.1.0 and here). +* Use local queue sizes for a sort of local-only backpressure routing, instead of the removed bandwidth estimates, when deciding where to send a packet. ### Removed -- UDP peering, this may be added again if/when a better implementation appears. -- Per peer bandwidth estimation, as this has been replaced with an early local backpressure implementation. +* UDP peering, this may be added again if/when a better implementation appears. +* Per peer bandwidth estimation, as this has been replaced with an early local backpressure implementation. ## [0.1.0] - 2018-02-01 ### Added -- Adopt semantic versioning. +* Adopt semantic versioning. ### Changed -- Wire format changes (backwards incompatible). -- Many other undocumented changes leading up to this release and before the next one. +* Wire format changes (backwards incompatible). +* Many other undocumented changes leading up to this release and before the next one. ## [0.0.1] - 2017-12-28 ### Added -- First commit. -- Initial public release. +* First commit. +* Initial public release. From 74ca02edfdce5bdcdbe32b1e8ad459034ffc6597 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 15 Oct 2023 23:06:10 +0100 Subject: [PATCH 157/293] Don't require TLS client certificate --- src/core/tls.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/tls.go b/src/core/tls.go index 8a17b40e..0ac7e87a 100644 --- a/src/core/tls.go +++ b/src/core/tls.go @@ -8,7 +8,7 @@ import ( func (c *Core) generateTLSConfig(cert *tls.Certificate) (*tls.Config, error) { config := &tls.Config{ Certificates: []tls.Certificate{*cert}, - ClientAuth: tls.RequireAnyClientCert, + ClientAuth: tls.NoClientCert, GetClientCertificate: func(cri *tls.CertificateRequestInfo) (*tls.Certificate, error) { return cert, nil }, From bcd80b043ff6cd11751c784fe19dbf4b1e270ff4 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 17 Oct 2023 21:41:21 +0100 Subject: [PATCH 158/293] Don't tightloop when a listener can no longer accept connections --- src/core/link.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/link.go b/src/core/link.go index bc4f191b..4d51c03a 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -374,7 +374,7 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { for { conn, err := listener.Accept() if err != nil { - continue + return } go func(conn net.Conn) { defer conn.Close() From aceb037c577afb49d06608c21777a93a63e39936 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 18 Oct 2023 22:38:10 +0100 Subject: [PATCH 159/293] Fix panic in mobile `GetPeersJSON` --- contrib/mobile/mobile.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/contrib/mobile/mobile.go b/contrib/mobile/mobile.go index eb79430d..018fd1a3 100644 --- a/contrib/mobile/mobile.go +++ b/contrib/mobile/mobile.go @@ -208,8 +208,11 @@ func (m *Yggdrasil) GetPeersJSON() (result string) { IP string }{} for _, v := range m.core.GetPeers() { - a := address.AddrForKey(v.Key) - ip := net.IP(a[:]).String() + var ip string + if v.Key != nil { + a := address.AddrForKey(v.Key) + ip = net.IP(a[:]).String() + } peers = append(peers, struct { core.PeerInfo IP string From a2053b51fef37c5d625857525fa354c35728bc99 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 18 Oct 2023 22:44:14 +0100 Subject: [PATCH 160/293] Yggdrasil 0.5 RC2 From a2dffeff33f4279cbcb2acc0842702a7daffdb8b Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 18 Oct 2023 22:52:37 +0100 Subject: [PATCH 161/293] Version 0.5 RC2 release notes --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 249faf86..9959abd4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - in case of vulnerabilities. --> +## [0.5.0] - Release Candidate 2 + +### Fixed + +* A bug which could result in high CPU usage after a network interface change has been fixed +* TLS listeners no longer require a TLS client certificate, as it is not necessary +* A panic in the mobile wrapper has been fixed when getting peers JSON + ## [0.5.0] - Release Candidate 1 ### Added From 8ea20cd205a32f22a1f267bf61efc487db2e6b8b Mon Sep 17 00:00:00 2001 From: John Jolly Date: Sun, 16 Apr 2023 19:43:25 -0600 Subject: [PATCH 162/293] Add output for threadcount and key generation time to cmd/genkey This change is to display information about the key generation process. Specifically, two bits of information are now displayed * The number of threads created to search for keys, and * The time taken to generate a successful "next best" key --- cmd/genkeys/main.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmd/genkeys/main.go b/cmd/genkeys/main.go index a12d303e..36107c0a 100644 --- a/cmd/genkeys/main.go +++ b/cmd/genkeys/main.go @@ -16,6 +16,7 @@ import ( "fmt" "net" "runtime" + "time" "github.com/yggdrasil-network/yggdrasil-go/src/address" ) @@ -27,6 +28,8 @@ type keySet struct { func main() { threads := runtime.GOMAXPROCS(0) + fmt.Println("Threads:", threads) + start := time.Now() var currentBest ed25519.PublicKey newKeys := make(chan keySet, threads) for i := 0; i < threads; i++ { @@ -36,7 +39,7 @@ func main() { newKey := <-newKeys if isBetter(currentBest, newKey.pub) || len(currentBest) == 0 { currentBest = newKey.pub - fmt.Println("-----") + fmt.Println("-----", time.Since(start)) fmt.Println("Priv:", hex.EncodeToString(newKey.priv)) fmt.Println("Pub:", hex.EncodeToString(newKey.pub)) addr := address.AddrForKey(newKey.pub) From 6a9493757d88471e4e70ad1323e27d731424ef8e Mon Sep 17 00:00:00 2001 From: Alex Akselrod Date: Sat, 21 Oct 2023 10:33:17 -0700 Subject: [PATCH 163/293] mobile: add support for `Listen` in config (#1063) Co-authored-by: Neil --- contrib/mobile/mobile.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/contrib/mobile/mobile.go b/contrib/mobile/mobile.go index 018fd1a3..31e2f0af 100644 --- a/contrib/mobile/mobile.go +++ b/contrib/mobile/mobile.go @@ -70,6 +70,9 @@ func (m *Yggdrasil) StartJSON(configjson []byte) error { } options = append(options, core.AllowedPublicKey(k[:])) } + for _, lAddr := range m.config.Listen { + options = append(options, core.ListenAddress(lAddr)) + } var err error m.core, err = core.New(m.config.Certificate, logger, options...) if err != nil { From 80e56eafcdb620b61c4e60256d757d826d31b37a Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 21 Oct 2023 21:36:28 +0100 Subject: [PATCH 164/293] Allow `PPROFLISTEN` on all builds --- src/core/debug.go | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/src/core/debug.go b/src/core/debug.go index 6085ceeb..23139f9a 100644 --- a/src/core/debug.go +++ b/src/core/debug.go @@ -1,6 +1,3 @@ -//go:build debug -// +build debug - package core import ( @@ -8,12 +5,9 @@ import ( "net/http" _ "net/http/pprof" "os" - "runtime" - - "github.com/gologme/log" ) -// Start the profiler in debug builds, if the required environment variable is set. +// Start the profiler if the required environment variable is set. func init() { envVarName := "PPROFLISTEN" hostPort := os.Getenv(envVarName) @@ -22,14 +16,6 @@ func init() { fmt.Fprintf(os.Stderr, "DEBUG: %s not set, profiler not started.\n", envVarName) default: fmt.Fprintf(os.Stderr, "DEBUG: Starting pprof on %s\n", hostPort) - go func() { fmt.Println(http.ListenAndServe(hostPort, nil)) }() + go fmt.Println(http.ListenAndServe(hostPort, nil)) } } - -// Starts the function profiler. This is only supported when built with -// '-tags build'. -func StartProfiler(log *log.Logger) error { - runtime.SetBlockProfileRate(1) - go func() { log.Println(http.ListenAndServe("localhost:6060", nil)) }() - return nil -} From 73c6c25bd9d518a5bdc33e40c2f38e44505b706b Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 22 Oct 2023 10:27:41 +0100 Subject: [PATCH 165/293] Restore `removePeer` method --- src/admin/removepeer.go | 11 ++++++- src/core/api.go | 25 ++------------- src/core/link.go | 70 +++++++++++++++++++++++++++++++---------- 3 files changed, 66 insertions(+), 40 deletions(-) diff --git a/src/admin/removepeer.go b/src/admin/removepeer.go index 145b8524..6b2e162e 100644 --- a/src/admin/removepeer.go +++ b/src/admin/removepeer.go @@ -1,5 +1,10 @@ package admin +import ( + "fmt" + "net/url" +) + type RemovePeerRequest struct { Uri string `json:"uri"` Sintf string `json:"interface,omitempty"` @@ -8,5 +13,9 @@ type RemovePeerRequest struct { type RemovePeerResponse struct{} func (a *AdminSocket) removePeerHandler(req *RemovePeerRequest, res *RemovePeerResponse) error { - return a.core.RemovePeer(req.Uri, req.Sintf) + u, err := url.Parse(req.Uri) + if err != nil { + return fmt.Errorf("unable to parse peering URI: %w", err) + } + return a.core.RemovePeer(u, req.Sintf) } diff --git a/src/core/api.go b/src/core/api.go index ebc818f5..b5fa93cb 100644 --- a/src/core/api.go +++ b/src/core/api.go @@ -3,7 +3,6 @@ package core import ( "crypto/ed25519" "encoding/json" - "fmt" "net" "net/url" "sync/atomic" @@ -192,28 +191,8 @@ func (c *Core) AddPeer(u *url.URL, sintf string) error { // RemovePeer removes a peer. The peer should be specified in URI format, see AddPeer. // The peer is not disconnected immediately. -func (c *Core) RemovePeer(uri string, sourceInterface string) error { - return fmt.Errorf("not implemented yet") - /* - var err error - phony.Block(c, func() { - peer := Peer{uri, sourceInterface} - linkInfo, ok := c.config._peers[peer] - if !ok { - err = fmt.Errorf("peer not configured") - return - } - if ok && linkInfo != nil { - c.links.Act(nil, func() { - if link := c.links._links[*linkInfo]; link != nil { - _ = link.conn.Close() - } - }) - } - delete(c.config._peers, peer) - }) - return err - */ +func (c *Core) RemovePeer(u *url.URL, sintf string) error { + return c.links.remove(u, sintf, linkTypePersistent) } // CallPeer calls a peer once. This should be specified in the peer URI format, diff --git a/src/core/link.go b/src/core/link.go index 4d51c03a..c3808698 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -53,9 +53,11 @@ type linkInfo struct { // link tracks the state of a connection, either persistent or non-persistent type link struct { - kick chan struct{} // Attempt to reconnect now, if backing off - linkType linkType // Type of link, i.e. outbound/inbound, persistent/ephemeral - linkProto string // Protocol carrier of link, e.g. TCP, AWDL + ctx context.Context // Connection context + cancel context.CancelFunc // Stop future redial attempts (when peer removed) + kick chan struct{} // Attempt to reconnect now, if backing off + linkType linkType // Type of link, i.e. outbound/inbound, persistent/ephemeral + linkProto string // Protocol carrier of link, e.g. TCP, AWDL // The remaining fields can only be modified safely from within the links actor _conn *linkConn // Connected link, if any, nil if not connected _err error // Last error on the connection, if any @@ -129,6 +131,7 @@ type linkError string func (e linkError) Error() string { return string(e) } const ErrLinkAlreadyConfigured = linkError("peer is already configured") +const ErrLinkNotConfigured = linkError("peer is not configured") const ErrLinkPriorityInvalid = linkError("priority value is invalid") const ErrLinkPinnedKeyInvalid = linkError("pinned public key is invalid") const ErrLinkPasswordInvalid = linkError("password is invalid") @@ -199,6 +202,7 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error { linkProto: strings.ToUpper(u.Scheme), kick: make(chan struct{}), } + state.ctx, state.cancel = context.WithCancel(l.core.ctx) // Store the state of the link so that it can be queried later. l._links[info] = state @@ -218,12 +222,14 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error { backoff++ duration := time.Second * time.Duration(math.Exp2(float64(backoff))) select { - case <-time.After(duration): - return true case <-state.kick: return true + case <-state.ctx.Done(): + return false case <-l.core.ctx.Done(): return false + case <-time.After(duration): + return true } } @@ -232,19 +238,25 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error { // then the loop will run endlessly, using backoffs as needed. // Otherwise the loop will end, cleaning up the link entry. go func() { - defer func() { - phony.Block(l, func() { - if l._links[info] == state { - delete(l._links, info) - } - }) - }() + defer phony.Block(l, func() { + if l._links[info] == state { + delete(l._links, info) + } + }) // This loop will run each and every time we want to attempt // a connection to this peer. // TODO get rid of this loop, this is *exactly* what time.AfterFunc is for, we should just send a signal to the links actor to kick off a goroutine as needed for { - conn, err := l.connect(u, info, options) + select { + case <-state.ctx.Done(): + // The peering context has been cancelled, so don't try + // to dial again. + return + default: + } + + conn, err := l.connect(state.ctx, u, info, options) if err != nil { if linkType == linkTypePersistent { // If the link is a persistent configured peering, @@ -319,13 +331,39 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error { } return } - break } }() }) return retErr } +func (l *links) remove(u *url.URL, sintf string, linkType linkType) error { + var retErr error + phony.Block(l, func() { + // Generate the link info and see whether we think we already + // have an open peering to this peer. + lu := urlForLinkInfo(*u) + info := linkInfo{ + uri: lu.String(), + sintf: sintf, + } + + // If this peer is already configured then we will close the + // connection and stop it from retrying. + state, ok := l._links[info] + if ok && state != nil { + state.cancel() + if conn := state._conn; conn != nil { + retErr = conn.Close() + } + return + } + + retErr = ErrLinkNotConfigured + }) + return retErr +} + func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { ctx, cancel := context.WithCancel(l.core.ctx) var protocol linkProtocol @@ -453,7 +491,7 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { return li, nil } -func (l *links) connect(u *url.URL, info linkInfo, options linkOptions) (net.Conn, error) { +func (l *links) connect(ctx context.Context, u *url.URL, info linkInfo, options linkOptions) (net.Conn, error) { var dialer linkProtocol switch strings.ToLower(u.Scheme) { case "tcp": @@ -485,7 +523,7 @@ func (l *links) connect(u *url.URL, info linkInfo, options linkOptions) (net.Con default: return nil, ErrLinkUnrecognisedSchema } - return dialer.dial(l.core.ctx, u, info, options) + return dialer.dial(ctx, u, info, options) } func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn) error { From 955aa4af79886f05c3d45ea08c8b36de721fd79e Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 22 Oct 2023 10:29:19 +0100 Subject: [PATCH 166/293] Remove unnecessary pprof log line --- src/core/debug.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/core/debug.go b/src/core/debug.go index 23139f9a..a6eb5e2a 100644 --- a/src/core/debug.go +++ b/src/core/debug.go @@ -10,11 +10,7 @@ import ( // Start the profiler if the required environment variable is set. func init() { envVarName := "PPROFLISTEN" - hostPort := os.Getenv(envVarName) - switch { - case hostPort == "": - fmt.Fprintf(os.Stderr, "DEBUG: %s not set, profiler not started.\n", envVarName) - default: + if hostPort := os.Getenv(envVarName); hostPort != "" { fmt.Fprintf(os.Stderr, "DEBUG: Starting pprof on %s\n", hostPort) go fmt.Println(http.ListenAndServe(hostPort, nil)) } From 094f80f39c2fdd25a1cee2eee7584abecea379b0 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 22 Oct 2023 15:51:30 +0100 Subject: [PATCH 167/293] Fix `RetryPeersNow`, move startup logging, don't set TUN address if not available --- cmd/yggdrasil/main.go | 6 +++++- contrib/mobile/mobile.go | 4 ++++ src/core/core.go | 14 ++++++++------ src/tun/tun.go | 7 +++++-- src/tun/tun_bsd.go | 5 ++++- src/tun/tun_darwin.go | 5 ++++- src/tun/tun_linux.go | 5 ++++- src/tun/tun_other.go | 5 ++++- src/tun/tun_windows.go | 8 +++++--- 9 files changed, 43 insertions(+), 16 deletions(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 2aef529d..64cc6ae9 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -67,7 +67,7 @@ func main() { case "syslog": if syslogger, err := gsyslog.NewLogger(gsyslog.LOG_NOTICE, "DAEMON", version.BuildName()); err == nil { - logger = log.New(syslogger, "", log.Flags() &^ (log.Ldate | log.Ltime)) + logger = log.New(syslogger, "", log.Flags()&^(log.Ldate|log.Ltime)) } default: @@ -205,6 +205,10 @@ func main() { if n.core, err = core.New(cfg.Certificate, logger, options...); err != nil { panic(err) } + address, subnet := n.core.Address(), n.core.Subnet() + logger.Infof("Your public key is %s", hex.EncodeToString(n.core.PublicKey())) + logger.Infof("Your IPv6 address is %s", address.String()) + logger.Infof("Your IPv6 subnet is %s", subnet.String()) } // Setup the admin socket. diff --git a/contrib/mobile/mobile.go b/contrib/mobile/mobile.go index 31e2f0af..3e7a5f21 100644 --- a/contrib/mobile/mobile.go +++ b/contrib/mobile/mobile.go @@ -78,6 +78,10 @@ func (m *Yggdrasil) StartJSON(configjson []byte) error { if err != nil { panic(err) } + address, subnet := m.core.Address(), m.core.Subnet() + logger.Infof("Your public key is %s", hex.EncodeToString(m.core.PublicKey())) + logger.Infof("Your IPv6 address is %s", address.String()) + logger.Infof("Your IPv6 subnet is %s", subnet.String()) } // Setup the multicast module. diff --git a/src/core/core.go b/src/core/core.go index e641c8cc..79c59d5d 100644 --- a/src/core/core.go +++ b/src/core/core.go @@ -4,7 +4,6 @@ import ( "context" "crypto/ed25519" "crypto/tls" - "encoding/hex" "fmt" "io" "net" @@ -104,10 +103,6 @@ func New(cert *tls.Certificate, logger Logger, opts ...SetupOption) (*Core, erro ); err != nil { return nil, fmt.Errorf("error creating encryption: %w", err) } - address, subnet := c.Address(), c.Subnet() - c.log.Infof("Your public key is %s", hex.EncodeToString(c.public)) - c.log.Infof("Your IPv6 address is %s", address.String()) - c.log.Infof("Your IPv6 subnet is %s", subnet.String()) c.proto.init(c) if err := c.links.init(c); err != nil { return nil, fmt.Errorf("error initialising links: %w", err) @@ -140,7 +135,14 @@ func New(cert *tls.Certificate, logger Logger, opts ...SetupOption) (*Core, erro } func (c *Core) RetryPeersNow() { - // TODO: figure out a way to retrigger peer connections. + phony.Block(&c.links, func() { + for _, l := range c.links._links { + select { + case l.kick <- struct{}{}: + default: + } + } + }) } // Stop shuts down the Yggdrasil node. diff --git a/src/tun/tun.go b/src/tun/tun.go index cca3af5b..83c82670 100644 --- a/src/tun/tun.go +++ b/src/tun/tun.go @@ -44,7 +44,7 @@ type TunAdapter struct { isOpen bool isEnabled bool // Used by the writer to drop sessionTraffic if not enabled config struct { - fd int32 + fd int32 name InterfaceName mtu InterfaceMTU } @@ -116,7 +116,10 @@ func (tun *TunAdapter) _start() error { tun.addr = tun.rwc.Address() tun.subnet = tun.rwc.Subnet() prefix := address.GetPrefix() - addr := fmt.Sprintf("%s/%d", net.IP(tun.addr[:]).String(), 8*len(prefix[:])-1) + var addr string + if tun.addr.IsValid() { + addr = fmt.Sprintf("%s/%d", net.IP(tun.addr[:]).String(), 8*len(prefix[:])-1) + } if tun.config.name == "none" || tun.config.name == "dummy" { tun.log.Debugln("Not starting TUN as ifname is none or dummy") tun.isEnabled = false diff --git a/src/tun/tun_bsd.go b/src/tun/tun_bsd.go index 3910ccef..5f42f52b 100644 --- a/src/tun/tun_bsd.go +++ b/src/tun/tun_bsd.go @@ -86,7 +86,10 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error { } else { tun.mtu = 0 } - return tun.setupAddress(addr) + if addr != "" { + return tun.setupAddress(addr) + } + return nil } // Configures the "utun" adapter from an existing file descriptor. diff --git a/src/tun/tun_darwin.go b/src/tun/tun_darwin.go index b8e32a2f..8fde93a1 100644 --- a/src/tun/tun_darwin.go +++ b/src/tun/tun_darwin.go @@ -32,7 +32,10 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error { } else { tun.mtu = 0 } - return tun.setupAddress(addr) + if addr != "" { + return tun.setupAddress(addr) + } + return nil } // Configures the "utun" adapter from an existing file descriptor. diff --git a/src/tun/tun_linux.go b/src/tun/tun_linux.go index d87552b7..9cf21446 100644 --- a/src/tun/tun_linux.go +++ b/src/tun/tun_linux.go @@ -27,7 +27,10 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error { } else { tun.mtu = 0 } - return tun.setupAddress(addr) + if addr != "" { + return tun.setupAddress(addr) + } + return nil } // Configures the "utun" adapter from an existing file descriptor. diff --git a/src/tun/tun_other.go b/src/tun/tun_other.go index 075ccfe1..bf8ac892 100644 --- a/src/tun/tun_other.go +++ b/src/tun/tun_other.go @@ -24,7 +24,10 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error { } else { tun.mtu = 0 } - return tun.setupAddress(addr) + if addr != "" { + return tun.setupAddress(addr) + } + return nil } // Configures the "utun" adapter from an existing file descriptor. diff --git a/src/tun/tun_windows.go b/src/tun/tun_windows.go index 10fb5f1a..19d9248c 100644 --- a/src/tun/tun_windows.go +++ b/src/tun/tun_windows.go @@ -35,9 +35,11 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error { return err } tun.iface = iface - if err = tun.setupAddress(addr); err != nil { - tun.log.Errorln("Failed to set up TUN address:", err) - return err + if addr != "" { + if err = tun.setupAddress(addr); err != nil { + tun.log.Errorln("Failed to set up TUN address:", err) + return err + } } if err = tun.setupMTU(getSupportedMTU(mtu)); err != nil { tun.log.Errorln("Failed to set up TUN MTU:", err) From 90c6288f7c2ab2b20d19856387a6c148041ee26d Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 23 Oct 2023 22:26:53 +0100 Subject: [PATCH 168/293] Yggdrasil 0.5 RC3 --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9959abd4..001b3182 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - in case of vulnerabilities. --> +## [0.5.0] - Release Candidate 3 + +### Fixed + +* Restored `removePeer` admin socket endpoint +* Fixed the `RetryPeersNow` API call for mobile + ## [0.5.0] - Release Candidate 2 ### Fixed From a60771344ac2270a324c7ce9f1e2913a8f242038 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 23 Oct 2023 23:42:31 +0100 Subject: [PATCH 169/293] Remove DHT from `yggdrasilctl` help text (fixes #1069) --- cmd/yggdrasilctl/cmd_line_env.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/yggdrasilctl/cmd_line_env.go b/cmd/yggdrasilctl/cmd_line_env.go index d96d6950..b350b7e7 100644 --- a/cmd/yggdrasilctl/cmd_line_env.go +++ b/cmd/yggdrasilctl/cmd_line_env.go @@ -39,8 +39,8 @@ func (cmdLineEnv *CmdLineEnv) parseFlagsAndArgs() { fmt.Println(" - ", os.Args[0], "list") fmt.Println(" - ", os.Args[0], "getPeers") fmt.Println(" - ", os.Args[0], "setTunTap name=auto mtu=1500 tap_mode=false") - fmt.Println(" - ", os.Args[0], "-endpoint=tcp://localhost:9001 getDHT") - fmt.Println(" - ", os.Args[0], "-endpoint=unix:///var/run/ygg.sock getDHT") + fmt.Println(" - ", os.Args[0], "-endpoint=tcp://localhost:9001 getPeers") + fmt.Println(" - ", os.Args[0], "-endpoint=unix:///var/run/ygg.sock getPeers") } server := flag.String("endpoint", cmdLineEnv.endpoint, "Admin socket endpoint") From 7934158f5f49e9eea0fac43b21770b2071bc5cf2 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 24 Oct 2023 12:10:48 +0100 Subject: [PATCH 170/293] Use `ubuntu-20.04` image for Debian packages in CI --- .github/workflows/pkg.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pkg.yml b/.github/workflows/pkg.yml index dd43fce8..615976f0 100644 --- a/.github/workflows/pkg.yml +++ b/.github/workflows/pkg.yml @@ -16,7 +16,7 @@ jobs: name: Package (Debian, ${{ matrix.pkgarch }}) - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v3 with: From 8afa737a8ddf02861e5605982b06f64305a793ba Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 24 Oct 2023 22:44:33 +0100 Subject: [PATCH 171/293] Use `ubuntu-20.04` image for router packages in CI --- .github/workflows/pkg.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pkg.yml b/.github/workflows/pkg.yml index 615976f0..f47baa0f 100644 --- a/.github/workflows/pkg.yml +++ b/.github/workflows/pkg.yml @@ -107,7 +107,7 @@ jobs: name: Package (Router, ${{ matrix.pkgarch }}) - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v3 with: From 6873fd44ffdf764f4733b7efd8834856b1b8c5bc Mon Sep 17 00:00:00 2001 From: Revertron Date: Wed, 25 Oct 2023 20:59:19 +0200 Subject: [PATCH 172/293] Fixes logger, adds some log messages. --- contrib/mobile/mobile.go | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/contrib/mobile/mobile.go b/contrib/mobile/mobile.go index 3e7a5f21..0b52ffa2 100644 --- a/contrib/mobile/mobile.go +++ b/contrib/mobile/mobile.go @@ -48,6 +48,7 @@ func (m *Yggdrasil) StartJSON(configjson []byte) error { logger.EnableLevel("error") logger.EnableLevel("warn") logger.EnableLevel("info") + m.logger = logger m.config = config.GenerateConfig() if err := m.config.UnmarshalHJSON(configjson); err != nil { return err @@ -87,6 +88,7 @@ func (m *Yggdrasil) StartJSON(configjson []byte) error { // Setup the multicast module. if len(m.config.MulticastInterfaces) > 0 { var err error + logger.Infof("Initializing multicast %s", "") options := []multicast.SetupOption{} for _, intf := range m.config.MulticastInterfaces { options = append(options, multicast.MulticastInterface{ @@ -98,9 +100,10 @@ func (m *Yggdrasil) StartJSON(configjson []byte) error { Password: intf.Password, }) } + logger.Infof("Starting multicast %s", "") m.multicast, err = multicast.New(m.core, m.logger, options...) if err != nil { - m.logger.Errorln("An error occurred starting multicast:", err) + logger.Errorln("An error occurred starting multicast:", err) } } @@ -159,15 +162,20 @@ func (m *Yggdrasil) RecvBuffer(buf []byte) (int, error) { func (m *Yggdrasil) Stop() error { logger := log.New(m.log, "", 0) logger.EnableLevel("info") - logger.Infof("Stop the mobile Yggdrasil instance %s", "") - if err := m.multicast.Stop(); err != nil { - return err + logger.Infof("Stopping the mobile Yggdrasil instance %s", "") + if m.multicast != nil { + logger.Infof("Stopping multicast %s", "") + if err := m.multicast.Stop(); err != nil { + return err + } } + logger.Infof("Stopping TUN device %s", "") if m.tun != nil { if err := m.tun.Stop(); err != nil { return err } } + logger.Infof("Stopping Yggdrasil core %s", "") m.core.Stop() return nil } From ea6ccf552f8b5e29b912e5b9d2f91012bca84c50 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Fri, 27 Oct 2023 23:15:34 +0100 Subject: [PATCH 173/293] Update dependencies, test cross-builds for FreeBSD and OpenBSD in CI --- .github/workflows/ci.yml | 26 ++++++++++++++++++++++++++ go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c7cb75f6..92ad2561 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -119,6 +119,32 @@ jobs: - name: Unit tests run: go test -v ./... + build-freebsd: + strategy: + fail-fast: false + matrix: + goversion: ["1.20", "1.21"] + goos: + - freebsd + - openbsd + + name: Build (Cross ${{ matrix.goos }}, Go ${{ matrix.goversion }}) + needs: [lint] + + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: ${{ matrix.goversion }} + + - name: Build Yggdrasil + run: go build -v ./... + env: + GOOS: ${{ matrix.goos }} + tests-ok: name: All tests passed needs: [lint, codeql, build-linux, build-windows, build-macos] diff --git a/go.mod b/go.mod index ed6c47c8..3f2f6b76 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/hashicorp/go-syslog v1.0.0 github.com/hjson/hjson-go/v4 v4.3.0 github.com/kardianos/minwinsvc v1.0.2 - github.com/quic-go/quic-go v0.39.0 + github.com/quic-go/quic-go v0.39.3 github.com/vishvananda/netlink v1.1.0 golang.org/x/crypto v0.14.0 golang.org/x/mobile v0.0.0-20231006135142-2b44d11868fe diff --git a/go.sum b/go.sum index 6c0bdb11..e8bbf5f1 100644 --- a/go.sum +++ b/go.sum @@ -52,8 +52,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/quic-go/qtls-go1-20 v0.3.4 h1:MfFAPULvst4yoMgY9QmtpYmfij/em7O8UUi+bNVm7Cg= github.com/quic-go/qtls-go1-20 v0.3.4/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= -github.com/quic-go/quic-go v0.39.0 h1:AgP40iThFMY0bj8jGxROhw3S0FMGa8ryqsmi9tBH3So= -github.com/quic-go/quic-go v0.39.0/go.mod h1:T09QsDQWjLiQ74ZmacDfqZmhY/NLnw5BC40MANNNZ1Q= +github.com/quic-go/quic-go v0.39.3 h1:o3YB6t2SR+HU/pgwF29kJ6g4jJIJEwEZ8CKia1h1TKg= +github.com/quic-go/quic-go v0.39.3/go.mod h1:T09QsDQWjLiQ74ZmacDfqZmhY/NLnw5BC40MANNNZ1Q= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= From d17ac397898391129c834f9966464a6072513249 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sat, 28 Oct 2023 05:26:43 -0500 Subject: [PATCH 174/293] update ironwood dependency, add a debug API call for lookups --- cmd/yggdrasil/main.go | 3 +++ contrib/mobile/mobile.go | 8 +++--- go.mod | 2 +- go.sum | 4 +-- src/admin/options.go | 54 ++++++++++++++++++++++++++++++++++++++++ src/config/config.go | 1 + 6 files changed, 65 insertions(+), 7 deletions(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 64cc6ae9..7f820db5 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -216,6 +216,9 @@ func main() { options := []admin.SetupOption{ admin.ListenAddress(cfg.AdminListen), } + if cfg.LogLookups { + options = append(options, admin.LogLookups{}) + } if n.admin, err = admin.New(n.core, logger, options...); err != nil { panic(err) } diff --git a/contrib/mobile/mobile.go b/contrib/mobile/mobile.go index 0b52ffa2..cf026a70 100644 --- a/contrib/mobile/mobile.go +++ b/contrib/mobile/mobile.go @@ -164,10 +164,10 @@ func (m *Yggdrasil) Stop() error { logger.EnableLevel("info") logger.Infof("Stopping the mobile Yggdrasil instance %s", "") if m.multicast != nil { - logger.Infof("Stopping multicast %s", "") - if err := m.multicast.Stop(); err != nil { - return err - } + logger.Infof("Stopping multicast %s", "") + if err := m.multicast.Stop(); err != nil { + return err + } } logger.Infof("Stopping TUN device %s", "") if m.tun != nil { diff --git a/go.mod b/go.mod index 3f2f6b76..faa0e0d8 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/yggdrasil-network/yggdrasil-go go 1.20 require ( - github.com/Arceliar/ironwood v0.0.0-20230805085300-86206813435f + github.com/Arceliar/ironwood v0.0.0-20231028101932-ceac99571f43 github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d github.com/cheggaaa/pb/v3 v3.1.4 github.com/gologme/log v1.3.0 diff --git a/go.sum b/go.sum index e8bbf5f1..901eabaa 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Arceliar/ironwood v0.0.0-20230805085300-86206813435f h1:Fz0zG7ZyQQqk+ROnmHuGrIZO250Lx/YHmp9o48XE+Vw= -github.com/Arceliar/ironwood v0.0.0-20230805085300-86206813435f/go.mod h1:5x7fWW0mshe9WQ1lvSMmmHBYC3BeHH9gpwW5tz7cbfw= +github.com/Arceliar/ironwood v0.0.0-20231028101932-ceac99571f43 h1:M4NczBPk7Fy0Uy2YvNoXwSkk3dGoGTOYtUjyqpOC5ko= +github.com/Arceliar/ironwood v0.0.0-20231028101932-ceac99571f43/go.mod h1:5x7fWW0mshe9WQ1lvSMmmHBYC3BeHH9gpwW5tz7cbfw= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= diff --git a/src/admin/options.go b/src/admin/options.go index 4607384f..b2e273b3 100644 --- a/src/admin/options.go +++ b/src/admin/options.go @@ -1,9 +1,20 @@ package admin +import ( + "encoding/hex" + "encoding/json" + "sync" + "time" + + "github.com/Arceliar/ironwood/network" +) + func (c *AdminSocket) _applyOption(opt SetupOption) { switch v := opt.(type) { case ListenAddress: c.config.listenaddr = v + case LogLookups: + c.logLookups() } } @@ -14,3 +25,46 @@ type SetupOption interface { type ListenAddress string func (a ListenAddress) isSetupOption() {} + +type LogLookups struct{} + +func (l LogLookups) isSetupOption() {} + +func (a *AdminSocket) logLookups() { + type resi struct { + Key string `json:"key"` + Path []uint64 `json:"path"` + Time int64 `json:"time"` + } + type res struct { + Infos []resi `json:"infos"` + } + type info struct { + path []uint64 + time time.Time + } + infos := make(map[string]info) + var m sync.Mutex + a.core.PacketConn.PacketConn.Debug.SetDebugLookupLogger(func(l network.DebugLookupInfo) { + key := hex.EncodeToString(l.Key[:]) + m.Lock() + infos[key] = info{path: l.Path, time: time.Now()} + m.Unlock() + }) + _ = a.AddHandler( + "lookups", "Dump a record of lookups received in the past hour", []string{}, + func(in json.RawMessage) (interface{}, error) { + m.Lock() + rs := make([]resi, 0, len(infos)) + for k, v := range infos { + if time.Since(v.time) > 24*time.Hour { + // TODO? automatic cleanup, so we don't need to call lookups periodically to prevent leaks + delete(infos, k) + } + rs = append(rs, resi{Key: k, Path: v.path, Time: v.time.Unix()}) + } + m.Unlock() + return &res{Infos: rs}, nil + }, + ) +} diff --git a/src/config/config.go b/src/config/config.go index bb94b674..289b6f92 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -53,6 +53,7 @@ type NodeConfig struct { IfMTU uint64 `comment:"Maximum Transmission Unit (MTU) size for your local TUN interface.\nDefault is the largest supported size for your platform. The lowest\npossible value is 1280."` NodeInfoPrivacy bool `comment:"By default, nodeinfo contains some defaults including the platform,\narchitecture and Yggdrasil version. These can help when surveying\nthe network and diagnosing network routing problems. Enabling\nnodeinfo privacy prevents this, so that only items specified in\n\"NodeInfo\" are sent back if specified."` NodeInfo map[string]interface{} `comment:"Optional node info. This must be a { \"key\": \"value\", ... } map\nor set as null. This is entirely optional but, if set, is visible\nto the whole network on request."` + LogLookups bool `json:",omitempty"` } type MulticastInterfaceConfig struct { From 82c54f87eae55c7f0acbb1dfb91a91482b393ce3 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sat, 28 Oct 2023 06:36:01 -0500 Subject: [PATCH 175/293] clean up some debug API output --- src/admin/options.go | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/admin/options.go b/src/admin/options.go index b2e273b3..03cf74ab 100644 --- a/src/admin/options.go +++ b/src/admin/options.go @@ -1,12 +1,16 @@ package admin import ( + "crypto/ed25519" "encoding/hex" "encoding/json" + "net" "sync" "time" "github.com/Arceliar/ironwood/network" + + "github.com/yggdrasil-network/yggdrasil-go/src/address" ) func (c *AdminSocket) _applyOption(opt SetupOption) { @@ -32,9 +36,10 @@ func (l LogLookups) isSetupOption() {} func (a *AdminSocket) logLookups() { type resi struct { - Key string `json:"key"` - Path []uint64 `json:"path"` - Time int64 `json:"time"` + Address string `json:"addr"` + Key string `json:"key"` + Path []uint64 `json:"path"` + Time int64 `json:"time"` } type res struct { Infos []resi `json:"infos"` @@ -43,12 +48,14 @@ func (a *AdminSocket) logLookups() { path []uint64 time time.Time } - infos := make(map[string]info) + type edk [ed25519.PublicKeySize]byte + infos := make(map[edk]info) var m sync.Mutex a.core.PacketConn.PacketConn.Debug.SetDebugLookupLogger(func(l network.DebugLookupInfo) { - key := hex.EncodeToString(l.Key[:]) + var k edk + copy(k[:], l.Key[:]) m.Lock() - infos[key] = info{path: l.Path, time: time.Now()} + infos[k] = info{path: l.Path, time: time.Now()} m.Unlock() }) _ = a.AddHandler( @@ -61,7 +68,9 @@ func (a *AdminSocket) logLookups() { // TODO? automatic cleanup, so we don't need to call lookups periodically to prevent leaks delete(infos, k) } - rs = append(rs, resi{Key: k, Path: v.path, Time: v.time.Unix()}) + a := address.AddrForKey(ed25519.PublicKey(k[:])) + addr := net.IP(a[:]).String() + rs = append(rs, resi{Address: addr, Key: hex.EncodeToString(k[:]), Path: v.path, Time: v.time.Unix()}) } m.Unlock() return &res{Infos: rs}, nil From 0b578a637a86468f22a6e142901340d93889e52b Mon Sep 17 00:00:00 2001 From: Neil Date: Sat, 28 Oct 2023 14:58:52 +0100 Subject: [PATCH 176/293] Debian package updates (#1073) * Update Debian package * Don't put `AdminListen` in config by default, fix path in Debian package * Fix path in unit file * Preserve original service files for other packages --------- Co-authored-by: Neil Alexander --- build | 2 +- cmd/yggdrasil/main.go | 1 + contrib/.DS_Store | Bin 0 -> 6148 bytes contrib/deb/generate.sh | 65 ++++++++++++------ .../yggdrasil-default-config.service.debian | 13 ++++ contrib/systemd/yggdrasil.service.debian | 25 +++++++ src/config/config.go | 2 +- 7 files changed, 84 insertions(+), 24 deletions(-) create mode 100644 contrib/.DS_Store create mode 100644 contrib/systemd/yggdrasil-default-config.service.debian create mode 100644 contrib/systemd/yggdrasil.service.debian diff --git a/build b/build index c7214438..de5d9edc 100755 --- a/build +++ b/build @@ -6,7 +6,7 @@ PKGSRC=${PKGSRC:-github.com/yggdrasil-network/yggdrasil-go/src/version} PKGNAME=${PKGNAME:-$(sh contrib/semver/name.sh)} PKGVER=${PKGVER:-$(sh contrib/semver/version.sh --bare)} -LDFLAGS="-X $PKGSRC.buildName=$PKGNAME -X $PKGSRC.buildVersion=$PKGVER" +LDFLAGS="${LDFLAGS} -X $PKGSRC.buildName=$PKGNAME -X $PKGSRC.buildVersion=$PKGVER" ARGS="-v" while getopts "utc:l:dro:p" option diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 7f820db5..e0b33990 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -113,6 +113,7 @@ func main() { _ = f.Close() case *genconf: + cfg.AdminListen = "" var bs []byte if *confjson { bs, err = json.MarshalIndent(cfg, "", " ") diff --git a/contrib/.DS_Store b/contrib/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..6116147462bb7351bfd2bb86ff6280de2c119bb6 GIT binary patch literal 6148 zcmeHKO^?$s5FNMOHf2HT0nlEMB5`e@%g4fsOX)5Lt`xxmP)V9ns!ii6Nw=V?QqS-o z_zQ>|e+2#uCwODKRg$t-c7;5Z{T%zv#Ce(6H4%yKEZ8S%5RrqzSh>vB!lRE-Zv9q<7B2-JP4V+6 zrU^-k_4mHve=YDYBkBy%KLRCvpcd{Ep`>{~aiic@rhtaz7g(PQtSVTN26O^$VJ6~N zfm@?@nDdaMVm?7Wa%ogxSFn~z8j&pUew@Yg=gHI$l@8ziX*f>OcKfF&l`A{dnp1Oj zoj1Mra^6e5be43z_!VD0mooI%ZO?x(9?tuXt4A_Qy>T>*mBTm~g7W(1I11#vD`!!V zDBqqQaOzII-`HC$4h|2R?&1Aq(_QS}ZQp6S5AHo&F6+*z{;^NPfcHVFIhQ zvMUDH!0K+E`?MEFp^R7;=I`Kbdr(?Zl2@dBgwbqw}BJ*fF@$ zsFn^?>IeX=pj#Q*{HK9^T!S5hD~%X|3GE8huEHEKgmy>2Yj_=lD~;NnggJZ&b7x^r zC_>#G@m)nH(a~sYtAJG?uRvK}HhBMka`yQ@?_{s60#){dN|%| uU6i*dY|L9}R4%B@cB~q_74M-a! /tmp/$PKGNAME/debian/changelog << EOF Please see https://github.com/yggdrasil-network/yggdrasil-go/ @@ -68,35 +71,52 @@ EOF cat > /tmp/$PKGNAME/debian/install << EOF usr/bin/yggdrasil usr/bin usr/bin/yggdrasilctl usr/bin -etc/systemd/system/*.service etc/systemd/system +usr/lib/systemd/system/*.service usr/lib/systemd/system EOF cat > /tmp/$PKGNAME/debian/postinst << EOF #!/bin/sh +systemctl daemon-reload + if ! getent group yggdrasil 2>&1 > /dev/null; then - groupadd --system --force yggdrasil || echo "Failed to create group 'yggdrasil' - please create it manually and reinstall" + groupadd --system --force yggdrasil fi -if [ -f /etc/yggdrasil.conf ]; +if [ ! -d /etc/yggdrasil ]; +then + mkdir -p /etc/yggdrasil + chown root:yggdrasil /etc/yggdrasil + chmod 750 /etc/yggdrasil +fi + +if [ ! -f /etc/yggdrasil/yggdrasil.conf ]; +then + test -f /etc/yggdrasil.conf && mv /etc/yggdrasil.conf /etc/yggdrasil/yggdrasil.conf +fi + +if [ -f /etc/yggdrasil/yggdrasil.conf ]; then mkdir -p /var/backups echo "Backing up configuration file to /var/backups/yggdrasil.conf.`date +%Y%m%d`" - cp /etc/yggdrasil.conf /var/backups/yggdrasil.conf.`date +%Y%m%d` - echo "Normalising and updating /etc/yggdrasil.conf" - /usr/bin/yggdrasil -useconf -normaliseconf < /var/backups/yggdrasil.conf.`date +%Y%m%d` > /etc/yggdrasil.conf - chgrp yggdrasil /etc/yggdrasil.conf + cp /etc/yggdrasil/yggdrasil.conf /var/backups/yggdrasil.conf.`date +%Y%m%d` - if command -v systemctl >/dev/null; then - systemctl daemon-reload >/dev/null || true - systemctl enable yggdrasil || true - systemctl start yggdrasil || true - fi + echo "Normalising and updating /etc/yggdrasil/yggdrasil.conf" + /usr/bin/yggdrasil -useconf -normaliseconf < /var/backups/yggdrasil.conf.`date +%Y%m%d` > /etc/yggdrasil/yggdrasil.conf + + chown root:yggdrasil /etc/yggdrasil/yggdrasil.conf + chmod 640 /etc/yggdrasil/yggdrasil.conf else - echo "Generating initial configuration file /etc/yggdrasil.conf" - echo "Please familiarise yourself with this file before starting Yggdrasil" - sh -c 'umask 0027 && /usr/bin/yggdrasil -genconf > /etc/yggdrasil.conf' - chgrp yggdrasil /etc/yggdrasil.conf + echo "Generating initial configuration file /etc/yggdrasil/yggdrasil.conf" + /usr/bin/yggdrasil -genconf > /etc/yggdrasil/yggdrasil.conf + + chown root:yggdrasil /etc/yggdrasil/yggdrasil.conf + chmod 640 /etc/yggdrasil/yggdrasil.conf fi + +systemctl enable yggdrasil +systemctl restart yggdrasil + +exit 0 EOF cat > /tmp/$PKGNAME/debian/prerm << EOF #!/bin/sh @@ -110,13 +130,14 @@ EOF cp yggdrasil /tmp/$PKGNAME/usr/bin/ cp yggdrasilctl /tmp/$PKGNAME/usr/bin/ -cp contrib/systemd/*.service /tmp/$PKGNAME/etc/systemd/system/ +cp contrib/systemd/yggdrasil-default-config.service.debian /tmp/$PKGNAME/usr/lib/systemd/system/yggdrasil-default-config.service +cp contrib/systemd/yggdrasil.service.debian /tmp/$PKGNAME/usr/lib/systemd/system/yggdrasil.service -tar -czvf /tmp/$PKGNAME/data.tar.gz -C /tmp/$PKGNAME/ \ +tar --no-xattrs -czvf /tmp/$PKGNAME/data.tar.gz -C /tmp/$PKGNAME/ \ usr/bin/yggdrasil usr/bin/yggdrasilctl \ - etc/systemd/system/yggdrasil.service \ - etc/systemd/system/yggdrasil-default-config.service -tar -czvf /tmp/$PKGNAME/control.tar.gz -C /tmp/$PKGNAME/debian . + usr/lib/systemd/system/yggdrasil.service \ + usr/lib/systemd/system/yggdrasil-default-config.service +tar --no-xattrs -czvf /tmp/$PKGNAME/control.tar.gz -C /tmp/$PKGNAME/debian . echo 2.0 > /tmp/$PKGNAME/debian-binary ar -r $PKGFILE \ diff --git a/contrib/systemd/yggdrasil-default-config.service.debian b/contrib/systemd/yggdrasil-default-config.service.debian new file mode 100644 index 00000000..dc3fdc5a --- /dev/null +++ b/contrib/systemd/yggdrasil-default-config.service.debian @@ -0,0 +1,13 @@ +[Unit] +Description=Yggdrasil default config generator +ConditionPathExists=|!/etc/yggdrasil/yggdrasil.conf +ConditionFileNotEmpty=|!/etc/yggdrasil/yggdrasil.conf +Wants=local-fs.target +After=local-fs.target + +[Service] +Type=oneshot +Group=yggdrasil +ExecStartPre=/usr/bin/mkdir -p /etc/yggdrasil +ExecStart=/usr/bin/yggdrasil -genconf > /etc/yggdrasil/yggdrasil.conf +ExecStartPost=/usr/bin/chmod -R 0640 /etc/yggdrasil diff --git a/contrib/systemd/yggdrasil.service.debian b/contrib/systemd/yggdrasil.service.debian new file mode 100644 index 00000000..0f3c7a8d --- /dev/null +++ b/contrib/systemd/yggdrasil.service.debian @@ -0,0 +1,25 @@ +[Unit] +Description=Yggdrasil Network +Wants=network-online.target +Wants=yggdrasil-default-config.service +After=network-online.target +After=yggdrasil-default-config.service + +[Service] +Group=yggdrasil +ProtectHome=true +ProtectSystem=strict +NoNewPrivileges=true +RuntimeDirectory=yggdrasil +ReadWritePaths=/var/run/yggdrasil/ /run/yggdrasil/ +SyslogIdentifier=yggdrasil +CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE +AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE +ExecStartPre=+-/sbin/modprobe tun +ExecStart=/usr/bin/yggdrasil -useconffile /etc/yggdrasil/yggdrasil.conf +ExecReload=/bin/kill -HUP $MAINPID +Restart=always +TimeoutStopSec=5 + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/src/config/config.go b/src/config/config.go index 289b6f92..e899a35d 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -46,7 +46,7 @@ type NodeConfig struct { Peers []string `comment:"List of connection strings for outbound peer connections in URI format,\ne.g. tls://a.b.c.d:e or socks://a.b.c.d:e/f.g.h.i:j. These connections\nwill obey the operating system routing table, therefore you should\nuse this section when you may connect via different interfaces."` InterfacePeers map[string][]string `comment:"List of connection strings for outbound peer connections in URI format,\narranged by source interface, e.g. { \"eth0\": [ \"tls://a.b.c.d:e\" ] }.\nNote that SOCKS peerings will NOT be affected by this option and should\ngo in the \"Peers\" section instead."` Listen []string `comment:"Listen addresses for incoming connections. You will need to add\nlisteners in order to accept incoming peerings from non-local nodes.\nMulticast peer discovery will work regardless of any listeners set\nhere. Each listener should be specified in URI format as above, e.g.\ntls://0.0.0.0:0 or tls://[::]:0 to listen on all interfaces."` - 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."` + AdminListen string `json:",omitempty" 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."` MulticastInterfaces []MulticastInterfaceConfig `comment:"Configuration for which interfaces multicast peer discovery should be\nenabled on. Each entry in the list should be a json object which may\ncontain Regex, Beacon, Listen, and Port. Regex is a regular expression\nwhich is matched against an interface name, and interfaces use the\nfirst configuration that they match gainst. Beacon configures whether\nor not the node should send link-local multicast beacons to advertise\ntheir presence, while listening for incoming connections on Port.\nListen controls whether or not the node listens for multicast beacons\nand opens outgoing connections."` AllowedPublicKeys []string `comment:"List of peer public keys to allow incoming peering connections\nfrom. If left empty/undefined then all connections will be allowed\nby default. This does not affect outgoing peerings, nor does it\naffect link-local peers discovered via multicast."` IfName string `comment:"Local network interface name for TUN adapter, or \"auto\" to select\nan interface automatically, or \"none\" to run without TUN."` From 01c1498bd52291ba246c8f00d0bd5511b16c1959 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 28 Oct 2023 15:07:45 +0100 Subject: [PATCH 177/293] Yggdrasil 0.5 release notes --- CHANGELOG.md | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 001b3182..50296af0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,22 +26,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - in case of vulnerabilities. --> -## [0.5.0] - Release Candidate 3 - -### Fixed - -* Restored `removePeer` admin socket endpoint -* Fixed the `RetryPeersNow` API call for mobile - -## [0.5.0] - Release Candidate 2 - -### Fixed - -* A bug which could result in high CPU usage after a network interface change has been fixed -* TLS listeners no longer require a TLS client certificate, as it is not necessary -* A panic in the mobile wrapper has been fixed when getting peers JSON - -## [0.5.0] - Release Candidate 1 +## [0.5.0] - 2023-10-28 ### Added From d781fef760d2e8152f2fa955e51ee03028a3da9a Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 28 Oct 2023 15:20:56 +0100 Subject: [PATCH 178/293] Release: Yggdrasil 0.5.0 --- contrib/deb/generate.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/contrib/deb/generate.sh b/contrib/deb/generate.sh index 5b4275a9..41ca6fe5 100644 --- a/contrib/deb/generate.sh +++ b/contrib/deb/generate.sh @@ -41,7 +41,7 @@ echo "Building $PKGFILE" mkdir -p /tmp/$PKGNAME/ mkdir -p /tmp/$PKGNAME/debian/ mkdir -p /tmp/$PKGNAME/usr/bin/ -mkdir -p /tmp/$PKGNAME/usr/lib/systemd/system/ +mkdir -p /tmp/$PKGNAME/lib/systemd/system/ cat > /tmp/$PKGNAME/debian/changelog << EOF Please see https://github.com/yggdrasil-network/yggdrasil-go/ @@ -71,7 +71,7 @@ EOF cat > /tmp/$PKGNAME/debian/install << EOF usr/bin/yggdrasil usr/bin usr/bin/yggdrasilctl usr/bin -usr/lib/systemd/system/*.service usr/lib/systemd/system +lib/systemd/system/*.service lib/systemd/system EOF cat > /tmp/$PKGNAME/debian/postinst << EOF #!/bin/sh @@ -130,13 +130,13 @@ EOF cp yggdrasil /tmp/$PKGNAME/usr/bin/ cp yggdrasilctl /tmp/$PKGNAME/usr/bin/ -cp contrib/systemd/yggdrasil-default-config.service.debian /tmp/$PKGNAME/usr/lib/systemd/system/yggdrasil-default-config.service -cp contrib/systemd/yggdrasil.service.debian /tmp/$PKGNAME/usr/lib/systemd/system/yggdrasil.service +cp contrib/systemd/yggdrasil-default-config.service.debian /tmp/$PKGNAME/lib/systemd/system/yggdrasil-default-config.service +cp contrib/systemd/yggdrasil.service.debian /tmp/$PKGNAME/lib/systemd/system/yggdrasil.service tar --no-xattrs -czvf /tmp/$PKGNAME/data.tar.gz -C /tmp/$PKGNAME/ \ usr/bin/yggdrasil usr/bin/yggdrasilctl \ - usr/lib/systemd/system/yggdrasil.service \ - usr/lib/systemd/system/yggdrasil-default-config.service + lib/systemd/system/yggdrasil.service \ + lib/systemd/system/yggdrasil-default-config.service tar --no-xattrs -czvf /tmp/$PKGNAME/control.tar.gz -C /tmp/$PKGNAME/debian . echo 2.0 > /tmp/$PKGNAME/debian-binary From a6b316ef085553761b20af61ecfc4acefbf2bbec Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 28 Oct 2023 16:21:50 +0100 Subject: [PATCH 179/293] Release: Yggdrasil 0.5.1 --- CHANGELOG.md | 6 ++++++ cmd/yggdrasil/main.go | 1 + contrib/deb/generate.sh | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 50296af0..d0e9e3da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - in case of vulnerabilities. --> +## [0.5.1] - 2023-10-28 + +### Fixed + +* Fix the Debian package so that upgrades are handled more smoothly + ## [0.5.0] - 2023-10-28 ### Added diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index e0b33990..975e4945 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -156,6 +156,7 @@ func main() { return case *normaliseconf: + cfg.AdminListen = "" var bs []byte if *confjson { bs, err = json.MarshalIndent(cfg, "", " ") diff --git a/contrib/deb/generate.sh b/contrib/deb/generate.sh index 41ca6fe5..69eaa764 100644 --- a/contrib/deb/generate.sh +++ b/contrib/deb/generate.sh @@ -22,7 +22,7 @@ if [ $PKGBRANCH = "master" ]; then fi export LDFLAGS="-X github.com/yggdrasil-network/yggdrasil-go/src/config.defaultConfig=/etc/yggdrasil/yggdrasil.conf" -export LDFLAGS="${LDFLAGS} -X github.com/yggdrasil-network/yggdrasil-go/src/config.defaultAdminListen=unix://var/run/yggdrasil/yggdrasil.sock" +export LDFLAGS="${LDFLAGS} -X github.com/yggdrasil-network/yggdrasil-go/src/config.defaultAdminListen=unix:///var/run/yggdrasil/yggdrasil.sock" if [ $PKGARCH = "amd64" ]; then GOARCH=amd64 GOOS=linux ./build elif [ $PKGARCH = "i386" ]; then GOARCH=386 GOOS=linux ./build From 7f9d4f3f6d06262e212a8dd101d51fda134332da Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 28 Oct 2023 18:21:26 +0100 Subject: [PATCH 180/293] Don't import `LDFLAGS` from the environment --- build | 2 +- contrib/deb/generate.sh | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/build b/build index de5d9edc..c7214438 100755 --- a/build +++ b/build @@ -6,7 +6,7 @@ PKGSRC=${PKGSRC:-github.com/yggdrasil-network/yggdrasil-go/src/version} PKGNAME=${PKGNAME:-$(sh contrib/semver/name.sh)} PKGVER=${PKGVER:-$(sh contrib/semver/version.sh --bare)} -LDFLAGS="${LDFLAGS} -X $PKGSRC.buildName=$PKGNAME -X $PKGSRC.buildVersion=$PKGVER" +LDFLAGS="-X $PKGSRC.buildName=$PKGNAME -X $PKGSRC.buildVersion=$PKGVER" ARGS="-v" while getopts "utc:l:dro:p" option diff --git a/contrib/deb/generate.sh b/contrib/deb/generate.sh index 69eaa764..8d39eb1d 100644 --- a/contrib/deb/generate.sh +++ b/contrib/deb/generate.sh @@ -21,16 +21,16 @@ if [ $PKGBRANCH = "master" ]; then PKGREPLACES=yggdrasil-develop fi -export LDFLAGS="-X github.com/yggdrasil-network/yggdrasil-go/src/config.defaultConfig=/etc/yggdrasil/yggdrasil.conf" -export LDFLAGS="${LDFLAGS} -X github.com/yggdrasil-network/yggdrasil-go/src/config.defaultAdminListen=unix:///var/run/yggdrasil/yggdrasil.sock" +GOLDFLAGS="-X github.com/yggdrasil-network/yggdrasil-go/src/config.defaultConfig=/etc/yggdrasil/yggdrasil.conf" +GOLDFLAGS="${GOLDFLAGS} -X github.com/yggdrasil-network/yggdrasil-go/src/config.defaultAdminListen=unix:///var/run/yggdrasil/yggdrasil.sock" -if [ $PKGARCH = "amd64" ]; then GOARCH=amd64 GOOS=linux ./build -elif [ $PKGARCH = "i386" ]; then GOARCH=386 GOOS=linux ./build -elif [ $PKGARCH = "mipsel" ]; then GOARCH=mipsle GOOS=linux ./build -elif [ $PKGARCH = "mips" ]; then GOARCH=mips64 GOOS=linux ./build -elif [ $PKGARCH = "armhf" ]; then GOARCH=arm GOOS=linux GOARM=6 ./build -elif [ $PKGARCH = "arm64" ]; then GOARCH=arm64 GOOS=linux ./build -elif [ $PKGARCH = "armel" ]; then GOARCH=arm GOOS=linux GOARM=5 ./build +if [ $PKGARCH = "amd64" ]; then GOARCH=amd64 GOOS=linux ./build -l "${GOLDFLAGS}" +elif [ $PKGARCH = "i386" ]; then GOARCH=386 GOOS=linux ./build -l "${GOLDFLAGS}" +elif [ $PKGARCH = "mipsel" ]; then GOARCH=mipsle GOOS=linux ./build -l "${GOLDFLAGS}" +elif [ $PKGARCH = "mips" ]; then GOARCH=mips64 GOOS=linux ./build -l "${GOLDFLAGS}" +elif [ $PKGARCH = "armhf" ]; then GOARCH=arm GOOS=linux GOARM=6 ./build -l "${GOLDFLAGS}" +elif [ $PKGARCH = "arm64" ]; then GOARCH=arm64 GOOS=linux ./build -l "${GOLDFLAGS}" +elif [ $PKGARCH = "armel" ]; then GOARCH=arm GOOS=linux GOARM=5 ./build -l "${GOLDFLAGS}" else echo "Specify PKGARCH=amd64,i386,mips,mipsel,armhf,arm64,armel" exit 1 From e41b838d8f119b4c71f7a27995c5a941185164c5 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 28 Oct 2023 21:28:38 +0100 Subject: [PATCH 181/293] Don't panic at startup when duplicate peers are configured Fixes #1077 --- src/core/options.go | 10 +++++++++- src/core/options_test.go | 41 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 src/core/options_test.go diff --git a/src/core/options.go b/src/core/options.go index ffbdae73..581c033b 100644 --- a/src/core/options.go +++ b/src/core/options.go @@ -13,7 +13,15 @@ func (c *Core) _applyOption(opt SetupOption) (err error) { if err != nil { return fmt.Errorf("unable to parse peering URI: %w", err) } - return c.links.add(u, v.SourceInterface, linkTypePersistent) + err = c.links.add(u, v.SourceInterface, linkTypePersistent) + switch err { + case ErrLinkAlreadyConfigured: + // Don't return this error, otherwise we'll panic at startup + // if there are multiple of the same peer configured + return nil + default: + return err + } case ListenAddress: c.config._listeners[v] = struct{}{} case NodeInfo: diff --git a/src/core/options_test.go b/src/core/options_test.go new file mode 100644 index 00000000..bab22fb1 --- /dev/null +++ b/src/core/options_test.go @@ -0,0 +1,41 @@ +package core + +import ( + "net/url" + "testing" + + "github.com/yggdrasil-network/yggdrasil-go/src/config" +) + +// Tests that duplicate peers in the configuration file +// won't cause an error when the node starts. Otherwise +// we can panic unnecessarily. +func TestDuplicatePeerAtStartup(t *testing.T) { + cfg := config.GenerateConfig() + for i := 0; i < 5; i++ { + cfg.Peers = append(cfg.Peers, "tcp://1.2.3.4:4321") + } + if _, err := New(cfg.Certificate, nil); err != nil { + t.Fatal(err) + } +} + +// Tests that duplicate peers given to us through the +// API will still error as expected, even if they didn't +// at startup. We expect to notify the user through the +// admin socket if they try to add a peer that is already +// configured. +func TestDuplicatePeerFromAPI(t *testing.T) { + cfg := config.GenerateConfig() + c, err := New(cfg.Certificate, nil) + if err != nil { + t.Fatal(err) + } + u, _ := url.Parse("tcp://1.2.3.4:4321") + if err := c.AddPeer(u, ""); err != nil { + t.Fatalf("Adding peer failed on first attempt: %s", err) + } + if err := c.AddPeer(u, ""); err == nil { + t.Fatalf("Adding peer should have failed on second attempt") + } +} From 41e045fe5bf18eb08b3e467f499ef4f469fad329 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Fri, 3 Nov 2023 21:55:42 -0500 Subject: [PATCH 182/293] update ironwood dependency --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index faa0e0d8..1b79c4fa 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/yggdrasil-network/yggdrasil-go go 1.20 require ( - github.com/Arceliar/ironwood v0.0.0-20231028101932-ceac99571f43 + github.com/Arceliar/ironwood v0.0.0-20231104025256-ec84c695fc44 github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d github.com/cheggaaa/pb/v3 v3.1.4 github.com/gologme/log v1.3.0 diff --git a/go.sum b/go.sum index 901eabaa..3208f003 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Arceliar/ironwood v0.0.0-20231028101932-ceac99571f43 h1:M4NczBPk7Fy0Uy2YvNoXwSkk3dGoGTOYtUjyqpOC5ko= -github.com/Arceliar/ironwood v0.0.0-20231028101932-ceac99571f43/go.mod h1:5x7fWW0mshe9WQ1lvSMmmHBYC3BeHH9gpwW5tz7cbfw= +github.com/Arceliar/ironwood v0.0.0-20231104025256-ec84c695fc44 h1:u328GAZGtL0W4oFWQs4YWHZT215LL6Lw9aYJxx76UVs= +github.com/Arceliar/ironwood v0.0.0-20231104025256-ec84c695fc44/go.mod h1:5x7fWW0mshe9WQ1lvSMmmHBYC3BeHH9gpwW5tz7cbfw= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= From ae997a5acbe6a522841b1f51cadbdde05998ff64 Mon Sep 17 00:00:00 2001 From: Neil Date: Sat, 4 Nov 2023 17:56:52 +0000 Subject: [PATCH 183/293] Improve TUN setup logging (#1093) (#1095) Co-authored-by: Neil Alexander --- src/tun/tun_bsd.go | 2 +- src/tun/tun_darwin.go | 15 ++++++++------- src/tun/tun_linux.go | 12 ++++++------ src/tun/tun_other.go | 2 +- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/tun/tun_bsd.go b/src/tun/tun_bsd.go index 5f42f52b..da5b3297 100644 --- a/src/tun/tun_bsd.go +++ b/src/tun/tun_bsd.go @@ -78,7 +78,7 @@ type in6_ifreq_lifetime struct { func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error { iface, err := wgtun.CreateTUN(ifname, int(mtu)) if err != nil { - panic(err) + return fmt.Errorf("failed to create TUN: %w", err) } tun.iface = iface if mtu, err := iface.MTU(); err == nil { diff --git a/src/tun/tun_darwin.go b/src/tun/tun_darwin.go index 8fde93a1..71f82701 100644 --- a/src/tun/tun_darwin.go +++ b/src/tun/tun_darwin.go @@ -7,6 +7,7 @@ package tun import ( "encoding/binary" + "fmt" "os" "strconv" "strings" @@ -24,7 +25,7 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error { } iface, err := wgtun.CreateTUN(ifname, int(mtu)) if err != nil { - panic(err) + return fmt.Errorf("failed to create TUN: %w", err) } tun.iface = iface if m, err := iface.MTU(); err == nil { @@ -42,17 +43,17 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error { func (tun *TunAdapter) setupFD(fd int32, addr string, mtu uint64) error { dfd, err := unix.Dup(int(fd)) if err != nil { - return err + return fmt.Errorf("failed to duplicate FD: %w", err) } err = unix.SetNonblock(dfd, true) if err != nil { unix.Close(dfd) - return err + return fmt.Errorf("failed to set FD as non-blocking: %w", err) } iface, err := wgtun.CreateTUNFromFile(os.NewFile(uintptr(dfd), "/dev/tun"), 0) if err != nil { unix.Close(dfd) - return err + return fmt.Errorf("failed to create TUN from FD: %w", err) } tun.iface = iface if m, err := iface.MTU(); err == nil { @@ -111,7 +112,7 @@ func (tun *TunAdapter) setupAddress(addr string) error { if fd, err = unix.Socket(unix.AF_INET6, unix.SOCK_DGRAM, 0); err != nil { tun.log.Printf("Create AF_SYSTEM socket failed: %v.", err) - return err + return fmt.Errorf("failed to open AF_SYSTEM: %w", err) } var ar in6_aliasreq @@ -149,13 +150,13 @@ func (tun *TunAdapter) setupAddress(addr string) error { if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(darwin_SIOCAIFADDR_IN6), uintptr(unsafe.Pointer(&ar))); errno != 0 { // nolint:staticcheck err = errno tun.log.Errorf("Error in darwin_SIOCAIFADDR_IN6: %v", errno) - return err + return fmt.Errorf("failed to call SIOCAIFADDR_IN6: %w", err) } if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(unix.SIOCSIFMTU), uintptr(unsafe.Pointer(&ir))); errno != 0 { // nolint:staticcheck err = errno tun.log.Errorf("Error in SIOCSIFMTU: %v", errno) - return err + return fmt.Errorf("failed to call SIOCSIFMTU: %w", err) } return err diff --git a/src/tun/tun_linux.go b/src/tun/tun_linux.go index 9cf21446..98d63db4 100644 --- a/src/tun/tun_linux.go +++ b/src/tun/tun_linux.go @@ -19,7 +19,7 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error { } iface, err := wgtun.CreateTUN(ifname, int(mtu)) if err != nil { - panic(err) + return fmt.Errorf("failed to create TUN: %w", err) } tun.iface = iface if mtu, err := iface.MTU(); err == nil { @@ -45,20 +45,20 @@ func (tun *TunAdapter) setupFD(fd int32, addr string, mtu uint64) error { func (tun *TunAdapter) setupAddress(addr string) error { nladdr, err := netlink.ParseAddr(addr) if err != nil { - return err + return fmt.Errorf("couldn't parse address %q: %w", addr, err) } nlintf, err := netlink.LinkByName(tun.Name()) if err != nil { - return err + return fmt.Errorf("failed to find link by name: %w", err) } if err := netlink.AddrAdd(nlintf, nladdr); err != nil { - return err + return fmt.Errorf("failed to add address to link: %w", err) } if err := netlink.LinkSetMTU(nlintf, int(tun.mtu)); err != nil { - return err + return fmt.Errorf("failed to set link MTU: %w", err) } if err := netlink.LinkSetUp(nlintf); err != nil { - return err + return fmt.Errorf("failed to bring link up: %w", err) } // Friendly output tun.log.Infof("Interface name: %s", tun.Name()) diff --git a/src/tun/tun_other.go b/src/tun/tun_other.go index bf8ac892..0ddd0c1e 100644 --- a/src/tun/tun_other.go +++ b/src/tun/tun_other.go @@ -16,7 +16,7 @@ import ( func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error { iface, err := wgtun.CreateTUN(ifname, mtu) if err != nil { - panic(err) + return fmt.Errorf("failed to create TUN: %w", err) } tun.iface = iface if mtu, err := iface.MTU(); err == nil { From ddb75700a08ebca65e8a6dfac825f4ec781af43c Mon Sep 17 00:00:00 2001 From: Neil Date: Sat, 4 Nov 2023 17:57:04 +0000 Subject: [PATCH 184/293] Report errors during handshake stage (#1091) Co-authored-by: Neil Alexander --- src/core/link.go | 5 +++-- src/core/version.go | 27 +++++++++++++++------------ src/core/version_test.go | 6 +++--- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/core/link.go b/src/core/link.go index c3808698..f3cb4318 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -546,8 +546,9 @@ func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn) e } meta = version_metadata{} base := version_getBaseMetadata() - if !meta.decode(conn, options.password) { - return conn.Close() + if err := meta.decode(conn, options.password); err != nil { + _ = conn.Close() + return err } if !meta.check() { return fmt.Errorf("remote node incompatible version (local %s, remote %s)", diff --git a/src/core/version.go b/src/core/version.go index 332e18c8..28b16430 100644 --- a/src/core/version.go +++ b/src/core/version.go @@ -87,22 +87,22 @@ func (m *version_metadata) encode(privateKey ed25519.PrivateKey, password []byte } // Decodes version metadata from its wire format into the struct. -func (m *version_metadata) decode(r io.Reader, password []byte) bool { +func (m *version_metadata) decode(r io.Reader, password []byte) error { bh := [6]byte{} if _, err := io.ReadFull(r, bh[:]); err != nil { - return false + return err } meta := [4]byte{'m', 'e', 't', 'a'} if !bytes.Equal(bh[:4], meta[:]) { - return false + return fmt.Errorf("invalid handshake preamble") } - bs := make([]byte, binary.BigEndian.Uint16(bh[4:6])) + hl := binary.BigEndian.Uint16(bh[4:6]) + if hl < ed25519.SignatureSize { + return fmt.Errorf("invalid handshake length") + } + bs := make([]byte, hl) if _, err := io.ReadFull(r, bs); err != nil { - return false - } - - if len(bs) < ed25519.SignatureSize { - return false + return err } sig := bs[len(bs)-ed25519.SignatureSize:] bs = bs[:len(bs)-ed25519.SignatureSize] @@ -132,14 +132,17 @@ func (m *version_metadata) decode(r io.Reader, password []byte) bool { hasher, err := blake2b.New512(password) if err != nil { - return false + return fmt.Errorf("invalid password supplied") } n, err := hasher.Write(m.publicKey) if err != nil || n != ed25519.PublicKeySize { - return false + return fmt.Errorf("failed to generate hash") } hash := hasher.Sum(nil) - return ed25519.Verify(m.publicKey, hash, sig) + if !ed25519.Verify(m.publicKey, hash, sig) { + return fmt.Errorf("password is incorrect") + } + return nil } // Checks that the "meta" bytes and the version numbers are the expected values. diff --git a/src/core/version_test.go b/src/core/version_test.go index 7efe2f35..512c6e59 100644 --- a/src/core/version_test.go +++ b/src/core/version_test.go @@ -34,7 +34,7 @@ func TestVersionPasswordAuth(t *testing.T) { } var decoded version_metadata - if allowed := decoded.decode(bytes.NewBuffer(encoded), tt.password2); allowed != tt.allowed { + if allowed := decoded.decode(bytes.NewBuffer(encoded), tt.password2) == nil; allowed != tt.allowed { t.Fatalf("Permutation %q -> %q should have been %v but was %v", tt.password1, tt.password2, tt.allowed, allowed) } } @@ -67,8 +67,8 @@ func TestVersionRoundtrip(t *testing.T) { } encoded := bytes.NewBuffer(meta) decoded := &version_metadata{} - if !decoded.decode(encoded, password) { - t.Fatalf("failed to decode") + if err := decoded.decode(encoded, password); err != nil { + t.Fatalf("failed to decode: %s", err) } if !reflect.DeepEqual(test, decoded) { t.Fatalf("round-trip failed\nwant: %+v\n got: %+v", test, decoded) From 93a5adfd189ff8ecd233693dc123b0ef7e11216f Mon Sep 17 00:00:00 2001 From: Neil Date: Sat, 4 Nov 2023 17:57:15 +0000 Subject: [PATCH 185/293] Add `sockstls://` (#1090) Closes #1087. Co-authored-by: Neil Alexander --- src/core/link.go | 34 +++++++++++++++++----------------- src/core/link_socks.go | 12 +++++++++++- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/core/link.go b/src/core/link.go index f3cb4318..0b2efc57 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -179,6 +179,22 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error { } options.password = []byte(p) } + // SNI headers must contain hostnames and not IP addresses, so we must make sure + // that we do not populate the SNI with an IP literal. We do this by splitting + // the host-port combo from the query option and then seeing if it parses to an + // IP address successfully or not. + if sni := u.Query().Get("sni"); sni != "" { + if net.ParseIP(sni) == nil { + options.tlsSNI = sni + } + } + // If the SNI is not configured still because the above failed then we'll try + // again but this time we'll use the host part of the peering URI instead. + if options.tlsSNI == "" { + if host, _, err := net.SplitHostPort(u.Host); err == nil && net.ParseIP(host) == nil { + options.tlsSNI = host + } + } // If we think we're already connected to this peer, load up // the existing peer state. Try to kick the peer if possible, @@ -497,24 +513,8 @@ func (l *links) connect(ctx context.Context, u *url.URL, info linkInfo, options case "tcp": dialer = l.tcp case "tls": - // SNI headers must contain hostnames and not IP addresses, so we must make sure - // that we do not populate the SNI with an IP literal. We do this by splitting - // the host-port combo from the query option and then seeing if it parses to an - // IP address successfully or not. - if sni := u.Query().Get("sni"); sni != "" { - if net.ParseIP(sni) == nil { - options.tlsSNI = sni - } - } - // If the SNI is not configured still because the above failed then we'll try - // again but this time we'll use the host part of the peering URI instead. - if options.tlsSNI == "" { - if host, _, err := net.SplitHostPort(u.Host); err == nil && net.ParseIP(host) == nil { - options.tlsSNI = host - } - } dialer = l.tls - case "socks": + case "socks", "sockstls": dialer = l.socks case "unix": dialer = l.unix diff --git a/src/core/link_socks.go b/src/core/link_socks.go index 1a038fa6..b92374d4 100644 --- a/src/core/link_socks.go +++ b/src/core/link_socks.go @@ -2,6 +2,7 @@ package core import ( "context" + "crypto/tls" "fmt" "net" "net/url" @@ -34,7 +35,16 @@ func (l *linkSOCKS) dial(_ context.Context, url *url.URL, info linkInfo, options return nil, fmt.Errorf("failed to configure proxy") } pathtokens := strings.Split(strings.Trim(url.Path, "/"), "/") - return dialer.Dial("tcp", pathtokens[0]) + conn, err := dialer.Dial("tcp", pathtokens[0]) + if err != nil { + return nil, fmt.Errorf("failed to dial: %w", err) + } + if url.Scheme == "sockstls" { + tlsconfig := l.tls.config.Clone() + tlsconfig.ServerName = options.tlsSNI + conn = tls.Client(conn, tlsconfig) + } + return conn, nil } func (l *linkSOCKS) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) { From 0346af46dae233e7d95d2a08e2b992382e1f2d64 Mon Sep 17 00:00:00 2001 From: Neil Date: Sat, 4 Nov 2023 18:42:42 +0000 Subject: [PATCH 186/293] Don't panic when connect returns `nil` (fixes #1086) (#1089) * Don't panic when connect returns `nil` (fixes #1086) It isn't clear to me why this would happen but let's guard the condition anyway. * Log inconsistent error state --------- Co-authored-by: Neil Alexander --- src/core/link.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/link.go b/src/core/link.go index 0b2efc57..af4a5a59 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -273,7 +273,10 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error { } conn, err := l.connect(state.ctx, u, info, options) - if err != nil { + if err != nil || conn == nil { + if err == nil && conn == nil { + l.core.log.Warnf("Link %q reached inconsistent error state", u.String()) + } if linkType == linkTypePersistent { // If the link is a persistent configured peering, // store information about the connection error so From 49c424ef213589d63215f10dc1958b2e93fb1e6c Mon Sep 17 00:00:00 2001 From: Neil Date: Sat, 4 Nov 2023 18:42:51 +0000 Subject: [PATCH 187/293] Add `-publickey` command line switch (#1096) Co-authored-by: Neil Alexander --- cmd/yggdrasil/main.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 975e4945..a225755c 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -48,8 +48,9 @@ func main() { autoconf := flag.Bool("autoconf", false, "automatic mode (dynamic IP, peer with IPv6 neighbors)") ver := flag.Bool("version", false, "prints the version of this build") 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") + getaddr := flag.Bool("address", false, "use in combination with either -useconf or -useconffile, outputs your IPv6 address") + getsnet := flag.Bool("subnet", false, "use in combination with either -useconf or -useconffile, outputs your IPv6 subnet") + getpkey := flag.Bool("publickey", false, "use in combination with either -useconf or -useconffile, outputs your public key") loglevel := flag.String("loglevel", "info", "loglevel to enable") flag.Parse() @@ -155,6 +156,10 @@ func main() { fmt.Println(ipnet.String()) return + case *getpkey: + fmt.Println(hex.EncodeToString(publicKey)) + return + case *normaliseconf: cfg.AdminListen = "" var bs []byte From 7ac38e3e58d2134802fcd0e7ae42f54bfd92314a Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 6 Nov 2023 09:25:15 +0000 Subject: [PATCH 188/293] Release: Yggdrasil 0.5.2 --- CHANGELOG.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d0e9e3da..c91e83ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,25 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - in case of vulnerabilities. --> +## [0.5.2] - 2023-11-06 + +### Added + +* New `-publickey` command line option that prints the derived public key from a configuration file +* Support for connecting to TLS peers via SOCKS with the new `sockstls://` link schema + +### Changed + +* Stabilise tree parent selection algorithm +* Improved logging when the TUN interface fails to set up + +### Fixed + +* Fixed a panic that could occur when a connection reaches an inconsistent error state +* The admin socket will now report more peering handshake error conditions in `getPeers` +* Yggdrasil will no longer panic at startup when duplicate peers are configured +* The `build` script will no longer incorrectly import `LDFLAGS` from the environment + ## [0.5.1] - 2023-10-28 ### Fixed From 6677d70648b076d25a8db0dfe5fe886e7ae3190e Mon Sep 17 00:00:00 2001 From: Arceliar Date: Thu, 9 Nov 2023 22:06:19 -0600 Subject: [PATCH 189/293] update ironwood, fixed data race from buffered pathfinder traffic --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 1b79c4fa..164aa3d0 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/yggdrasil-network/yggdrasil-go go 1.20 require ( - github.com/Arceliar/ironwood v0.0.0-20231104025256-ec84c695fc44 + github.com/Arceliar/ironwood v0.0.0-20231110040415-4972641b0b94 github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d github.com/cheggaaa/pb/v3 v3.1.4 github.com/gologme/log v1.3.0 diff --git a/go.sum b/go.sum index 3208f003..4b3a8198 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Arceliar/ironwood v0.0.0-20231104025256-ec84c695fc44 h1:u328GAZGtL0W4oFWQs4YWHZT215LL6Lw9aYJxx76UVs= -github.com/Arceliar/ironwood v0.0.0-20231104025256-ec84c695fc44/go.mod h1:5x7fWW0mshe9WQ1lvSMmmHBYC3BeHH9gpwW5tz7cbfw= +github.com/Arceliar/ironwood v0.0.0-20231110040415-4972641b0b94 h1:9gsjQMpH9uwu5wRD2wJrocsudTw/WRy8oJykdrBz4F8= +github.com/Arceliar/ironwood v0.0.0-20231110040415-4972641b0b94/go.mod h1:5x7fWW0mshe9WQ1lvSMmmHBYC3BeHH9gpwW5tz7cbfw= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= From 7aca8691706f5bbc99a986c2205a43646d90ca3d Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 21 Nov 2023 10:35:17 +0000 Subject: [PATCH 190/293] Tweak backoff success handling --- src/core/link.go | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/core/link.go b/src/core/link.go index af4a5a59..f2abd7c6 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -249,6 +249,12 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error { } } + // resetBackoff is called by the connection handler when the + // handshake has successfully completed. + resetBackoff := func() { + backoff = 0 + } + // The goroutine is responsible for attempting the connection // and then running the handler. If the connection is persistent // then the loop will run endlessly, using backoffs as needed. @@ -325,10 +331,8 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error { // Give the connection to the handler. The handler will block // for the lifetime of the connection. - if err = l.handler(linkType, options, lc); err != nil && err != io.EOF { + if err = l.handler(linkType, options, lc, resetBackoff); err != nil && err != io.EOF { l.core.log.Debugf("Link %s error: %s\n", info.uri, err) - } else { - backoff = 0 } // The handler has stopped running so the connection is dead, @@ -491,7 +495,7 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { // Give the connection to the handler. The handler will block // for the lifetime of the connection. - if err = l.handler(linkTypeIncoming, options, lc); err != nil && err != io.EOF { + if err = l.handler(linkTypeIncoming, options, lc, nil); err != nil && err != io.EOF { l.core.log.Debugf("Link %s error: %s\n", u.Host, err) } @@ -529,7 +533,7 @@ func (l *links) connect(ctx context.Context, u *url.URL, info linkInfo, options return dialer.dial(ctx, u, info, options) } -func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn) error { +func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn, success func()) error { meta := version_getBaseMetadata() meta.publicKey = l.core.public meta.priority = options.priority @@ -600,6 +604,9 @@ func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn) e } l.core.log.Infof("Connected %s: %s, source %s", dir, remoteStr, localStr) + if success != nil { + success() + } err = l.core.HandleConn(meta.publicKey, conn, priority) switch err { From a0b38972787947fc9dbf5e1e9fc6539213a998ce Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 21 Nov 2023 23:54:27 +0000 Subject: [PATCH 191/293] Cap link backoff at roughly 4.5 hours --- src/core/link.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core/link.go b/src/core/link.go index f2abd7c6..0f445ff5 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -235,7 +235,9 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error { // The caller should check the return value to decide whether // or not to give up trying. backoffNow := func() bool { - backoff++ + if backoff < 14 { // Cap at roughly 4.5 hours maximum. + backoff++ + } duration := time.Second * time.Duration(math.Exp2(float64(backoff))) select { case <-state.kick: From 0d676c6a3bbd3289b98a5b2572e1149b5d3dc4c2 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sun, 26 Nov 2023 04:56:44 -0600 Subject: [PATCH 192/293] update ironwood dependency --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 164aa3d0..73dfe053 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/yggdrasil-network/yggdrasil-go go 1.20 require ( - github.com/Arceliar/ironwood v0.0.0-20231110040415-4972641b0b94 + github.com/Arceliar/ironwood v0.0.0-20231126105342-ad38416a77c8 github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d github.com/cheggaaa/pb/v3 v3.1.4 github.com/gologme/log v1.3.0 diff --git a/go.sum b/go.sum index 4b3a8198..ba37a002 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Arceliar/ironwood v0.0.0-20231110040415-4972641b0b94 h1:9gsjQMpH9uwu5wRD2wJrocsudTw/WRy8oJykdrBz4F8= -github.com/Arceliar/ironwood v0.0.0-20231110040415-4972641b0b94/go.mod h1:5x7fWW0mshe9WQ1lvSMmmHBYC3BeHH9gpwW5tz7cbfw= +github.com/Arceliar/ironwood v0.0.0-20231126105342-ad38416a77c8 h1:qyXiPZClVoe6QsbsiDP23g0Ze/MNXiHIAL/nVNfauH0= +github.com/Arceliar/ironwood v0.0.0-20231126105342-ad38416a77c8/go.mod h1:5x7fWW0mshe9WQ1lvSMmmHBYC3BeHH9gpwW5tz7cbfw= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= From 39c4b24395a471d032a96f9bac1e69e17cafb99b Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 26 Nov 2023 16:19:00 +0000 Subject: [PATCH 193/293] Don't use 0-RTT for QUIC --- src/core/link_quic.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/core/link_quic.go b/src/core/link_quic.go index 976aba6a..a2db6e05 100644 --- a/src/core/link_quic.go +++ b/src/core/link_quic.go @@ -24,7 +24,7 @@ type linkQUICStream struct { } type linkQUICListener struct { - *quic.EarlyListener + *quic.Listener ch <-chan *linkQUICStream } @@ -50,11 +50,11 @@ func (l *links) newLinkQUIC() *linkQUIC { } func (l *linkQUIC) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) { - qc, err := quic.DialAddrEarly(ctx, url.Host, l.tlsconfig, l.quicconfig) + qc, err := quic.DialAddr(ctx, url.Host, l.tlsconfig, l.quicconfig) if err != nil { return nil, err } - qs, err := qc.OpenStream() + qs, err := qc.OpenStreamSync(ctx) if err != nil { return nil, err } @@ -65,14 +65,14 @@ func (l *linkQUIC) dial(ctx context.Context, url *url.URL, info linkInfo, option } func (l *linkQUIC) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) { - ql, err := quic.ListenAddrEarly(url.Host, l.tlsconfig, l.quicconfig) + ql, err := quic.ListenAddr(url.Host, l.tlsconfig, l.quicconfig) if err != nil { return nil, err } ch := make(chan *linkQUICStream) lql := &linkQUICListener{ - EarlyListener: ql, - ch: ch, + Listener: ql, + ch: ch, } go func() { for { From f6f669617fffd75b96de6977c3b75b5eff2985c1 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 26 Nov 2023 16:20:52 +0000 Subject: [PATCH 194/293] Fix `-normaliseconf` when using `PrivateKeyPath` --- cmd/yggdrasil/main.go | 3 +++ src/config/config.go | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index a225755c..b133ed36 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -162,6 +162,9 @@ func main() { case *normaliseconf: cfg.AdminListen = "" + if cfg.PrivateKeyPath != "" { + cfg.PrivateKey = nil + } var bs []byte if *confjson { bs, err = json.MarshalIndent(cfg, "", " ") diff --git a/src/config/config.go b/src/config/config.go index e899a35d..aac51467 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -40,8 +40,8 @@ import ( // options that are necessary for an Yggdrasil node to run. You will need to // supply one of these structs to the Yggdrasil core when starting a node. type NodeConfig struct { - PrivateKey KeyBytes `comment:"Your private key. DO NOT share this with anyone!"` - PrivateKeyPath string `json:",omitempty"` + PrivateKey KeyBytes `json:",omitempty" comment:"Your private key. DO NOT share this with anyone!"` + PrivateKeyPath string `json:",omitempty" comment:"The path to your private key file in PEM format."` Certificate *tls.Certificate `json:"-"` Peers []string `comment:"List of connection strings for outbound peer connections in URI format,\ne.g. tls://a.b.c.d:e or socks://a.b.c.d:e/f.g.h.i:j. These connections\nwill obey the operating system routing table, therefore you should\nuse this section when you may connect via different interfaces."` InterfacePeers map[string][]string `comment:"List of connection strings for outbound peer connections in URI format,\narranged by source interface, e.g. { \"eth0\": [ \"tls://a.b.c.d:e\" ] }.\nNote that SOCKS peerings will NOT be affected by this option and should\ngo in the \"Peers\" section instead."` From fef553ed185b279145a132461da862f8f8a98f79 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 26 Nov 2023 16:28:48 +0000 Subject: [PATCH 195/293] Tweak logging --- cmd/yggdrasil/main.go | 6 +++--- src/core/link.go | 4 ++-- src/tun/iface.go | 2 +- src/tun/tun_darwin.go | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index b133ed36..a1c79492 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -216,9 +216,9 @@ func main() { panic(err) } address, subnet := n.core.Address(), n.core.Subnet() - logger.Infof("Your public key is %s", hex.EncodeToString(n.core.PublicKey())) - logger.Infof("Your IPv6 address is %s", address.String()) - logger.Infof("Your IPv6 subnet is %s", subnet.String()) + logger.Printf("Your public key is %s", hex.EncodeToString(n.core.PublicKey())) + logger.Printf("Your IPv6 address is %s", address.String()) + logger.Printf("Your IPv6 subnet is %s", subnet.String()) } // Setup the admin socket. diff --git a/src/core/link.go b/src/core/link.go index 0f445ff5..09f815c2 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -432,8 +432,8 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { } go func() { - l.core.log.Printf("%s listener started on %s", strings.ToUpper(u.Scheme), listener.Addr()) - defer l.core.log.Printf("%s listener stopped on %s", strings.ToUpper(u.Scheme), listener.Addr()) + l.core.log.Infof("%s listener started on %s", strings.ToUpper(u.Scheme), listener.Addr()) + defer l.core.log.Infof("%s listener stopped on %s", strings.ToUpper(u.Scheme), listener.Addr()) for { conn, err := listener.Accept() if err != nil { diff --git a/src/tun/iface.go b/src/tun/iface.go index c98b56d6..e06f3a5d 100644 --- a/src/tun/iface.go +++ b/src/tun/iface.go @@ -29,7 +29,7 @@ func (tun *TunAdapter) write() { bs := buf[TUN_OFFSET_BYTES:] n, err := tun.rwc.Read(bs) if err != nil { - tun.log.Errorln("Exiting tun writer due to core read error:", err) + tun.log.Errorln("Exiting TUN writer due to core read error:", err) return } if !tun.isEnabled { diff --git a/src/tun/tun_darwin.go b/src/tun/tun_darwin.go index 71f82701..deeb115e 100644 --- a/src/tun/tun_darwin.go +++ b/src/tun/tun_darwin.go @@ -111,7 +111,7 @@ func (tun *TunAdapter) setupAddress(addr string) error { var err error if fd, err = unix.Socket(unix.AF_INET6, unix.SOCK_DGRAM, 0); err != nil { - tun.log.Printf("Create AF_SYSTEM socket failed: %v.", err) + tun.log.Errorf("Create AF_SYSTEM socket failed: %v.", err) return fmt.Errorf("failed to open AF_SYSTEM: %w", err) } From 676ae52503a5f0c86314958a5817afe949deb1e3 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 26 Nov 2023 18:42:08 +0000 Subject: [PATCH 196/293] Release: Yggdrasil 0.5.3 --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c91e83ba..fc7f9f73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,17 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - in case of vulnerabilities. --> +## [0.5.3] - 2023-11-26 + +### Fixed + +* Fixed a data race from buffered pathfinder traffic +* Fix a bug where the next-hop selection may not take shortcuts through treespace +* Backoffs are now reset correctly when a successful handshake is completed +* Backoffs will no longer exceed roughly 4.5 hours when peers are down for a long time +* The `-normaliseconf` option will now work correctly with `PrivateKeyPath` +* Improved the reliability of QUIC peering setup by disabling 0-RTT + ## [0.5.2] - 2023-11-06 ### Added From 741f825b8ed3e78664997f49c786dd51fa6a3805 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Mon, 27 Nov 2023 07:18:16 -0600 Subject: [PATCH 197/293] update ironwood dependency, should fix bloom filter encoding crash --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 73dfe053..6ce5beb0 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/yggdrasil-network/yggdrasil-go go 1.20 require ( - github.com/Arceliar/ironwood v0.0.0-20231126105342-ad38416a77c8 + github.com/Arceliar/ironwood v0.0.0-20231127131626-465b82dfb5bd github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d github.com/cheggaaa/pb/v3 v3.1.4 github.com/gologme/log v1.3.0 diff --git a/go.sum b/go.sum index ba37a002..e89d0fcf 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Arceliar/ironwood v0.0.0-20231126105342-ad38416a77c8 h1:qyXiPZClVoe6QsbsiDP23g0Ze/MNXiHIAL/nVNfauH0= -github.com/Arceliar/ironwood v0.0.0-20231126105342-ad38416a77c8/go.mod h1:5x7fWW0mshe9WQ1lvSMmmHBYC3BeHH9gpwW5tz7cbfw= +github.com/Arceliar/ironwood v0.0.0-20231127131626-465b82dfb5bd h1:458tnmZ4zM2gbLtefdYbaxyAJevDNEWu6tLKEqbK4wg= +github.com/Arceliar/ironwood v0.0.0-20231127131626-465b82dfb5bd/go.mod h1:5x7fWW0mshe9WQ1lvSMmmHBYC3BeHH9gpwW5tz7cbfw= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= From 3d15da34ad9021add84f5df00cdf65341cb29829 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 27 Nov 2023 14:17:16 +0000 Subject: [PATCH 198/293] Release: Yggdrasil 0.5.4 --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc7f9f73..b2715486 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - in case of vulnerabilities. --> +## [0.5.4] - 2023-11-27 + +### Fixed + +* Fixed a crash that could happen when calculating the size of bloom filters during encoding + ## [0.5.3] - 2023-11-26 ### Fixed From 6b6cd0bed553ada887632524994e4de4e839688e Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 28 Nov 2023 13:24:54 +0000 Subject: [PATCH 199/293] Fix `PPROFLISTEN` --- src/core/debug.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core/debug.go b/src/core/debug.go index a6eb5e2a..354c2ecd 100644 --- a/src/core/debug.go +++ b/src/core/debug.go @@ -12,6 +12,8 @@ func init() { envVarName := "PPROFLISTEN" if hostPort := os.Getenv(envVarName); hostPort != "" { fmt.Fprintf(os.Stderr, "DEBUG: Starting pprof on %s\n", hostPort) - go fmt.Println(http.ListenAndServe(hostPort, nil)) + go func() { + fmt.Fprintf(os.Stderr, "DEBUG: %s", http.ListenAndServe(hostPort, nil)) + }() } } From 3dfa6d0cc9f4020f065392b3f8eeaca9680b347e Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 3 Dec 2023 17:55:12 +0000 Subject: [PATCH 200/293] Validate public key lengths on `debug_` API endpoints (fixes #1113) --- src/core/proto.go | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/core/proto.go b/src/core/proto.go index d8cae737..bffc2022 100644 --- a/src/core/proto.go +++ b/src/core/proto.go @@ -251,15 +251,16 @@ func (p *protoHandler) getSelfHandler(in json.RawMessage) (interface{}, error) { if kbs, err = hex.DecodeString(req.Key); err != nil { return nil, err } + if len(kbs) != ed25519.PublicKeySize { + return nil, fmt.Errorf("invalid public key length") + } copy(key[:], kbs) ch := make(chan []byte, 1) p.sendGetSelfRequest(key, func(info []byte) { ch <- info }) - timer := time.NewTimer(6 * time.Second) - defer timer.Stop() select { - case <-timer.C: + case <-time.After(6 * time.Second): return nil, errors.New("timeout") case info := <-ch: var msg json.RawMessage @@ -291,15 +292,16 @@ func (p *protoHandler) getPeersHandler(in json.RawMessage) (interface{}, error) if kbs, err = hex.DecodeString(req.Key); err != nil { return nil, err } + if len(kbs) != ed25519.PublicKeySize { + return nil, fmt.Errorf("invalid public key length") + } copy(key[:], kbs) ch := make(chan []byte, 1) p.sendGetPeersRequest(key, func(info []byte) { ch <- info }) - timer := time.NewTimer(6 * time.Second) - defer timer.Stop() select { - case <-timer.C: + case <-time.After(6 * time.Second): return nil, errors.New("timeout") case info := <-ch: ks := make(map[string][]string) @@ -341,15 +343,16 @@ func (p *protoHandler) getTreeHandler(in json.RawMessage) (interface{}, error) { if kbs, err = hex.DecodeString(req.Key); err != nil { return nil, err } + if len(kbs) != ed25519.PublicKeySize { + return nil, fmt.Errorf("invalid public key length") + } copy(key[:], kbs) ch := make(chan []byte, 1) p.sendGetTreeRequest(key, func(info []byte) { ch <- info }) - timer := time.NewTimer(6 * time.Second) - defer timer.Stop() select { - case <-timer.C: + case <-time.After(6 * time.Second): return nil, errors.New("timeout") case info := <-ch: ks := make(map[string][]string) From 1e9a59edf99b232cf160982e42c6bc3a807eaf0e Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Fri, 5 Jan 2024 11:45:20 +0000 Subject: [PATCH 201/293] Update behaviour in QUIC listener handler --- src/core/link_quic.go | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/core/link_quic.go b/src/core/link_quic.go index a2db6e05..9ad5456d 100644 --- a/src/core/link_quic.go +++ b/src/core/link_quic.go @@ -3,6 +3,7 @@ package core import ( "context" "crypto/tls" + "fmt" "net" "net/url" "time" @@ -77,18 +78,22 @@ func (l *linkQUIC) listen(ctx context.Context, url *url.URL, _ string) (net.List go func() { for { qc, err := ql.Accept(ctx) - if err != nil { + switch err { + case context.Canceled, context.DeadlineExceeded: ql.Close() + fallthrough + case quic.ErrServerClosed: return - } - qs, err := qc.AcceptStream(ctx) - if err != nil { - ql.Close() - return - } - ch <- &linkQUICStream{ - Connection: qc, - Stream: qs, + case nil: + qs, err := qc.AcceptStream(ctx) + if err != nil { + _ = qc.CloseWithError(1, fmt.Sprintf("stream error: %s", err)) + continue + } + ch <- &linkQUICStream{ + Connection: qc, + Stream: qs, + } } } }() From 768278a8e6508a82ab5e2e4655c224be657a8e23 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 11 Jan 2024 22:37:05 +0000 Subject: [PATCH 202/293] Improve `getPeers` sorting --- src/admin/getpeers.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/admin/getpeers.go b/src/admin/getpeers.go index c6cd5be4..9bc3872f 100644 --- a/src/admin/getpeers.go +++ b/src/admin/getpeers.go @@ -56,10 +56,16 @@ func (a *AdminSocket) getPeersHandler(req *GetPeersRequest, res *GetPeersRespons res.Peers = append(res.Peers, peer) } sort.Slice(res.Peers, func(i, j int) bool { - if res.Peers[i].Port == res.Peers[j].Port { - return res.Peers[i].Priority < res.Peers[j].Priority + if res.Peers[i].Inbound == res.Peers[j].Inbound { + if res.Peers[i].PublicKey == res.Peers[j].PublicKey { + if res.Peers[i].Priority == res.Peers[j].Priority { + return res.Peers[i].Uptime > res.Peers[j].Uptime + } + return res.Peers[i].Priority < res.Peers[j].Priority + } + return res.Peers[i].PublicKey < res.Peers[j].PublicKey } - return res.Peers[i].Port < res.Peers[j].Port + return !res.Peers[i].Inbound && res.Peers[j].Inbound }) return nil } From 5da4c1131e224f63d376eec594c31ef29c1c427d Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 15 Jan 2024 19:07:17 +0000 Subject: [PATCH 203/293] Update ironwood to `ddd1fa6` --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 6ce5beb0..d63cecc0 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/yggdrasil-network/yggdrasil-go go 1.20 require ( - github.com/Arceliar/ironwood v0.0.0-20231127131626-465b82dfb5bd + github.com/Arceliar/ironwood v0.0.0-20240115190409-ddd1fa67c018 github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d github.com/cheggaaa/pb/v3 v3.1.4 github.com/gologme/log v1.3.0 diff --git a/go.sum b/go.sum index e89d0fcf..6b1cdabb 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Arceliar/ironwood v0.0.0-20231127131626-465b82dfb5bd h1:458tnmZ4zM2gbLtefdYbaxyAJevDNEWu6tLKEqbK4wg= -github.com/Arceliar/ironwood v0.0.0-20231127131626-465b82dfb5bd/go.mod h1:5x7fWW0mshe9WQ1lvSMmmHBYC3BeHH9gpwW5tz7cbfw= +github.com/Arceliar/ironwood v0.0.0-20240115190409-ddd1fa67c018 h1:7r/T7qJht4CaPl74AgU7dG5N6g7+2230/9BhrbtRijk= +github.com/Arceliar/ironwood v0.0.0-20240115190409-ddd1fa67c018/go.mod h1:5x7fWW0mshe9WQ1lvSMmmHBYC3BeHH9gpwW5tz7cbfw= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= From 9f4c89acad2e0cab52ab5e154212edcd000ce893 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 15 Jan 2024 23:00:58 +0000 Subject: [PATCH 204/293] Update dependencies --- go.mod | 14 +++++++------- go.sum | 28 ++++++++++++++-------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/go.mod b/go.mod index d63cecc0..f3b63daf 100644 --- a/go.mod +++ b/go.mod @@ -8,15 +8,15 @@ require ( github.com/cheggaaa/pb/v3 v3.1.4 github.com/gologme/log v1.3.0 github.com/hashicorp/go-syslog v1.0.0 - github.com/hjson/hjson-go/v4 v4.3.0 + github.com/hjson/hjson-go/v4 v4.4.0 github.com/kardianos/minwinsvc v1.0.2 - github.com/quic-go/quic-go v0.39.3 + github.com/quic-go/quic-go v0.40.1 github.com/vishvananda/netlink v1.1.0 - golang.org/x/crypto v0.14.0 + golang.org/x/crypto v0.18.0 golang.org/x/mobile v0.0.0-20231006135142-2b44d11868fe - golang.org/x/net v0.17.0 - golang.org/x/sys v0.13.0 - golang.org/x/text v0.13.0 + golang.org/x/net v0.20.0 + golang.org/x/sys v0.16.0 + golang.org/x/text v0.14.0 golang.zx2c4.com/wireguard v0.0.0-20230223181233-21636207a675 golang.zx2c4.com/wireguard/windows v0.5.3 ) @@ -28,7 +28,7 @@ require ( github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/onsi/ginkgo/v2 v2.9.5 // indirect - github.com/quic-go/qtls-go1-20 v0.3.4 // indirect + github.com/quic-go/qtls-go1-20 v0.4.1 // indirect github.com/rivo/uniseg v0.2.0 // indirect go.uber.org/mock v0.3.0 // indirect golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect diff --git a/go.sum b/go.sum index 6b1cdabb..6cd0ab72 100644 --- a/go.sum +++ b/go.sum @@ -30,8 +30,8 @@ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hjson/hjson-go/v4 v4.3.0 h1:dyrzJdqqFGhHt+FSrs5n9s6b0fPM8oSJdWo+oS3YnJw= -github.com/hjson/hjson-go/v4 v4.3.0/go.mod h1:KaYt3bTw3zhBjYqnXkYywcYctk0A2nxeEFTse3rH13E= +github.com/hjson/hjson-go/v4 v4.4.0 h1:D/NPvqOCH6/eisTb5/ztuIS8GUvmpHaLOcNk1Bjr298= +github.com/hjson/hjson-go/v4 v4.4.0/go.mod h1:KaYt3bTw3zhBjYqnXkYywcYctk0A2nxeEFTse3rH13E= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/kardianos/minwinsvc v1.0.2 h1:JmZKFJQrmTGa/WiW+vkJXKmfzdjabuEW4Tirj5lLdR0= github.com/kardianos/minwinsvc v1.0.2/go.mod h1:LUZNYhNmxujx2tR7FbdxqYJ9XDDoCd3MQcl1o//FWl4= @@ -50,10 +50,10 @@ github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3Ro github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/quic-go/qtls-go1-20 v0.3.4 h1:MfFAPULvst4yoMgY9QmtpYmfij/em7O8UUi+bNVm7Cg= -github.com/quic-go/qtls-go1-20 v0.3.4/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= -github.com/quic-go/quic-go v0.39.3 h1:o3YB6t2SR+HU/pgwF29kJ6g4jJIJEwEZ8CKia1h1TKg= -github.com/quic-go/quic-go v0.39.3/go.mod h1:T09QsDQWjLiQ74ZmacDfqZmhY/NLnw5BC40MANNNZ1Q= +github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs= +github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= +github.com/quic-go/quic-go v0.40.1 h1:X3AGzUNFs0jVuO3esAGnTfvdgvL4fq655WaOi1snv1Q= +github.com/quic-go/quic-go v0.40.1/go.mod h1:PeN7kuVJ4xZbxSv/4OX6S1USOX8MJvydwpTx31vx60c= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -72,8 +72,8 @@ go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o= golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/mobile v0.0.0-20231006135142-2b44d11868fe h1:lrXv4yHeD9FA8PSJATWowP1QvexpyAPWmPia+Kbzql8= @@ -87,8 +87,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -105,8 +105,8 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -116,8 +116,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= From 180d7bf499cbdb5d0aa732de02ef1600ab14284a Mon Sep 17 00:00:00 2001 From: Neil Date: Mon, 15 Jan 2024 23:09:07 +0000 Subject: [PATCH 205/293] Adjust default backoff max to just over 1 hour, add `?maxbackoff=` peer option (#1124) Co-authored-by: Neil Alexander --- src/core/link.go | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/core/link.go b/src/core/link.go index 09f815c2..08a6a99e 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -6,7 +6,6 @@ import ( "encoding/hex" "fmt" "io" - "math" "net" "net/netip" "net/url" @@ -28,6 +27,9 @@ const ( linkTypeIncoming // Incoming connection ) +const defaultBackoffLimit = time.Second << 12 // 1h8m16s +const minimumBackoffLimit = time.Second * 30 + type links struct { phony.Inbox core *Core @@ -69,6 +71,7 @@ type linkOptions struct { priority uint8 tlsSNI string password []byte + maxBackoff time.Duration } type Listener struct { @@ -136,6 +139,7 @@ const ErrLinkPriorityInvalid = linkError("priority value is invalid") const ErrLinkPinnedKeyInvalid = linkError("pinned public key is invalid") const ErrLinkPasswordInvalid = linkError("password is invalid") const ErrLinkUnrecognisedSchema = linkError("link schema unknown") +const ErrLinkMaxBackoffInvalid = linkError("max backoff duration invalid") func (l *links) add(u *url.URL, sintf string, linkType linkType) error { var retErr error @@ -150,7 +154,9 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error { // Collect together the link options, these are global options // that are not specific to any given protocol. - var options linkOptions + options := linkOptions{ + maxBackoff: defaultBackoffLimit, + } for _, pubkey := range u.Query()["key"] { sigPub, err := hex.DecodeString(pubkey) if err != nil { @@ -179,6 +185,14 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error { } options.password = []byte(p) } + if p := u.Query().Get("maxbackoff"); p != "" { + d, err := time.ParseDuration(p) + if err != nil || d < minimumBackoffLimit { + retErr = ErrLinkMaxBackoffInvalid + return + } + options.maxBackoff = d + } // SNI headers must contain hostnames and not IP addresses, so we must make sure // that we do not populate the SNI with an IP literal. We do this by splitting // the host-port combo from the query option and then seeing if it parses to an @@ -235,10 +249,13 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error { // The caller should check the return value to decide whether // or not to give up trying. backoffNow := func() bool { - if backoff < 14 { // Cap at roughly 4.5 hours maximum. + if backoff < 32 { backoff++ } - duration := time.Second * time.Duration(math.Exp2(float64(backoff))) + duration := time.Second << backoff + if duration > options.maxBackoff { + duration = options.maxBackoff + } select { case <-state.kick: return true From 81f2c711b484d05fbbab2394dd8860700294cbf2 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 15 Jan 2024 23:14:43 +0000 Subject: [PATCH 206/293] Fix panic in `getPeers` on abstract UNIX socket names Fixes #1111 --- cmd/yggdrasilctl/main.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cmd/yggdrasilctl/main.go b/cmd/yggdrasilctl/main.go index 8f99e0d2..db823417 100644 --- a/cmd/yggdrasilctl/main.go +++ b/cmd/yggdrasilctl/main.go @@ -183,13 +183,13 @@ func run() int { if peer.Inbound { dir = "In" } - uri, err := url.Parse(peer.URI) - if err != nil { - panic(err) + uristring := peer.URI + if uri, err := url.Parse(peer.URI); err == nil { + uri.RawQuery = "" + uristring = uri.String() } - uri.RawQuery = "" table.Append([]string{ - uri.String(), + uristring, state, dir, peer.IPAddress, From 2c20a043692a5f54f67480971830aa56c4d3de03 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 27 Jan 2024 22:54:54 +0000 Subject: [PATCH 207/293] Release: Yggdrasil 0.5.5 --- CHANGELOG.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b2715486..aa3fd33e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,27 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - in case of vulnerabilities. --> +## [0.5.5] - 2024-01-27 + +### Added + +* A new peer option `?maxbackoff=X` has been added to control the maximum backoff time for a given peer, supports duration values like `5m`, `1h` etc + +### Changed + +* The maximum backoff period for failing peer connections has been reduced to just over 1 hour, compared to 4.5 hours before +* The `getPeers` endpoint now sorts peers in a more stable fashion +* Upgrade dependencies + +### Fixed + +* A bug where QUIC listeners could stop listening for incoming connections unexpectedly has been fixed +* The priority tiebreak between multiple peerings to the same node has been fixed +* Peer connection ordering is no longer sensitive to poor system time resolution +* The admin socket now verifies the length of input public keys +* The `PPROFLISTEN` environment variable has been fixed and now starts the pprof listener correctly +* A panic in `getPeers` has been fixed when using abstract UNIX sockets on Linux + ## [0.5.4] - 2023-11-27 ### Fixed From 2d644eabc38ff4b3ebc0556315f97fdab30d7ec2 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Thu, 21 Mar 2024 21:33:07 -0500 Subject: [PATCH 208/293] update ironwood (updates bloom dependency) --- go.mod | 6 +++--- go.sum | 13 ++++++------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index f3b63daf..cf717d15 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/yggdrasil-network/yggdrasil-go go 1.20 require ( - github.com/Arceliar/ironwood v0.0.0-20240115190409-ddd1fa67c018 + github.com/Arceliar/ironwood v0.0.0-20240321132619-86dcce58a105 github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d github.com/cheggaaa/pb/v3 v3.1.4 github.com/gologme/log v1.3.0 @@ -22,8 +22,8 @@ require ( ) require ( - github.com/bits-and-blooms/bitset v1.5.0 // indirect - github.com/bits-and-blooms/bloom/v3 v3.3.1 // indirect + github.com/bits-and-blooms/bitset v1.10.0 // indirect + github.com/bits-and-blooms/bloom/v3 v3.7.0 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/mattn/go-colorable v0.1.13 // indirect diff --git a/go.sum b/go.sum index 6cd0ab72..c1fa1adc 100644 --- a/go.sum +++ b/go.sum @@ -1,14 +1,13 @@ -github.com/Arceliar/ironwood v0.0.0-20240115190409-ddd1fa67c018 h1:7r/T7qJht4CaPl74AgU7dG5N6g7+2230/9BhrbtRijk= -github.com/Arceliar/ironwood v0.0.0-20240115190409-ddd1fa67c018/go.mod h1:5x7fWW0mshe9WQ1lvSMmmHBYC3BeHH9gpwW5tz7cbfw= +github.com/Arceliar/ironwood v0.0.0-20240321132619-86dcce58a105 h1:H7OQD74Pd7FVA7i6rOIun8rzKY8uhJf7hAV02N/L17M= +github.com/Arceliar/ironwood v0.0.0-20240321132619-86dcce58a105/go.mod h1:U5njW14T0/EAvTtVdNpRDUaWsbtcaQmZjHHmUUH43B8= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= -github.com/bits-and-blooms/bitset v1.3.1/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= -github.com/bits-and-blooms/bitset v1.5.0 h1:NpE8frKRLGHIcEzkR+gZhiioW1+WbYV6fKwD6ZIpQT8= -github.com/bits-and-blooms/bitset v1.5.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= -github.com/bits-and-blooms/bloom/v3 v3.3.1 h1:K2+A19bXT8gJR5mU7y+1yW6hsKfNCjcP2uNfLFKncjQ= -github.com/bits-and-blooms/bloom/v3 v3.3.1/go.mod h1:bhUUknWd5khVbTe4UgMCSiOOVJzr3tMoijSK3WwvW90= +github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88= +github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/bits-and-blooms/bloom/v3 v3.7.0 h1:VfknkqV4xI+PsaDIsoHueyxVDZrfvMn56jeWUzvzdls= +github.com/bits-and-blooms/bloom/v3 v3.7.0/go.mod h1:VKlUSvp0lFIYqxJjzdnSsZEw4iHb1kOL2tfHTgyJBHg= github.com/cheggaaa/pb/v3 v3.1.4 h1:DN8j4TVVdKu3WxVwcRKu0sG00IIU6FewoABZzXbRQeo= github.com/cheggaaa/pb/v3 v3.1.4/go.mod h1:6wVjILNBaXMs8c21qRiaUM8BR82erfgau1DQ4iUXmSA= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= From 6cbe56adfe43a9d95781f110611c4188b34b34c8 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sat, 25 May 2024 06:15:36 -0500 Subject: [PATCH 209/293] fix incorrect pool use --- src/core/core.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/core.go b/src/core/core.go index 79c59d5d..41858cb1 100644 --- a/src/core/core.go +++ b/src/core/core.go @@ -212,7 +212,7 @@ func (c *Core) ReadFrom(p []byte) (n int, from net.Addr, err error) { func (c *Core) WriteTo(p []byte, addr net.Addr) (n int, err error) { buf := allocBytes(0) - defer freeBytes(buf) + defer func() { freeBytes(buf) }() buf = append(buf, typeSessionTraffic) buf = append(buf, p...) n, err = c.PacketConn.WriteTo(buf, addr) From 6f3a0a71d43c8f25cf8d34346e5e3948fe4221ef Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sat, 25 May 2024 06:16:11 -0500 Subject: [PATCH 210/293] update ironwood and other dependencies --- go.mod | 12 ++++++------ go.sum | 33 +++++++++++++++++++-------------- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/go.mod b/go.mod index cf717d15..c4847dff 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/yggdrasil-network/yggdrasil-go go 1.20 require ( - github.com/Arceliar/ironwood v0.0.0-20240321132619-86dcce58a105 + github.com/Arceliar/ironwood v0.0.0-20240525111224-8b482710326e github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d github.com/cheggaaa/pb/v3 v3.1.4 github.com/gologme/log v1.3.0 @@ -12,17 +12,17 @@ require ( github.com/kardianos/minwinsvc v1.0.2 github.com/quic-go/quic-go v0.40.1 github.com/vishvananda/netlink v1.1.0 - golang.org/x/crypto v0.18.0 + golang.org/x/crypto v0.23.0 golang.org/x/mobile v0.0.0-20231006135142-2b44d11868fe - golang.org/x/net v0.20.0 - golang.org/x/sys v0.16.0 - golang.org/x/text v0.14.0 + golang.org/x/net v0.21.0 + golang.org/x/sys v0.20.0 + golang.org/x/text v0.15.0 golang.zx2c4.com/wireguard v0.0.0-20230223181233-21636207a675 golang.zx2c4.com/wireguard/windows v0.5.3 ) require ( - github.com/bits-and-blooms/bitset v1.10.0 // indirect + github.com/bits-and-blooms/bitset v1.13.0 // indirect github.com/bits-and-blooms/bloom/v3 v3.7.0 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect diff --git a/go.sum b/go.sum index c1fa1adc..8afa93f3 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,12 @@ -github.com/Arceliar/ironwood v0.0.0-20240321132619-86dcce58a105 h1:H7OQD74Pd7FVA7i6rOIun8rzKY8uhJf7hAV02N/L17M= -github.com/Arceliar/ironwood v0.0.0-20240321132619-86dcce58a105/go.mod h1:U5njW14T0/EAvTtVdNpRDUaWsbtcaQmZjHHmUUH43B8= +github.com/Arceliar/ironwood v0.0.0-20240525111224-8b482710326e h1:fBcasrsrE1kVlsAlyG7Rz5/wvGFKHO4z3GfHzEc0xwE= +github.com/Arceliar/ironwood v0.0.0-20240525111224-8b482710326e/go.mod h1:6WP4799FX0OuWdENGQAh+0RXp9FLh0y7NZ7tM9cJyXk= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= -github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88= github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= +github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bits-and-blooms/bloom/v3 v3.7.0 h1:VfknkqV4xI+PsaDIsoHueyxVDZrfvMn56jeWUzvzdls= github.com/bits-and-blooms/bloom/v3 v3.7.0/go.mod h1:VKlUSvp0lFIYqxJjzdnSsZEw4iHb1kOL2tfHTgyJBHg= github.com/cheggaaa/pb/v3 v3.1.4 h1:DN8j4TVVdKu3WxVwcRKu0sG00IIU6FewoABZzXbRQeo= @@ -70,9 +71,9 @@ go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo= go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= -golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= -golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o= golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/mobile v0.0.0-20231006135142-2b44d11868fe h1:lrXv4yHeD9FA8PSJATWowP1QvexpyAPWmPia+Kbzql8= @@ -85,9 +86,9 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= -golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -103,20 +104,24 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= From 5da1fbe39748a6e057c1eef02fb4eb6b124840b7 Mon Sep 17 00:00:00 2001 From: trashpile-shenanigans Date: Mon, 27 May 2024 23:53:52 +0300 Subject: [PATCH 211/293] Bump minimum required go version to 1.20 in documentation as required by quic-go dependency (#1138) --- CHANGELOG.md | 2 +- README.md | 2 +- contrib/msi/build-msi.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa3fd33e..6e663441 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -189,7 +189,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Changed -* Go 1.17 is now required to build Yggdrasil +* Go 1.20 is now required to build Yggdrasil ## [0.4.3] - 2022-02-06 diff --git a/README.md b/README.md index d0afae47..527c6c2d 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ or tools in the `contrib` folder. If you want to build from source, as opposed to installing one of the pre-built packages: -1. Install [Go](https://golang.org) (requires Go 1.17 or later) +1. Install [Go](https://golang.org) (requires Go 1.20 or later) 2. Clone this repository 2. Run `./build` diff --git a/contrib/msi/build-msi.sh b/contrib/msi/build-msi.sh index 38b9b810..94d57147 100644 --- a/contrib/msi/build-msi.sh +++ b/contrib/msi/build-msi.sh @@ -1,7 +1,7 @@ #!/bin/sh # This script generates an MSI file for Yggdrasil for a given architecture. It -# needs to run on Windows within MSYS2 and Go 1.17 or later must be installed on +# needs to run on Windows within MSYS2 and Go 1.20 or later must be installed on # the system and within the PATH. This is ran currently by GitHub Actions (see # the workflows in the repository). # From f56f9c124ca5aec0b7d51ec2a37e5a5ba554dce0 Mon Sep 17 00:00:00 2001 From: Paul Donald Date: Mon, 27 May 2024 22:57:28 +0200 Subject: [PATCH 212/293] Minor Fixes (#1107) * Minor comment fixes. * Optimize PeerEntry for memory efficiency * Improve NodeConfig for memory alignment --- cmd/yggdrasil/main.go | 8 ++++---- contrib/mobile/mobile.go | 4 ++-- src/admin/getpeers.go | 2 +- src/config/config.go | 2 +- src/core/link.go | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index a1c79492..82b85cd4 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -188,7 +188,7 @@ func main() { n := &node{} - // Setup the Yggdrasil node itself. + // Set up the Yggdrasil node itself. { options := []core.SetupOption{ core.NodeInfo(cfg.NodeInfo), @@ -221,7 +221,7 @@ func main() { logger.Printf("Your IPv6 subnet is %s", subnet.String()) } - // Setup the admin socket. + // Set up the admin socket. { options := []admin.SetupOption{ admin.ListenAddress(cfg.AdminListen), @@ -237,7 +237,7 @@ func main() { } } - // Setup the multicast module. + // Set up the multicast module. { options := []multicast.SetupOption{} for _, intf := range cfg.MulticastInterfaces { @@ -258,7 +258,7 @@ func main() { } } - // Setup the TUN module. + // Set up the TUN module. { options := []tun.SetupOption{ tun.InterfaceName(cfg.IfName), diff --git a/contrib/mobile/mobile.go b/contrib/mobile/mobile.go index cf026a70..82e73485 100644 --- a/contrib/mobile/mobile.go +++ b/contrib/mobile/mobile.go @@ -53,7 +53,7 @@ func (m *Yggdrasil) StartJSON(configjson []byte) error { if err := m.config.UnmarshalHJSON(configjson); err != nil { return err } - // Setup the Yggdrasil node itself. + // Set up the Yggdrasil node itself. { options := []core.SetupOption{} for _, peer := range m.config.Peers { @@ -85,7 +85,7 @@ func (m *Yggdrasil) StartJSON(configjson []byte) error { logger.Infof("Your IPv6 subnet is %s", subnet.String()) } - // Setup the multicast module. + // Set up the multicast module. if len(m.config.MulticastInterfaces) > 0 { var err error logger.Infof("Initializing multicast %s", "") diff --git a/src/admin/getpeers.go b/src/admin/getpeers.go index 9bc3872f..aad98294 100644 --- a/src/admin/getpeers.go +++ b/src/admin/getpeers.go @@ -27,8 +27,8 @@ type PeerEntry struct { RXBytes DataUnit `json:"bytes_recvd,omitempty"` TXBytes DataUnit `json:"bytes_sent,omitempty"` Uptime float64 `json:"uptime,omitempty"` - LastError string `json:"last_error,omitempty"` LastErrorTime time.Duration `json:"last_error_time,omitempty"` + LastError string `json:"last_error,omitempty"` } func (a *AdminSocket) getPeersHandler(req *GetPeersRequest, res *GetPeersResponse) error { diff --git a/src/config/config.go b/src/config/config.go index aac51467..9a7f7180 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -51,9 +51,9 @@ type NodeConfig struct { AllowedPublicKeys []string `comment:"List of peer public keys to allow incoming peering connections\nfrom. If left empty/undefined then all connections will be allowed\nby default. This does not affect outgoing peerings, nor does it\naffect link-local peers discovered via multicast."` IfName string `comment:"Local network interface name for TUN adapter, or \"auto\" to select\nan interface automatically, or \"none\" to run without TUN."` IfMTU uint64 `comment:"Maximum Transmission Unit (MTU) size for your local TUN interface.\nDefault is the largest supported size for your platform. The lowest\npossible value is 1280."` + LogLookups bool `json:",omitempty"` NodeInfoPrivacy bool `comment:"By default, nodeinfo contains some defaults including the platform,\narchitecture and Yggdrasil version. These can help when surveying\nthe network and diagnosing network routing problems. Enabling\nnodeinfo privacy prevents this, so that only items specified in\n\"NodeInfo\" are sent back if specified."` NodeInfo map[string]interface{} `comment:"Optional node info. This must be a { \"key\": \"value\", ... } map\nor set as null. This is entirely optional but, if set, is visible\nto the whole network on request."` - LogLookups bool `json:",omitempty"` } type MulticastInterfaceConfig struct { diff --git a/src/core/link.go b/src/core/link.go index 08a6a99e..6bcbec59 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -63,7 +63,7 @@ type link struct { // The remaining fields can only be modified safely from within the links actor _conn *linkConn // Connected link, if any, nil if not connected _err error // Last error on the connection, if any - _errtime time.Time // Last time an error occured + _errtime time.Time // Last time an error occurred } type linkOptions struct { From 5d9c5b3c9b2f51d6a5076a50624146513ac41ece Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 27 May 2024 22:03:41 +0100 Subject: [PATCH 213/293] Minimum Go 1.21, update `quic-go`, update some CI actions --- .github/workflows/ci.yml | 18 ++++++++--------- .github/workflows/pkg.yml | 16 +++++++-------- README.md | 2 +- contrib/msi/build-msi.sh | 2 +- go.mod | 18 ++++++++--------- go.sum | 42 +++++++++++++++++++++++---------------- 6 files changed, 53 insertions(+), 45 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 92ad2561..356b49de 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: name: Lint runs-on: ubuntu-latest steps: - - uses: actions/setup-go@v3 + - uses: actions/setup-go@v5 with: go-version: 1.21 - uses: actions/checkout@v3 @@ -51,7 +51,7 @@ jobs: strategy: fail-fast: false matrix: - goversion: ["1.20", "1.21"] + goversion: ["1.21", "1.22"] name: Build & Test (Linux, Go ${{ matrix.goversion }}) needs: [lint] @@ -61,7 +61,7 @@ jobs: - uses: actions/checkout@v3 - name: Set up Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: go-version: ${{ matrix.goversion }} @@ -75,7 +75,7 @@ jobs: strategy: fail-fast: false matrix: - goversion: ["1.20", "1.21"] + goversion: ["1.21", "1.22"] name: Build & Test (Windows, Go ${{ matrix.goversion }}) needs: [lint] @@ -85,7 +85,7 @@ jobs: - uses: actions/checkout@v3 - name: Set up Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: go-version: ${{ matrix.goversion }} @@ -99,7 +99,7 @@ jobs: strategy: fail-fast: false matrix: - goversion: ["1.20", "1.21"] + goversion: ["1.21", "1.22"] name: Build & Test (macOS, Go ${{ matrix.goversion }}) needs: [lint] @@ -109,7 +109,7 @@ jobs: - uses: actions/checkout@v3 - name: Set up Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: go-version: ${{ matrix.goversion }} @@ -123,7 +123,7 @@ jobs: strategy: fail-fast: false matrix: - goversion: ["1.20", "1.21"] + goversion: ["1.21", "1.22"] goos: - freebsd - openbsd @@ -136,7 +136,7 @@ jobs: - uses: actions/checkout@v3 - name: Set up Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: go-version: ${{ matrix.goversion }} diff --git a/.github/workflows/pkg.yml b/.github/workflows/pkg.yml index f47baa0f..a3ccfe43 100644 --- a/.github/workflows/pkg.yml +++ b/.github/workflows/pkg.yml @@ -23,9 +23,9 @@ jobs: fetch-depth: 0 - name: Set up Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: - go-version: "1.20" + go-version: "stable" - name: Build package env: @@ -54,9 +54,9 @@ jobs: fetch-depth: 0 - name: Set up Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: - go-version: "1.20" + go-version: "stable" - name: Build package env: @@ -85,9 +85,9 @@ jobs: fetch-depth: 0 - name: Set up Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: - go-version: "1.20" + go-version: "stable" - name: Build package run: sh contrib/msi/build-msi.sh ${{ matrix.pkgarch }} @@ -120,9 +120,9 @@ jobs: path: vyatta-yggdrasil - name: Set up Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: - go-version: "1.20" + go-version: "stable" - name: Build package env: diff --git a/README.md b/README.md index 527c6c2d..8449f073 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ or tools in the `contrib` folder. If you want to build from source, as opposed to installing one of the pre-built packages: -1. Install [Go](https://golang.org) (requires Go 1.20 or later) +1. Install [Go](https://golang.org) (requires Go 1.21 or later) 2. Clone this repository 2. Run `./build` diff --git a/contrib/msi/build-msi.sh b/contrib/msi/build-msi.sh index 94d57147..3c37393e 100644 --- a/contrib/msi/build-msi.sh +++ b/contrib/msi/build-msi.sh @@ -1,7 +1,7 @@ #!/bin/sh # This script generates an MSI file for Yggdrasil for a given architecture. It -# needs to run on Windows within MSYS2 and Go 1.20 or later must be installed on +# needs to run on Windows within MSYS2 and Go 1.21 or later must be installed on # the system and within the PATH. This is ran currently by GitHub Actions (see # the workflows in the repository). # diff --git a/go.mod b/go.mod index c4847dff..7b3b7cc9 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/yggdrasil-network/yggdrasil-go -go 1.20 +go 1.21 require ( github.com/Arceliar/ironwood v0.0.0-20240525111224-8b482710326e @@ -10,11 +10,11 @@ require ( github.com/hashicorp/go-syslog v1.0.0 github.com/hjson/hjson-go/v4 v4.4.0 github.com/kardianos/minwinsvc v1.0.2 - github.com/quic-go/quic-go v0.40.1 + github.com/quic-go/quic-go v0.44.0 github.com/vishvananda/netlink v1.1.0 golang.org/x/crypto v0.23.0 - golang.org/x/mobile v0.0.0-20231006135142-2b44d11868fe - golang.org/x/net v0.21.0 + golang.org/x/mobile v0.0.0-20240520174638-fa72addaaa1b + golang.org/x/net v0.25.0 golang.org/x/sys v0.20.0 golang.org/x/text v0.15.0 golang.zx2c4.com/wireguard v0.0.0-20230223181233-21636207a675 @@ -28,12 +28,12 @@ require ( github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/onsi/ginkgo/v2 v2.9.5 // indirect - github.com/quic-go/qtls-go1-20 v0.4.1 // indirect github.com/rivo/uniseg v0.2.0 // indirect - go.uber.org/mock v0.3.0 // indirect - golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect - golang.org/x/mod v0.13.0 // indirect - golang.org/x/tools v0.14.0 // indirect + go.uber.org/mock v0.4.0 // indirect + golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/tools v0.21.0 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect ) diff --git a/go.sum b/go.sum index 8afa93f3..137b5b8c 100644 --- a/go.sum +++ b/go.sum @@ -20,12 +20,15 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/gologme/log v1.3.0 h1:l781G4dE+pbigClDSDzSaaYKtiueHCILUa/qSDsmHAo= github.com/gologme/log v1.3.0/go.mod h1:yKT+DvIPdDdDoPtqFrFxheooyVmoqi0BAsw+erN3wA4= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= @@ -48,12 +51,11 @@ github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6 github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= +github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs= -github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= -github.com/quic-go/quic-go v0.40.1 h1:X3AGzUNFs0jVuO3esAGnTfvdgvL4fq655WaOi1snv1Q= -github.com/quic-go/quic-go v0.40.1/go.mod h1:PeN7kuVJ4xZbxSv/4OX6S1USOX8MJvydwpTx31vx60c= +github.com/quic-go/quic-go v0.44.0 h1:So5wOr7jyO4vzL2sd8/pD9Kesciv91zSk8BoFngItQ0= +github.com/quic-go/quic-go v0.44.0/go.mod h1:z4cx/9Ny9UtGITIPzmPTXh1ULfOyWh4qGQlpnPcWmek= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -67,32 +69,34 @@ github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17 github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f h1:p4VB7kIXpOQvVn1ZaTIVp+3vuYAXFe3OJEvjbUYJLaA= github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo= -go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= +go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= +go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o= -golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= -golang.org/x/mobile v0.0.0-20231006135142-2b44d11868fe h1:lrXv4yHeD9FA8PSJATWowP1QvexpyAPWmPia+Kbzql8= -golang.org/x/mobile v0.0.0-20231006135142-2b44d11868fe/go.mod h1:BrnXpEObnFxpaT75Jo9hsCazwOWcp7nVIa8NNuH5cuA= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= +golang.org/x/mobile v0.0.0-20240520174638-fa72addaaa1b h1:WX7nnnLfCEXg+FmdYZPai2XuP3VqCP1HZVMST0n9DF0= +golang.org/x/mobile v0.0.0-20240520174638-fa72addaaa1b/go.mod h1:EiXZlVfUTaAyySFVJb9rsODuiO+WXu8HrUuySb7nYFw= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= -golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -122,12 +126,14 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= -golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= +golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= +golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= @@ -136,6 +142,8 @@ golang.zx2c4.com/wireguard v0.0.0-20230223181233-21636207a675/go.mod h1:whfbyDBt golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE= golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI= google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From c2811c0cdc348a5575580d75d03cd8576f9060fb Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 27 May 2024 22:14:28 +0100 Subject: [PATCH 214/293] Update more GHA actions due to deprecations --- .github/workflows/ci.yml | 12 ++++++------ .github/workflows/pkg.yml | 18 +++++++++--------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 356b49de..a3f34789 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: - uses: actions/setup-go@v5 with: go-version: 1.21 - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: golangci-lint uses: golangci/golangci-lint-action@v3 with: @@ -34,7 +34,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Initialize CodeQL uses: github/codeql-action/init@v2 @@ -58,7 +58,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Go uses: actions/setup-go@v5 @@ -82,7 +82,7 @@ jobs: runs-on: windows-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Go uses: actions/setup-go@v5 @@ -106,7 +106,7 @@ jobs: runs-on: macos-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Go uses: actions/setup-go@v5 @@ -133,7 +133,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Go uses: actions/setup-go@v5 diff --git a/.github/workflows/pkg.yml b/.github/workflows/pkg.yml index a3ccfe43..c0045221 100644 --- a/.github/workflows/pkg.yml +++ b/.github/workflows/pkg.yml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 @@ -33,7 +33,7 @@ jobs: run: sh contrib/deb/generate.sh - name: Upload artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: Debian package (${{ matrix.pkgarch }}) path: "*.deb" @@ -49,7 +49,7 @@ jobs: runs-on: macos-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 @@ -64,7 +64,7 @@ jobs: run: sh contrib/macos/create-pkg.sh - name: Upload artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: macOS package (${{ matrix.pkgarch }}) path: "*.pkg" @@ -80,7 +80,7 @@ jobs: runs-on: windows-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 @@ -93,7 +93,7 @@ jobs: run: sh contrib/msi/build-msi.sh ${{ matrix.pkgarch }} - name: Upload artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: Windows package (${{ matrix.pkgarch }}) path: "*.msi" @@ -109,12 +109,12 @@ jobs: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 path: yggdrasil - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: repository: neilalexander/vyatta-yggdrasil path: vyatta-yggdrasil @@ -130,7 +130,7 @@ jobs: run: cd /home/runner/work/yggdrasil-go/yggdrasil-go/vyatta-yggdrasil && ./build-${{ matrix.pkgarch }} - name: Upload artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: Router package (${{ matrix.pkgarch }}) path: "/home/runner/work/yggdrasil-go/yggdrasil-go/vyatta-yggdrasil/*.deb" From 2831d73f73c273baf773a8b841bd425483c9d42a Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 27 May 2024 22:52:48 +0100 Subject: [PATCH 215/293] Try to fix WiX for Windows MSI builds --- .github/workflows/pkg.yml | 3 +++ contrib/msi/build-msi.sh | 26 +++++++++----------------- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/.github/workflows/pkg.yml b/.github/workflows/pkg.yml index c0045221..1484618e 100644 --- a/.github/workflows/pkg.yml +++ b/.github/workflows/pkg.yml @@ -89,6 +89,9 @@ jobs: with: go-version: "stable" + - name: Setup .NET Core SDK + uses: actions/setup-dotnet@v4 + - name: Build package run: sh contrib/msi/build-msi.sh ${{ matrix.pkgarch }} diff --git a/contrib/msi/build-msi.sh b/contrib/msi/build-msi.sh index 3c37393e..857aeec9 100644 --- a/contrib/msi/build-msi.sh +++ b/contrib/msi/build-msi.sh @@ -16,20 +16,7 @@ then fi # Download the wix tools! -if [ ! -d wixbin ]; -then - curl -LO https://wixtoolset.org/downloads/v3.14.0.3910/wix314-binaries.zip - if [ `md5sum wix314-binaries.zip | cut -f 1 -d " "` != "34f655cf108086838dd5a76d4318063b" ]; - then - echo "wix package didn't match expected checksum" - exit 1 - fi - mkdir -p wixbin - unzip -o wix314-binaries.zip -d wixbin || ( - echo "failed to unzip WiX" - exit 1 - ) -fi +dotnet tool install --global wix --version 5.0.0 # Build Yggdrasil! [ "${PKGARCH}" == "x64" ] && GOOS=windows GOARCH=amd64 CGO_ENABLED=0 ./build @@ -61,6 +48,11 @@ PKGVERSIONMS=$(echo $PKGVERSION | tr - .) if [ ! -d wintun ]; then curl -o wintun.zip https://www.wintun.net/builds/wintun-0.14.1.zip + if [ `sha256sum wintun.zip | cut -f 1 -d " "` != "07c256185d6ee3652e09fa55c0b673e2624b565e02c4b9091c79ca7d2f24ef51" ]; + then + echo "wintun package didn't match expected checksum" + exit 1 + fi unzip wintun.zip fi if [ $PKGARCH = "x64" ]; then @@ -101,7 +93,7 @@ cat > wix.xml << EOF Description="Yggdrasil Network Installer" Comments="Yggdrasil Network standalone router for Windows." Manufacturer="github.com/yggdrasil-network" - InstallerVersion="200" + InstallerVersion="500" InstallScope="perMachine" Languages="1033" Compressed="yes" @@ -205,5 +197,5 @@ EOF # Generate the MSI CANDLEFLAGS="-nologo" LIGHTFLAGS="-nologo -spdb -sice:ICE71 -sice:ICE61" -wixbin/candle $CANDLEFLAGS -out ${PKGNAME}-${PKGVERSION}-${PKGARCH}.wixobj -arch ${PKGARCH} wix.xml && \ -wixbin/light $LIGHTFLAGS -ext WixUtilExtension.dll -out ${PKGNAME}-${PKGVERSION}-${PKGARCH}.msi ${PKGNAME}-${PKGVERSION}-${PKGARCH}.wixobj +candle $CANDLEFLAGS -out ${PKGNAME}-${PKGVERSION}-${PKGARCH}.wixobj -arch ${PKGARCH} wix.xml && \ +light $LIGHTFLAGS -ext WixUtilExtension.dll -out ${PKGNAME}-${PKGVERSION}-${PKGARCH}.msi ${PKGNAME}-${PKGVERSION}-${PKGARCH}.wixobj From fcefb20993c51e06c140e922b61d7a9286c977b1 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 28 May 2024 10:03:48 +0100 Subject: [PATCH 216/293] Fix interval check when sending multicast beacons --- src/multicast/multicast.go | 1 + 1 file changed, 1 insertion(+) diff --git a/src/multicast/multicast.go b/src/multicast/multicast.go index 741c4315..bf10370f 100644 --- a/src/multicast/multicast.go +++ b/src/multicast/multicast.go @@ -358,6 +358,7 @@ func (m *Multicast) _announce() { if linfo.interval.Seconds() < 15 { linfo.interval += time.Second } + linfo.time = time.Now() break } } From f788a18befe25579d9406f51a2d22cc1a1a26d26 Mon Sep 17 00:00:00 2001 From: Neil Date: Thu, 30 May 2024 22:46:06 +0100 Subject: [PATCH 217/293] Measure RTT, report in `getPeers` (#1143) Co-authored-by: Neil Alexander --- cmd/yggdrasilctl/main.go | 7 +++++-- go.mod | 2 +- go.sum | 4 ++-- src/admin/getpeers.go | 4 ++++ src/core/api.go | 2 ++ 5 files changed, 14 insertions(+), 5 deletions(-) diff --git a/cmd/yggdrasilctl/main.go b/cmd/yggdrasilctl/main.go index db823417..2a1d70b1 100644 --- a/cmd/yggdrasilctl/main.go +++ b/cmd/yggdrasilctl/main.go @@ -174,11 +174,13 @@ func run() int { if err := json.Unmarshal(recv.Response, &resp); err != nil { panic(err) } - table.SetHeader([]string{"URI", "State", "Dir", "IP Address", "Uptime", "RX", "TX", "Pr", "Last Error"}) + table.SetHeader([]string{"URI", "State", "Dir", "IP Address", "Uptime", "RTT", "RX", "TX", "Pr", "Last Error"}) for _, peer := range resp.Peers { - state, lasterr, dir := "Up", "-", "Out" + state, lasterr, dir, rtt := "Up", "-", "Out", "-" if !peer.Up { state, lasterr = "Down", fmt.Sprintf("%s ago: %s", peer.LastErrorTime.Round(time.Second), peer.LastError) + } else if rttms := float64(peer.Latency.Microseconds()) / 1000; rttms > 0 { + rtt = fmt.Sprintf("%.02fms", rttms) } if peer.Inbound { dir = "In" @@ -194,6 +196,7 @@ func run() int { dir, peer.IPAddress, (time.Duration(peer.Uptime) * time.Second).String(), + rtt, peer.RXBytes.String(), peer.TXBytes.String(), fmt.Sprintf("%d", peer.Priority), diff --git a/go.mod b/go.mod index 7b3b7cc9..3dd8c848 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/yggdrasil-network/yggdrasil-go go 1.21 require ( - github.com/Arceliar/ironwood v0.0.0-20240525111224-8b482710326e + github.com/Arceliar/ironwood v0.0.0-20240529054413-b8e59574e2b2 github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d github.com/cheggaaa/pb/v3 v3.1.4 github.com/gologme/log v1.3.0 diff --git a/go.sum b/go.sum index 137b5b8c..7ee76861 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Arceliar/ironwood v0.0.0-20240525111224-8b482710326e h1:fBcasrsrE1kVlsAlyG7Rz5/wvGFKHO4z3GfHzEc0xwE= -github.com/Arceliar/ironwood v0.0.0-20240525111224-8b482710326e/go.mod h1:6WP4799FX0OuWdENGQAh+0RXp9FLh0y7NZ7tM9cJyXk= +github.com/Arceliar/ironwood v0.0.0-20240529054413-b8e59574e2b2 h1:SBdYBKeXYUUFef5wi2CMhYmXFVGiYaRpTvbki0Bu+JQ= +github.com/Arceliar/ironwood v0.0.0-20240529054413-b8e59574e2b2/go.mod h1:6WP4799FX0OuWdENGQAh+0RXp9FLh0y7NZ7tM9cJyXk= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= diff --git a/src/admin/getpeers.go b/src/admin/getpeers.go index aad98294..f79877ca 100644 --- a/src/admin/getpeers.go +++ b/src/admin/getpeers.go @@ -27,6 +27,7 @@ type PeerEntry struct { RXBytes DataUnit `json:"bytes_recvd,omitempty"` TXBytes DataUnit `json:"bytes_sent,omitempty"` Uptime float64 `json:"uptime,omitempty"` + Latency time.Duration `json:"latency_ms,omitempty"` LastErrorTime time.Duration `json:"last_error_time,omitempty"` LastError string `json:"last_error,omitempty"` } @@ -45,6 +46,9 @@ func (a *AdminSocket) getPeersHandler(req *GetPeersRequest, res *GetPeersRespons TXBytes: DataUnit(p.TXBytes), Uptime: p.Uptime.Seconds(), } + if p.Latency > 0 { + peer.Latency = p.Latency + } if addr := address.AddrForKey(p.Key); addr != nil { peer.PublicKey = hex.EncodeToString(p.Key) peer.IPAddress = net.IP(addr[:]).String() diff --git a/src/core/api.go b/src/core/api.go index b5fa93cb..875d7bf2 100644 --- a/src/core/api.go +++ b/src/core/api.go @@ -33,6 +33,7 @@ type PeerInfo struct { RXBytes uint64 TXBytes uint64 Uptime time.Duration + Latency time.Duration } type TreeEntryInfo struct { @@ -92,6 +93,7 @@ func (c *Core) GetPeers() []PeerInfo { peerinfo.Root = p.Root peerinfo.Port = p.Port peerinfo.Priority = p.Priority + peerinfo.Latency = p.Latency } peers = append(peers, peerinfo) } From fec96a38a423142ac1712eb63ad7651c5e7a82aa Mon Sep 17 00:00:00 2001 From: Neil Date: Thu, 30 May 2024 23:30:05 +0100 Subject: [PATCH 218/293] Release: Yggdrasil v0.5.6 (#1144) * Changelog updates for Yggdrasil v0.5.6 * Fix spelling error --------- Co-authored-by: Neil Alexander Co-authored-by: Arceliar --- CHANGELOG.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e663441..b339294c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,26 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - in case of vulnerabilities. --> +## [0.5.6] - 2024-05-30 + +* Go 1.21 is now required to build Yggdrasil + +### Added + +* The `getPeers` endpoint now reports the RTT/latency of directly connected peers + +### Changed + +* The tree parent selection algorithm now prefers the lowest latency peers instead of the most stable +* Session key exchange logic has been changed to improve throughput and reduce occasional jitter + +### Fixed + +* Bloom filter hashing now works correctly on big-endian architectures +* Incorrect buffer pool usage has been fixed, reducing memory allocations +* The multicast beacon interval now backs off correctly, reducing the number of beacons sent +* A denial-of-service vulnerability in the QUIC library has been fixed with a dependency update + ## [0.5.5] - 2024-01-27 ### Added From c505097be077ee2af02ce37a9f73e2efa08feeb7 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 26 Jun 2024 23:17:11 +0100 Subject: [PATCH 219/293] Update mobile build for iOS/macOS framework generation --- contrib/mobile/{mobile_ios.go => mobile_apple.go} | 4 ++-- contrib/mobile/mobile_other.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename contrib/mobile/{mobile_ios.go => mobile_apple.go} (94%) diff --git a/contrib/mobile/mobile_ios.go b/contrib/mobile/mobile_apple.go similarity index 94% rename from contrib/mobile/mobile_ios.go rename to contrib/mobile/mobile_apple.go index c7747ea3..2c53fa94 100644 --- a/contrib/mobile/mobile_ios.go +++ b/contrib/mobile/mobile_apple.go @@ -1,5 +1,5 @@ -//go:build ios -// +build ios +//go:build ios || darwin +// +build ios darwin package mobile diff --git a/contrib/mobile/mobile_other.go b/contrib/mobile/mobile_other.go index aceeb7e2..10804b3e 100644 --- a/contrib/mobile/mobile_other.go +++ b/contrib/mobile/mobile_other.go @@ -1,5 +1,5 @@ -//go:build !android && !ios -// +build !android,!ios +//go:build !android && !ios && !darwin +// +build !android,!ios,!darwin package mobile From 8ecc402d7c48da048cf700f5c6028737a14abfad Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 20 Jul 2024 11:31:08 +0100 Subject: [PATCH 220/293] Allow multiple connections to the same link-local address Note that this may mean that currently we end up with two links to each multicast-discovered peer, one incoming and one outgoing --- src/core/link.go | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/core/link.go b/src/core/link.go index 6bcbec59..fb780040 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -7,7 +7,6 @@ import ( "fmt" "io" "net" - "net/netip" "net/url" "strconv" "strings" @@ -641,16 +640,6 @@ func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn, s func urlForLinkInfo(u url.URL) url.URL { u.RawQuery = "" - if host, _, err := net.SplitHostPort(u.Host); err == nil { - if addr, err := netip.ParseAddr(host); err == nil { - // For peers that look like multicast peers (i.e. - // link-local addresses), we will ignore the port number, - // otherwise we might open multiple connections to them. - if addr.IsLinkLocalUnicast() { - u.Host = fmt.Sprintf("[%s]", addr.String()) - } - } - } return u } From 04c0acf71b8e21679f9b68e8145e9c3094da59ff Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 20 Jul 2024 12:31:58 +0100 Subject: [PATCH 221/293] Various clean-ups --- src/admin/addpeer.go | 2 +- src/admin/admin.go | 22 ---------------------- src/admin/getpaths.go | 2 +- src/admin/getpeers.go | 2 +- src/admin/getself.go | 2 +- src/admin/getsessions.go | 2 +- src/admin/gettree.go | 6 +----- src/admin/removepeer.go | 2 +- src/core/link.go | 4 ++-- src/core/link_tcp_darwin.go | 2 +- src/multicast/admin.go | 2 +- 11 files changed, 11 insertions(+), 37 deletions(-) diff --git a/src/admin/addpeer.go b/src/admin/addpeer.go index 843ed52a..a897e45e 100644 --- a/src/admin/addpeer.go +++ b/src/admin/addpeer.go @@ -12,7 +12,7 @@ type AddPeerRequest struct { type AddPeerResponse struct{} -func (a *AdminSocket) addPeerHandler(req *AddPeerRequest, res *AddPeerResponse) error { +func (a *AdminSocket) addPeerHandler(req *AddPeerRequest, _ *AddPeerResponse) error { u, err := url.Parse(req.Uri) if err != nil { return fmt.Errorf("unable to parse peering URI: %w", err) diff --git a/src/admin/admin.go b/src/admin/admin.go index 8fa76c07..7cca1bbb 100644 --- a/src/admin/admin.go +++ b/src/admin/admin.go @@ -201,10 +201,6 @@ func (a *AdminSocket) SetupAdminHandlers() { return res, nil }, ) - //_ = a.AddHandler("getNodeInfo", []string{"key"}, t.proto.nodeinfo.nodeInfoAdminHandler) - //_ = a.AddHandler("debug_remoteGetSelf", []string{"key"}, t.proto.getSelfHandler) - //_ = a.AddHandler("debug_remoteGetPeers", []string{"key"}, t.proto.getPeersHandler) - //_ = a.AddHandler("debug_remoteGetDHT", []string{"key"}, t.proto.getDHTHandler) } // IsStarted returns true if the module has been started. @@ -269,7 +265,6 @@ func (a *AdminSocket) listen() { case "tcp": a.listener, err = net.Listen("tcp", u.Host) default: - // err = errors.New(fmt.Sprint("protocol not supported: ", u.Scheme)) a.listener, err = net.Listen("tcp", listenaddr) } } else { @@ -309,23 +304,6 @@ func (a *AdminSocket) handleRequest(conn net.Conn) { defer conn.Close() - /* - defer func() { - r := recover() - if r != nil { - fmt.Println("ERROR:", r) - a.log.Debugln("Admin socket error:", r) - if err := encoder.Encode(&ErrorResponse{ - Error: "Check your syntax and input types", - }); err != nil { - fmt.Println("ERROR 2:", err) - a.log.Debugln("Admin socket JSON encode error:", err) - } - conn.Close() - } - }() - */ - for { var err error var buf json.RawMessage diff --git a/src/admin/getpaths.go b/src/admin/getpaths.go index 66e11bd2..34de4532 100644 --- a/src/admin/getpaths.go +++ b/src/admin/getpaths.go @@ -23,7 +23,7 @@ type PathEntry struct { Sequence uint64 `json:"sequence"` } -func (a *AdminSocket) getPathsHandler(req *GetPathsRequest, res *GetPathsResponse) error { +func (a *AdminSocket) getPathsHandler(_ *GetPathsRequest, res *GetPathsResponse) error { paths := a.core.GetPaths() res.Paths = make([]PathEntry, 0, len(paths)) for _, p := range paths { diff --git a/src/admin/getpeers.go b/src/admin/getpeers.go index f79877ca..e44428c3 100644 --- a/src/admin/getpeers.go +++ b/src/admin/getpeers.go @@ -32,7 +32,7 @@ type PeerEntry struct { LastError string `json:"last_error,omitempty"` } -func (a *AdminSocket) getPeersHandler(req *GetPeersRequest, res *GetPeersResponse) error { +func (a *AdminSocket) getPeersHandler(_ *GetPeersRequest, res *GetPeersResponse) error { peers := a.core.GetPeers() res.Peers = make([]PeerEntry, 0, len(peers)) for _, p := range peers { diff --git a/src/admin/getself.go b/src/admin/getself.go index 9b052101..b1a01567 100644 --- a/src/admin/getself.go +++ b/src/admin/getself.go @@ -17,7 +17,7 @@ type GetSelfResponse struct { Subnet string `json:"subnet"` } -func (a *AdminSocket) getSelfHandler(req *GetSelfRequest, res *GetSelfResponse) error { +func (a *AdminSocket) getSelfHandler(_ *GetSelfRequest, res *GetSelfResponse) error { self := a.core.GetSelf() snet := a.core.Subnet() res.BuildName = version.BuildName() diff --git a/src/admin/getsessions.go b/src/admin/getsessions.go index 324119d4..e6702f88 100644 --- a/src/admin/getsessions.go +++ b/src/admin/getsessions.go @@ -23,7 +23,7 @@ type SessionEntry struct { Uptime float64 `json:"uptime"` } -func (a *AdminSocket) getSessionsHandler(req *GetSessionsRequest, res *GetSessionsResponse) error { +func (a *AdminSocket) getSessionsHandler(_ *GetSessionsRequest, res *GetSessionsResponse) error { sessions := a.core.GetSessions() res.Sessions = make([]SessionEntry, 0, len(sessions)) for _, s := range sessions { diff --git a/src/admin/gettree.go b/src/admin/gettree.go index 06cf8e73..4b6f32a8 100644 --- a/src/admin/gettree.go +++ b/src/admin/gettree.go @@ -20,11 +20,9 @@ type TreeEntry struct { PublicKey string `json:"key"` Parent string `json:"parent"` Sequence uint64 `json:"sequence"` - //Port uint64 `json:"port"` - //Rest uint64 `json:"rest"` } -func (a *AdminSocket) getTreeHandler(req *GetTreeRequest, res *GetTreeResponse) error { +func (a *AdminSocket) getTreeHandler(_ *GetTreeRequest, res *GetTreeResponse) error { tree := a.core.GetTree() res.Tree = make([]TreeEntry, 0, len(tree)) for _, d := range tree { @@ -34,8 +32,6 @@ func (a *AdminSocket) getTreeHandler(req *GetTreeRequest, res *GetTreeResponse) PublicKey: hex.EncodeToString(d.Key[:]), Parent: hex.EncodeToString(d.Parent[:]), Sequence: d.Sequence, - //Port: d.Port, - //Rest: d.Rest, }) } sort.SliceStable(res.Tree, func(i, j int) bool { diff --git a/src/admin/removepeer.go b/src/admin/removepeer.go index 6b2e162e..6f368656 100644 --- a/src/admin/removepeer.go +++ b/src/admin/removepeer.go @@ -12,7 +12,7 @@ type RemovePeerRequest struct { type RemovePeerResponse struct{} -func (a *AdminSocket) removePeerHandler(req *RemovePeerRequest, res *RemovePeerResponse) error { +func (a *AdminSocket) removePeerHandler(req *RemovePeerRequest, _ *RemovePeerResponse) error { u, err := url.Parse(req.Uri) if err != nil { return fmt.Errorf("unable to parse peering URI: %w", err) diff --git a/src/core/link.go b/src/core/link.go index fb780040..b646605c 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -378,7 +378,7 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error { return retErr } -func (l *links) remove(u *url.URL, sintf string, linkType linkType) error { +func (l *links) remove(u *url.URL, sintf string, _ linkType) error { var retErr error phony.Block(l, func() { // Generate the link info and see whether we think we already @@ -566,7 +566,7 @@ func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn, s switch { case err != nil: return fmt.Errorf("write handshake: %w", err) - case err == nil && n != len(metaBytes): + case n != len(metaBytes): return fmt.Errorf("incomplete handshake send") } meta = version_metadata{} diff --git a/src/core/link_tcp_darwin.go b/src/core/link_tcp_darwin.go index daa51df0..4c19ba93 100644 --- a/src/core/link_tcp_darwin.go +++ b/src/core/link_tcp_darwin.go @@ -28,6 +28,6 @@ func (t *linkTCP) tcpContext(network, address string, c syscall.RawConn) error { } } -func (t *linkTCP) getControl(sintf string) func(string, string, syscall.RawConn) error { +func (t *linkTCP) getControl(_ string) func(string, string, syscall.RawConn) error { return t.tcpContext } diff --git a/src/multicast/admin.go b/src/multicast/admin.go index 9bcc257b..8b29716d 100644 --- a/src/multicast/admin.go +++ b/src/multicast/admin.go @@ -11,7 +11,7 @@ type GetMulticastInterfacesResponse struct { Interfaces []string `json:"multicast_interfaces"` } -func (m *Multicast) getMulticastInterfacesHandler(req *GetMulticastInterfacesRequest, res *GetMulticastInterfacesResponse) error { +func (m *Multicast) getMulticastInterfacesHandler(_ *GetMulticastInterfacesRequest, res *GetMulticastInterfacesResponse) error { res.Interfaces = []string{} for _, v := range m.Interfaces() { res.Interfaces = append(res.Interfaces, v.Name) From 02d92ff81c2dd986323492ef9f6af26a2bab5ec5 Mon Sep 17 00:00:00 2001 From: Neil Date: Sat, 20 Jul 2024 15:24:30 +0100 Subject: [PATCH 222/293] TUN vectorised reads/writes (#1145) This PR updates the Wireguard dependency and updates to use new vectorised reads/writes, which should reduce the number of syscalls and improve performance. This will only make a difference on Linux as this is the only platform for which the Wireguard TUN library supports vectorised reads/writes. For other platforms, single reads and writes will be performed as usual. --------- Co-authored-by: Neil Alexander --- go.mod | 2 +- go.sum | 8 ++++-- src/tun/iface.go | 56 ++++++++++++++++++++++++++++-------------- src/tun/tun.go | 32 ++++++++++++++++++++++-- src/tun/tun_bsd.go | 3 +++ src/tun/tun_darwin.go | 6 +++++ src/tun/tun_linux.go | 3 +++ src/tun/tun_other.go | 3 +++ src/tun/tun_windows.go | 3 +++ 9 files changed, 92 insertions(+), 24 deletions(-) diff --git a/go.mod b/go.mod index 3dd8c848..eec96260 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( golang.org/x/net v0.25.0 golang.org/x/sys v0.20.0 golang.org/x/text v0.15.0 - golang.zx2c4.com/wireguard v0.0.0-20230223181233-21636207a675 + golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 golang.zx2c4.com/wireguard/windows v0.5.3 ) diff --git a/go.sum b/go.sum index 7ee76861..b7d97986 100644 --- a/go.sum +++ b/go.sum @@ -27,6 +27,8 @@ github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/gologme/log v1.3.0 h1:l781G4dE+pbigClDSDzSaaYKtiueHCILUa/qSDsmHAo= github.com/gologme/log v1.3.0/go.mod h1:yKT+DvIPdDdDoPtqFrFxheooyVmoqi0BAsw+erN3wA4= +github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= @@ -137,8 +139,8 @@ golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= -golang.zx2c4.com/wireguard v0.0.0-20230223181233-21636207a675 h1:/J/RVnr7ng4fWPRH3xa4WtBJ1Jp+Auu4YNLmGiPv5QU= -golang.zx2c4.com/wireguard v0.0.0-20230223181233-21636207a675/go.mod h1:whfbyDBt09xhCYQWtO2+3UVjlaq6/9hDZrjg2ZE6SyA= +golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4= +golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA= golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE= golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI= google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= @@ -147,3 +149,5 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 h1:TbRPT0HtzFP3Cno1zZo7yPzEEnfu8EjLfl6IU9VfqkQ= +gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259/go.mod h1:AVgIgHMwK63XvmAzWG9vLQ41YnVHN0du0tEC46fI7yY= diff --git a/src/tun/iface.go b/src/tun/iface.go index e06f3a5d..3a4c55f4 100644 --- a/src/tun/iface.go +++ b/src/tun/iface.go @@ -1,42 +1,60 @@ package tun -const TUN_OFFSET_BYTES = 4 +const TUN_OFFSET_BYTES = 80 // sizeof(virtio_net_hdr) func (tun *TunAdapter) read() { - var buf [TUN_OFFSET_BYTES + 65535]byte + vs := tun.iface.BatchSize() + bufs := make([][]byte, vs) + sizes := make([]int, vs) + for i := range bufs { + bufs[i] = make([]byte, TUN_OFFSET_BYTES+65535) + } for { - n, err := tun.iface.Read(buf[:], TUN_OFFSET_BYTES) - if n <= TUN_OFFSET_BYTES || err != nil { + n, err := tun.iface.Read(bufs, sizes, TUN_OFFSET_BYTES) + if err != nil { tun.log.Errorln("Error reading TUN:", err) - ferr := tun.iface.Flush() - if ferr != nil { - tun.log.Errorln("Unable to flush packets:", ferr) - } return } - begin := TUN_OFFSET_BYTES - end := begin + n - bs := buf[begin:end] - if _, err := tun.rwc.Write(bs); err != nil { - tun.log.Debugln("Unable to send packet:", err) + for i, b := range bufs[:n] { + if _, err := tun.rwc.Write(b[TUN_OFFSET_BYTES : TUN_OFFSET_BYTES+sizes[i]]); err != nil { + tun.log.Debugln("Unable to send packet:", err) + } } } } -func (tun *TunAdapter) write() { - var buf [TUN_OFFSET_BYTES + 65535]byte +func (tun *TunAdapter) queue() { for { - bs := buf[TUN_OFFSET_BYTES:] - n, err := tun.rwc.Read(bs) + p := bufPool.Get().([]byte)[:bufPoolSize] + n, err := tun.rwc.Read(p) if err != nil { tun.log.Errorln("Exiting TUN writer due to core read error:", err) return } + tun.ch <- p[:n] + } +} + +func (tun *TunAdapter) write() { + vs := cap(tun.ch) + bufs := make([][]byte, vs) + for i := range bufs { + bufs[i] = make([]byte, TUN_OFFSET_BYTES+65535) + } + for { + n := len(tun.ch) + if n == 0 { + n = 1 // Nothing queued up yet, wait for it instead + } + for i := 0; i < n; i++ { + msg := <-tun.ch + bufs[i] = append(bufs[i][:TUN_OFFSET_BYTES], msg...) + bufPool.Put(msg) // nolint:staticcheck + } if !tun.isEnabled { continue // Nothing to do, the tun isn't enabled } - bs = buf[:TUN_OFFSET_BYTES+n] - if _, err = tun.iface.Write(bs, TUN_OFFSET_BYTES); err != nil { + if _, err := tun.iface.Write(bufs[:n], TUN_OFFSET_BYTES); err != nil { tun.Act(nil, func() { if !tun.isOpen { tun.log.Errorln("TUN iface write error:", err) diff --git a/src/tun/tun.go b/src/tun/tun.go index 83c82670..e6795c64 100644 --- a/src/tun/tun.go +++ b/src/tun/tun.go @@ -10,9 +10,11 @@ import ( "fmt" "io" "net" + "sync" + "time" "github.com/Arceliar/phony" - "golang.zx2c4.com/wireguard/tun" + wgtun "golang.zx2c4.com/wireguard/tun" "github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/config" @@ -39,7 +41,7 @@ type TunAdapter struct { addr address.Address subnet address.Subnet mtu uint64 - iface tun.Device + iface wgtun.Device phony.Inbox // Currently only used for _handlePacket from the reader, TODO: all the stuff that currently needs a mutex below isOpen bool isEnabled bool // Used by the writer to drop sessionTraffic if not enabled @@ -48,6 +50,7 @@ type TunAdapter struct { name InterfaceName mtu InterfaceMTU } + ch chan []byte } // Gets the maximum supported MTU for the platform based on the defaults in @@ -62,6 +65,20 @@ func getSupportedMTU(mtu uint64) uint64 { return mtu } +func waitForTUNUp(ch <-chan wgtun.Event) bool { + t := time.After(time.Second * 5) + for { + select { + case ev := <-ch: + if ev == wgtun.EventUp { + return true + } + case <-t: + return false + } + } +} + // Name returns the name of the adapter, e.g. "tun0". On Windows, this may // return a canonical adapter name instead. func (tun *TunAdapter) Name() string { @@ -145,6 +162,8 @@ func (tun *TunAdapter) _start() error { tun.rwc.SetMTU(tun.MTU()) tun.isOpen = true tun.isEnabled = true + tun.ch = make(chan []byte, tun.iface.BatchSize()) + go tun.queue() go tun.read() go tun.write() return nil @@ -178,3 +197,12 @@ func (tun *TunAdapter) _stop() error { } return nil } + +const bufPoolSize = TUN_OFFSET_BYTES + 65535 + +var bufPool = sync.Pool{ + New: func() any { + b := [bufPoolSize]byte{} + return b[:] + }, +} diff --git a/src/tun/tun_bsd.go b/src/tun/tun_bsd.go index da5b3297..7f26260c 100644 --- a/src/tun/tun_bsd.go +++ b/src/tun/tun_bsd.go @@ -80,6 +80,9 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error { if err != nil { return fmt.Errorf("failed to create TUN: %w", err) } + if !waitForTUNUp(iface.Events()) { + return fmt.Errorf("TUN did not come up in time") + } tun.iface = iface if mtu, err := iface.MTU(); err == nil { tun.mtu = getSupportedMTU(uint64(mtu)) diff --git a/src/tun/tun_darwin.go b/src/tun/tun_darwin.go index deeb115e..a9d734bb 100644 --- a/src/tun/tun_darwin.go +++ b/src/tun/tun_darwin.go @@ -27,6 +27,9 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error { if err != nil { return fmt.Errorf("failed to create TUN: %w", err) } + if !waitForTUNUp(iface.Events()) { + return fmt.Errorf("TUN did not come up in time") + } tun.iface = iface if m, err := iface.MTU(); err == nil { tun.mtu = getSupportedMTU(uint64(m)) @@ -55,6 +58,9 @@ func (tun *TunAdapter) setupFD(fd int32, addr string, mtu uint64) error { unix.Close(dfd) return fmt.Errorf("failed to create TUN from FD: %w", err) } + if !waitForTUNUp(iface.Events()) { + return fmt.Errorf("TUN did not come up in time") + } tun.iface = iface if m, err := iface.MTU(); err == nil { tun.mtu = getSupportedMTU(uint64(m)) diff --git a/src/tun/tun_linux.go b/src/tun/tun_linux.go index 98d63db4..c98cdd73 100644 --- a/src/tun/tun_linux.go +++ b/src/tun/tun_linux.go @@ -21,6 +21,9 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error { if err != nil { return fmt.Errorf("failed to create TUN: %w", err) } + if !waitForTUNUp(iface.Events()) { + return fmt.Errorf("TUN did not come up in time") + } tun.iface = iface if mtu, err := iface.MTU(); err == nil { tun.mtu = getSupportedMTU(uint64(mtu)) diff --git a/src/tun/tun_other.go b/src/tun/tun_other.go index 0ddd0c1e..fc940818 100644 --- a/src/tun/tun_other.go +++ b/src/tun/tun_other.go @@ -18,6 +18,9 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error { if err != nil { return fmt.Errorf("failed to create TUN: %w", err) } + if !waitForTUNUp(iface.Events()) { + return fmt.Errorf("TUN did not come up in time") + } tun.iface = iface if mtu, err := iface.MTU(); err == nil { tun.mtu = getSupportedMTU(uint64(mtu)) diff --git a/src/tun/tun_windows.go b/src/tun/tun_windows.go index 19d9248c..7bcdb7ac 100644 --- a/src/tun/tun_windows.go +++ b/src/tun/tun_windows.go @@ -34,6 +34,9 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error { if iface, err = wgtun.CreateTUNWithRequestedGUID(ifname, &guid, int(mtu)); err != nil { return err } + if !waitForTUNUp(iface.Events()) { + return fmt.Errorf("TUN did not come up in time") + } tun.iface = iface if addr != "" { if err = tun.setupAddress(addr); err != nil { From da7ebde828ee6cf0a2c896676e6d409448850fb4 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 20 Jul 2024 15:37:31 +0100 Subject: [PATCH 223/293] Update dependencies --- go.mod | 22 +++++++++++----------- go.sum | 45 ++++++++++++++++++++++++--------------------- 2 files changed, 35 insertions(+), 32 deletions(-) diff --git a/go.mod b/go.mod index eec96260..d32d1143 100644 --- a/go.mod +++ b/go.mod @@ -5,18 +5,18 @@ go 1.21 require ( github.com/Arceliar/ironwood v0.0.0-20240529054413-b8e59574e2b2 github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d - github.com/cheggaaa/pb/v3 v3.1.4 + github.com/cheggaaa/pb/v3 v3.1.5 github.com/gologme/log v1.3.0 github.com/hashicorp/go-syslog v1.0.0 github.com/hjson/hjson-go/v4 v4.4.0 github.com/kardianos/minwinsvc v1.0.2 - github.com/quic-go/quic-go v0.44.0 + github.com/quic-go/quic-go v0.45.1 github.com/vishvananda/netlink v1.1.0 - golang.org/x/crypto v0.23.0 - golang.org/x/mobile v0.0.0-20240520174638-fa72addaaa1b - golang.org/x/net v0.25.0 - golang.org/x/sys v0.20.0 - golang.org/x/text v0.15.0 + golang.org/x/crypto v0.25.0 + golang.org/x/mobile v0.0.0-20240716161057-1ad2df20a8b6 + golang.org/x/net v0.27.0 + golang.org/x/sys v0.22.0 + golang.org/x/text v0.16.0 golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 golang.zx2c4.com/wireguard/windows v0.5.3 ) @@ -30,10 +30,10 @@ require ( github.com/onsi/ginkgo/v2 v2.9.5 // indirect github.com/rivo/uniseg v0.2.0 // indirect go.uber.org/mock v0.4.0 // indirect - golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect - golang.org/x/mod v0.17.0 // indirect + golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect + golang.org/x/mod v0.19.0 // indirect golang.org/x/sync v0.7.0 // indirect - golang.org/x/tools v0.21.0 // indirect + golang.org/x/tools v0.23.0 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect ) @@ -41,7 +41,7 @@ require ( github.com/VividCortex/ewma v1.2.0 // indirect github.com/fatih/color v1.15.0 // indirect github.com/mattn/go-isatty v0.0.19 // indirect - github.com/mattn/go-runewidth v0.0.14 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect github.com/olekukonko/tablewriter v0.0.5 github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f // indirect ) diff --git a/go.sum b/go.sum index b7d97986..be42c112 100644 --- a/go.sum +++ b/go.sum @@ -9,8 +9,8 @@ github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJR github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bits-and-blooms/bloom/v3 v3.7.0 h1:VfknkqV4xI+PsaDIsoHueyxVDZrfvMn56jeWUzvzdls= github.com/bits-and-blooms/bloom/v3 v3.7.0/go.mod h1:VKlUSvp0lFIYqxJjzdnSsZEw4iHb1kOL2tfHTgyJBHg= -github.com/cheggaaa/pb/v3 v3.1.4 h1:DN8j4TVVdKu3WxVwcRKu0sG00IIU6FewoABZzXbRQeo= -github.com/cheggaaa/pb/v3 v3.1.4/go.mod h1:6wVjILNBaXMs8c21qRiaUM8BR82erfgau1DQ4iUXmSA= +github.com/cheggaaa/pb/v3 v3.1.5 h1:QuuUzeM2WsAqG2gMqtzaWithDJv0i+i6UlnwSCI4QLk= +github.com/cheggaaa/pb/v3 v3.1.5/go.mod h1:CrxkeghYTXi1lQBEI7jSn+3svI3cuc19haAj6jM60XI= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -46,8 +46,8 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= -github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= @@ -56,8 +56,8 @@ github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/quic-go/quic-go v0.44.0 h1:So5wOr7jyO4vzL2sd8/pD9Kesciv91zSk8BoFngItQ0= -github.com/quic-go/quic-go v0.44.0/go.mod h1:z4cx/9Ny9UtGITIPzmPTXh1ULfOyWh4qGQlpnPcWmek= +github.com/quic-go/quic-go v0.45.1 h1:tPfeYCk+uZHjmDRwHHQmvHRYL2t44ROTujLeFVBmjCA= +github.com/quic-go/quic-go v0.45.1/go.mod h1:1dLehS7TIR64+vxGR70GDcatWTOtMX2PUtnKsjbTurI= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -76,24 +76,25 @@ go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= -golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= -golang.org/x/mobile v0.0.0-20240520174638-fa72addaaa1b h1:WX7nnnLfCEXg+FmdYZPai2XuP3VqCP1HZVMST0n9DF0= -golang.org/x/mobile v0.0.0-20240520174638-fa72addaaa1b/go.mod h1:EiXZlVfUTaAyySFVJb9rsODuiO+WXu8HrUuySb7nYFw= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +golang.org/x/mobile v0.0.0-20240716161057-1ad2df20a8b6 h1:/VlmIrkuLf2wzPjkZ8imSpckHoW7Y71h66dxbLHSpi8= +golang.org/x/mobile v0.0.0-20240716161057-1ad2df20a8b6/go.mod h1:TCsc78+c4cqb8IKEosz2LwJ6YRNkIjMuAYeHYjchGDE= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= +golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -112,8 +113,9 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -126,16 +128,17 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= -golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= +golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= @@ -143,8 +146,8 @@ golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uI golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA= golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE= golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI= -google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= From 5ea16e63a1574ca2e4e07c52d167350241a6ff1c Mon Sep 17 00:00:00 2001 From: Vasyl Gello Date: Tue, 23 Jul 2024 21:58:11 +0000 Subject: [PATCH 224/293] Implement websocket (ws:// and wss://) links (#1152) ws:// can be listened and dialed wss:// is a convenience link for ws:// that supports dialing to ws:// peer. --------- Signed-off-by: Vasyl Gello Co-authored-by: Neil Alexander --- go.mod | 1 + go.sum | 2 + src/core/link.go | 12 +++++ src/core/link_ws.go | 123 +++++++++++++++++++++++++++++++++++++++++++ src/core/link_wss.go | 43 +++++++++++++++ 5 files changed, 181 insertions(+) create mode 100644 src/core/link_ws.go create mode 100644 src/core/link_wss.go diff --git a/go.mod b/go.mod index d32d1143..5ff4479a 100644 --- a/go.mod +++ b/go.mod @@ -19,6 +19,7 @@ require ( golang.org/x/text v0.16.0 golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 golang.zx2c4.com/wireguard/windows v0.5.3 + nhooyr.io/websocket v1.8.11 ) require ( diff --git a/go.sum b/go.sum index be42c112..1cb8f5b8 100644 --- a/go.sum +++ b/go.sum @@ -154,3 +154,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 h1:TbRPT0HtzFP3Cno1zZo7yPzEEnfu8EjLfl6IU9VfqkQ= gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259/go.mod h1:AVgIgHMwK63XvmAzWG9vLQ41YnVHN0du0tEC46fI7yY= +nhooyr.io/websocket v1.8.11 h1:f/qXNc2/3DpoSZkHt1DQu6rj4zGC8JmkkLkWss0MgN0= +nhooyr.io/websocket v1.8.11/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c= diff --git a/src/core/link.go b/src/core/link.go index b646605c..f45c2cee 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -37,6 +37,8 @@ type links struct { unix *linkUNIX // UNIX interface support socks *linkSOCKS // SOCKS interface support quic *linkQUIC // QUIC interface support + ws *linkWS // WS interface support + wss *linkWSS // WSS interface support // _links can only be modified safely from within the links actor _links map[linkInfo]*link // *link is nil if connection in progress } @@ -97,6 +99,8 @@ func (l *links) init(c *Core) error { l.unix = l.newLinkUNIX() l.socks = l.newLinkSOCKS() l.quic = l.newLinkQUIC() + l.ws = l.newLinkWS() + l.wss = l.newLinkWSS() l._links = make(map[linkInfo]*link) var listeners []ListenAddress @@ -417,6 +421,10 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { protocol = l.unix case "quic": protocol = l.quic + case "ws": + protocol = l.ws + case "wss": + protocol = l.wss default: cancel() return nil, ErrLinkUnrecognisedSchema @@ -545,6 +553,10 @@ func (l *links) connect(ctx context.Context, u *url.URL, info linkInfo, options dialer = l.unix case "quic": dialer = l.quic + case "ws": + dialer = l.ws + case "wss": + dialer = l.wss default: return nil, ErrLinkUnrecognisedSchema } diff --git a/src/core/link_ws.go b/src/core/link_ws.go new file mode 100644 index 00000000..f323b025 --- /dev/null +++ b/src/core/link_ws.go @@ -0,0 +1,123 @@ +package core + +import ( + "context" + "net" + "net/http" + "net/url" + "time" + + "github.com/Arceliar/phony" + "nhooyr.io/websocket" +) + +type linkWS struct { + phony.Inbox + *links +} + +type linkWSConn struct { + net.Conn +} + +type linkWSListener struct { + ch chan *linkWSConn + ctx context.Context + httpServer *http.Server + listener net.Listener +} + +type wsServer struct { + ch chan *linkWSConn + ctx context.Context +} + +func (l *linkWSListener) Accept() (net.Conn, error) { + qs := <-l.ch + if qs == nil { + return nil, context.Canceled + } + return qs, nil +} + +func (l *linkWSListener) Addr() net.Addr { + return l.listener.Addr() +} + +func (l *linkWSListener) Close() error { + if err := l.httpServer.Shutdown(l.ctx); err != nil { + return err + } + return l.listener.Close() +} + +func (s *wsServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if r.URL.Path == "/health" || r.URL.Path == "/healthz" { + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte("OK")) + return + } + + c, err := websocket.Accept(w, r, &websocket.AcceptOptions{ + Subprotocols: []string{"ygg-ws"}, + }) + if err != nil { + return + } + + if c.Subprotocol() != "ygg-ws" { + c.Close(websocket.StatusPolicyViolation, "client must speak the ygg-ws subprotocol") + return + } + + s.ch <- &linkWSConn{ + Conn: websocket.NetConn(s.ctx, c, websocket.MessageBinary), + } +} + +func (l *links) newLinkWS() *linkWS { + lt := &linkWS{ + links: l, + } + return lt +} + +func (l *linkWS) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) { + wsconn, _, err := websocket.Dial(ctx, url.String(), &websocket.DialOptions{ + Subprotocols: []string{"ygg-ws"}, + }) + if err != nil { + return nil, err + } + return &linkWSConn{ + Conn: websocket.NetConn(ctx, wsconn, websocket.MessageBinary), + }, nil +} + +func (l *linkWS) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) { + nl, err := net.Listen("tcp", url.Host) + if err != nil { + return nil, err + } + + ch := make(chan *linkWSConn) + + httpServer := &http.Server{ + Handler: &wsServer{ + ch: ch, + ctx: ctx, + }, + BaseContext: func(_ net.Listener) context.Context { return ctx }, + ReadTimeout: time.Second * 10, + WriteTimeout: time.Second * 10, + } + + lwl := &linkWSListener{ + ch: ch, + ctx: ctx, + httpServer: httpServer, + listener: nl, + } + go lwl.httpServer.Serve(nl) // nolint:errcheck + return lwl, nil +} diff --git a/src/core/link_wss.go b/src/core/link_wss.go new file mode 100644 index 00000000..a9a8df24 --- /dev/null +++ b/src/core/link_wss.go @@ -0,0 +1,43 @@ +package core + +import ( + "context" + "fmt" + "net" + "net/url" + + "github.com/Arceliar/phony" + "nhooyr.io/websocket" +) + +type linkWSS struct { + phony.Inbox + *links +} + +type linkWSSConn struct { + net.Conn +} + +func (l *links) newLinkWSS() *linkWSS { + lwss := &linkWSS{ + links: l, + } + return lwss +} + +func (l *linkWSS) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) { + wsconn, _, err := websocket.Dial(ctx, url.String(), &websocket.DialOptions{ + Subprotocols: []string{"ygg-ws"}, + }) + if err != nil { + return nil, err + } + return &linkWSSConn{ + Conn: websocket.NetConn(ctx, wsconn, websocket.MessageBinary), + }, nil +} + +func (l *linkWSS) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) { + return nil, fmt.Errorf("WSS listener not supported, use WS listener behind reverse proxy instead") +} From 4fbdeb4e3fd77dfc2bef8f9784bf14213d5461e4 Mon Sep 17 00:00:00 2001 From: Revertron <105154+Revertron@users.noreply.github.com> Date: Thu, 25 Jul 2024 14:55:14 +0200 Subject: [PATCH 225/293] Fixed Windows service life-cycle. (#1153) This fix fixes two issues: https://github.com/yggdrasil-network/yggdrasil-go/issues/993 & https://github.com/yggdrasil-network/yggdrasil-go/issues/1098 --- cmd/yggdrasil/main.go | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 82b85cd4..29afdf5d 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -53,13 +53,13 @@ func main() { getpkey := flag.Bool("publickey", false, "use in combination with either -useconf or -useconffile, outputs your public key") loglevel := flag.String("loglevel", "info", "loglevel to enable") flag.Parse() - + + done := make(chan struct{}) + defer close(done) + // Catch interrupts from the operating system to exit gracefully. ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) - // Capture the service being stopped on Windows. - minwinsvc.SetOnExit(cancel) - // Create a new logger that logs output to stdout. var logger *log.Logger switch *logto { @@ -271,6 +271,14 @@ func main() { n.tun.SetupAdminHandlers(n.admin) } } + + //Windows service shutdown + minwinsvc.SetOnExit(func() { + logger.Infof("Shutting down service ...") + cancel() + // Wait for all parts to shutdown properly + <-done + }) // Block until we are told to shut down. <-ctx.Done() From 9950d1225d1284ed8f47da3f027bb1444954c0d2 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 1 Aug 2024 21:53:48 +0100 Subject: [PATCH 226/293] Improve link and handshake errors --- src/core/link.go | 10 ++++++---- src/core/version.go | 23 ++++++++++++++++------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/core/link.go b/src/core/link.go index f45c2cee..ee2ad06f 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -140,7 +140,7 @@ const ErrLinkAlreadyConfigured = linkError("peer is already configured") const ErrLinkNotConfigured = linkError("peer is not configured") const ErrLinkPriorityInvalid = linkError("priority value is invalid") const ErrLinkPinnedKeyInvalid = linkError("pinned public key is invalid") -const ErrLinkPasswordInvalid = linkError("password is invalid") +const ErrLinkPasswordInvalid = linkError("invalid password supplied") const ErrLinkUnrecognisedSchema = linkError("link schema unknown") const ErrLinkMaxBackoffInvalid = linkError("max backoff duration invalid") @@ -363,9 +363,11 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error { _ = lc.Close() phony.Block(l, func() { state._conn = nil - if state._err = err; state._err != nil { - state._errtime = time.Now() + if err == nil { + err = fmt.Errorf("remote side closed the connection") } + state._err = err + state._errtime = time.Now() }) // If the link is persistently configured, back off if needed @@ -647,7 +649,7 @@ func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn, s l.core.log.Infof("Disconnected %s: %s, source %s; error: %s", dir, remoteStr, localStr, err) } - return nil + return err } func urlForLinkInfo(u url.URL) url.URL { diff --git a/src/core/version.go b/src/core/version.go index 28b16430..bb3b9538 100644 --- a/src/core/version.go +++ b/src/core/version.go @@ -8,7 +8,6 @@ import ( "bytes" "crypto/ed25519" "encoding/binary" - "fmt" "io" "golang.org/x/crypto/blake2b" @@ -38,6 +37,16 @@ const ( metaPriority // uint8 ) +type handshakeError string + +func (e handshakeError) Error() string { return string(e) } + +const ErrHandshakeInvalidPreamble = handshakeError("invalid handshake, remote side is not Yggdrasil") +const ErrHandshakeInvalidLength = handshakeError("invalid handshake length, possible version mismatch") +const ErrHandshakeInvalidPassword = handshakeError("invalid password supplied, check your config") +const ErrHandshakeHashFailure = handshakeError("invalid hash length") +const ErrHandshakeIncorrectPassword = handshakeError("password does not match remote side") + // Gets a base metadata with no keys set, but with the correct version numbers. func version_getBaseMetadata() version_metadata { return version_metadata{ @@ -77,7 +86,7 @@ func (m *version_metadata) encode(privateKey ed25519.PrivateKey, password []byte return nil, err } if n != ed25519.PublicKeySize { - return nil, fmt.Errorf("hash writer only wrote %d bytes", n) + return nil, ErrHandshakeHashFailure } hash := hasher.Sum(nil) bs = append(bs, ed25519.Sign(privateKey, hash)...) @@ -94,11 +103,11 @@ func (m *version_metadata) decode(r io.Reader, password []byte) error { } meta := [4]byte{'m', 'e', 't', 'a'} if !bytes.Equal(bh[:4], meta[:]) { - return fmt.Errorf("invalid handshake preamble") + return ErrHandshakeInvalidPreamble } hl := binary.BigEndian.Uint16(bh[4:6]) if hl < ed25519.SignatureSize { - return fmt.Errorf("invalid handshake length") + return ErrHandshakeInvalidLength } bs := make([]byte, hl) if _, err := io.ReadFull(r, bs); err != nil { @@ -132,15 +141,15 @@ func (m *version_metadata) decode(r io.Reader, password []byte) error { hasher, err := blake2b.New512(password) if err != nil { - return fmt.Errorf("invalid password supplied") + return ErrHandshakeInvalidPassword } n, err := hasher.Write(m.publicKey) if err != nil || n != ed25519.PublicKeySize { - return fmt.Errorf("failed to generate hash") + return ErrHandshakeHashFailure } hash := hasher.Sum(nil) if !ed25519.Verify(m.publicKey, hash, sig) { - return fmt.Errorf("password is incorrect") + return ErrHandshakeIncorrectPassword } return nil } From edf179ed26df30b30665f703b366f2688fefde92 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 5 Aug 2024 19:18:38 +0100 Subject: [PATCH 227/293] Yggdrasil 0.5.7 --- CHANGELOG.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b339294c..323206d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,25 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - in case of vulnerabilities. --> +## [0.5.7] - 2024-08-05 + +### Added + +* WebSocket support for peerings, by using the new `ws://` scheme in `Listen` and `Peers` + * Additionally, the `wss://` scheme can be used to connect to a WebSocket peer behind a HTTPS reverse proxy + +### Changed + +* On Linux, the TUN adapter now uses vectorised reads/writes where possible, which should reduce the amount of CPU time spent on syscalls and potentially improve throughput +* Link error handling has been improved and various link error messages have been rewritten to be clearer +* Upgrade dependencies + +### Fixed + +* Multiple multicast connections to the same remote machine should now work correctly + * You may get two connections in some cases, one inbound and one outbound, this is known and will not cause problems +* Running as a Windows service should be more reliable with service startup and shutdown bugs fixed + ## [0.5.6] - 2024-05-30 * Go 1.21 is now required to build Yggdrasil From 5e5de3a34314d9ac3f549a93e3e30a5a9ce66966 Mon Sep 17 00:00:00 2001 From: Revertron <105154+Revertron@users.noreply.github.com> Date: Tue, 6 Aug 2024 11:28:15 +0200 Subject: [PATCH 228/293] Fixed wait for TUN to come up (#1157) So, the function waiting for TUN to come up never succeeds: ``` func waitForTUNUp(ch <-chan wgtun.Event) bool { t := time.After(time.Second * 5) for { select { case ev := <-ch: if ev == wgtun.EventUp { return true } case <-t: return false } } } ``` I've tried the sleep for one second, and it works flawlessly on several PCs. Another point - sometimes, if the service stop abruptly (in case of some errors) there is an old hidden device in the system, that we need to uninstall, and then create new. --- src/tun/tun_windows.go | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/tun/tun_windows.go b/src/tun/tun_windows.go index 7bcdb7ac..a2861894 100644 --- a/src/tun/tun_windows.go +++ b/src/tun/tun_windows.go @@ -8,10 +8,12 @@ import ( "fmt" "log" "net/netip" + "time" "github.com/yggdrasil-network/yggdrasil-go/src/config" "golang.org/x/sys/windows" + "golang.zx2c4.com/wintun" wgtun "golang.zx2c4.com/wireguard/tun" "golang.zx2c4.com/wireguard/windows/elevate" "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg" @@ -31,14 +33,23 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error { if guid, err = windows.GUIDFromString("{8f59971a-7872-4aa6-b2eb-061fc4e9d0a7}"); err != nil { return err } - if iface, err = wgtun.CreateTUNWithRequestedGUID(ifname, &guid, int(mtu)); err != nil { - return err - } - if !waitForTUNUp(iface.Events()) { - return fmt.Errorf("TUN did not come up in time") + iface, err = wgtun.CreateTUNWithRequestedGUID(ifname, &guid, int(mtu)) + if err != nil { + // Very rare condition, it will purge the old device and create new + tun.log.Printf("Error creating TUN: '%s'", err) + wintun.Uninstall() + time.Sleep(3 * time.Second) + tun.log.Printf("Trying again") + iface, err = wgtun.CreateTUNWithRequestedGUID(ifname, &guid, int(mtu)) + if err != nil { + return err + } } + tun.log.Printf("Waiting for TUN to come up") + time.Sleep(1 * time.Second) tun.iface = iface if addr != "" { + tun.log.Printf("Setting up address") if err = tun.setupAddress(addr); err != nil { tun.log.Errorln("Failed to set up TUN address:", err) return err @@ -51,6 +62,7 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error { if mtu, err := iface.MTU(); err == nil { tun.mtu = uint64(mtu) } + tun.log.Printf("TUN is set up successfully") return nil }) } From 63cd7575258a68d7ceda425c80766c4dd6e9b42d Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 7 Aug 2024 19:52:19 +0100 Subject: [PATCH 229/293] Remove `waitForTUNUp` from TUN Causes issues such as #1156. --- src/tun/tun.go | 15 --------------- src/tun/tun_bsd.go | 3 --- src/tun/tun_darwin.go | 3 --- src/tun/tun_linux.go | 3 --- src/tun/tun_other.go | 3 --- 5 files changed, 27 deletions(-) diff --git a/src/tun/tun.go b/src/tun/tun.go index e6795c64..e7ba3a0f 100644 --- a/src/tun/tun.go +++ b/src/tun/tun.go @@ -11,7 +11,6 @@ import ( "io" "net" "sync" - "time" "github.com/Arceliar/phony" wgtun "golang.zx2c4.com/wireguard/tun" @@ -65,20 +64,6 @@ func getSupportedMTU(mtu uint64) uint64 { return mtu } -func waitForTUNUp(ch <-chan wgtun.Event) bool { - t := time.After(time.Second * 5) - for { - select { - case ev := <-ch: - if ev == wgtun.EventUp { - return true - } - case <-t: - return false - } - } -} - // Name returns the name of the adapter, e.g. "tun0". On Windows, this may // return a canonical adapter name instead. func (tun *TunAdapter) Name() string { diff --git a/src/tun/tun_bsd.go b/src/tun/tun_bsd.go index 7f26260c..da5b3297 100644 --- a/src/tun/tun_bsd.go +++ b/src/tun/tun_bsd.go @@ -80,9 +80,6 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error { if err != nil { return fmt.Errorf("failed to create TUN: %w", err) } - if !waitForTUNUp(iface.Events()) { - return fmt.Errorf("TUN did not come up in time") - } tun.iface = iface if mtu, err := iface.MTU(); err == nil { tun.mtu = getSupportedMTU(uint64(mtu)) diff --git a/src/tun/tun_darwin.go b/src/tun/tun_darwin.go index a9d734bb..aa30b7f3 100644 --- a/src/tun/tun_darwin.go +++ b/src/tun/tun_darwin.go @@ -27,9 +27,6 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error { if err != nil { return fmt.Errorf("failed to create TUN: %w", err) } - if !waitForTUNUp(iface.Events()) { - return fmt.Errorf("TUN did not come up in time") - } tun.iface = iface if m, err := iface.MTU(); err == nil { tun.mtu = getSupportedMTU(uint64(m)) diff --git a/src/tun/tun_linux.go b/src/tun/tun_linux.go index c98cdd73..98d63db4 100644 --- a/src/tun/tun_linux.go +++ b/src/tun/tun_linux.go @@ -21,9 +21,6 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error { if err != nil { return fmt.Errorf("failed to create TUN: %w", err) } - if !waitForTUNUp(iface.Events()) { - return fmt.Errorf("TUN did not come up in time") - } tun.iface = iface if mtu, err := iface.MTU(); err == nil { tun.mtu = getSupportedMTU(uint64(mtu)) diff --git a/src/tun/tun_other.go b/src/tun/tun_other.go index fc940818..0ddd0c1e 100644 --- a/src/tun/tun_other.go +++ b/src/tun/tun_other.go @@ -18,9 +18,6 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error { if err != nil { return fmt.Errorf("failed to create TUN: %w", err) } - if !waitForTUNUp(iface.Events()) { - return fmt.Errorf("TUN did not come up in time") - } tun.iface = iface if mtu, err := iface.MTU(); err == nil { tun.mtu = getSupportedMTU(uint64(mtu)) From af9ff34995aa8da5670227fc1a8981051ceaec65 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 7 Aug 2024 19:55:10 +0100 Subject: [PATCH 230/293] Fix macOS build --- src/tun/tun_darwin.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/tun/tun_darwin.go b/src/tun/tun_darwin.go index aa30b7f3..deeb115e 100644 --- a/src/tun/tun_darwin.go +++ b/src/tun/tun_darwin.go @@ -55,9 +55,6 @@ func (tun *TunAdapter) setupFD(fd int32, addr string, mtu uint64) error { unix.Close(dfd) return fmt.Errorf("failed to create TUN from FD: %w", err) } - if !waitForTUNUp(iface.Events()) { - return fmt.Errorf("TUN did not come up in time") - } tun.iface = iface if m, err := iface.MTU(); err == nil { tun.mtu = getSupportedMTU(uint64(m)) From ef989bef6362ad42eec9376a512a5d7be2b7e9e7 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 11 Aug 2024 10:41:58 +0100 Subject: [PATCH 231/293] Multicast module state tweaks --- src/multicast/multicast.go | 26 +++++++++++++++----------- src/multicast/multicast_darwin_cgo.go | 2 +- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/multicast/multicast.go b/src/multicast/multicast.go index bf10370f..0fe72dc7 100644 --- a/src/multicast/multicast.go +++ b/src/multicast/multicast.go @@ -9,6 +9,7 @@ import ( "math/rand" "net" "net/url" + "sync/atomic" "time" "github.com/Arceliar/phony" @@ -28,7 +29,7 @@ type Multicast struct { core *core.Core log *log.Logger sock *ipv6.PacketConn - _isOpen bool + running atomic.Bool _listeners map[string]*listenerInfo _interfaces map[string]*interfaceInfo _timer *time.Timer @@ -79,7 +80,7 @@ func New(core *core.Core, log *log.Logger, opts ...SetupOption) (*Multicast, err } func (m *Multicast) _start() error { - if m._isOpen { + if !m.running.CompareAndSwap(false, true) { return fmt.Errorf("multicast module is already started") } var anyEnabled bool @@ -87,12 +88,14 @@ func (m *Multicast) _start() error { anyEnabled = anyEnabled || intf.Beacon || intf.Listen } if !anyEnabled { + m.running.Store(false) return nil } m.log.Debugln("Starting multicast module") defer m.log.Debugln("Started multicast module") addr, err := net.ResolveUDPAddr("udp", string(m.config._groupAddr)) if err != nil { + m.running.Store(false) return err } listenString := fmt.Sprintf("[::]:%v", addr.Port) @@ -101,6 +104,7 @@ func (m *Multicast) _start() error { } conn, err := lc.ListenPacket(context.Background(), "udp6", listenString) if err != nil { + m.running.Store(false) return err } m.sock = ipv6.NewPacketConn(conn) @@ -108,7 +112,6 @@ func (m *Multicast) _start() error { // Windows can't set this flag, so we need to handle it in other ways } - m._isOpen = true go m.listen() m.Act(nil, m._multicastStarted) m.Act(nil, m._announce) @@ -118,11 +121,7 @@ func (m *Multicast) _start() error { // IsStarted returns true if the module has been started. func (m *Multicast) IsStarted() bool { - var isOpen bool - phony.Block(m, func() { - isOpen = m._isOpen - }) - return isOpen + return m.running.Load() } // Stop stops the multicast module. @@ -136,8 +135,10 @@ func (m *Multicast) Stop() error { } func (m *Multicast) _stop() error { + if !m.running.CompareAndSwap(true, false) { + return nil + } m.log.Infoln("Stopping multicast module") - m._isOpen = false if m.sock != nil { m.sock.Close() } @@ -233,7 +234,7 @@ func (m *Multicast) AnnounceNow() { } func (m *Multicast) _announce() { - if !m._isOpen { + if !m.running.Load() { return } m._updateInterfaces() @@ -250,7 +251,7 @@ func (m *Multicast) _announce() { for name, info := range m._listeners { // Prepare our stop function! stop := func() { - info.listener.Close() + info.listener.Cancel() delete(m._listeners, name) m.log.Debugln("No longer multicasting on", name) } @@ -376,6 +377,9 @@ func (m *Multicast) listen() { bs := make([]byte, 2048) hb := make([]byte, 0, blake2b.Size) // Reused to reduce hash allocations for { + if !m.running.Load() { + return + } n, rcm, fromAddr, err := m.sock.ReadFrom(bs) if err != nil { if !m.IsStarted() { diff --git a/src/multicast/multicast_darwin_cgo.go b/src/multicast/multicast_darwin_cgo.go index 5c2af7ab..16266381 100644 --- a/src/multicast/multicast_darwin_cgo.go +++ b/src/multicast/multicast_darwin_cgo.go @@ -31,7 +31,7 @@ import ( ) func (m *Multicast) _multicastStarted() { - if !m._isOpen { + if !m.running.Load() { return } C.StopAWDLBrowsing() From b1283e15f63dc7a553e0381efed6abdfa6006819 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 11 Aug 2024 10:42:25 +0100 Subject: [PATCH 232/293] Link state tracking tweaks and improved shutdown --- src/core/link.go | 66 +++++++++++++++++++------------------------ src/core/link_tcp.go | 2 -- src/core/link_tls.go | 10 +++---- src/core/link_unix.go | 6 ++-- src/core/link_ws.go | 6 +++- 5 files changed, 40 insertions(+), 50 deletions(-) diff --git a/src/core/link.go b/src/core/link.go index ee2ad06f..1ead4e32 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "encoding/hex" + "errors" "fmt" "io" "net" @@ -40,7 +41,8 @@ type links struct { ws *linkWS // WS interface support wss *linkWSS // WSS interface support // _links can only be modified safely from within the links actor - _links map[linkInfo]*link // *link is nil if connection in progress + _links map[linkInfo]*link // *link is nil if connection in progress + _listeners map[*Listener]context.CancelFunc } type linkProtocol interface { @@ -85,13 +87,6 @@ func (l *Listener) Addr() net.Addr { return l.listener.Addr() } -func (l *Listener) Close() error { - l.Cancel() - err := l.listener.Close() - <-l.ctx.Done() - return err -} - func (l *links) init(c *Core) error { l.core = c l.tcp = l.newLinkTCP() @@ -102,32 +97,18 @@ func (l *links) init(c *Core) error { l.ws = l.newLinkWS() l.wss = l.newLinkWSS() l._links = make(map[linkInfo]*link) - - var listeners []ListenAddress - phony.Block(c, func() { - listeners = make([]ListenAddress, 0, len(c.config._listeners)) - for listener := range c.config._listeners { - listeners = append(listeners, listener) - } - }) + l._listeners = make(map[*Listener]context.CancelFunc) return nil } func (l *links) shutdown() { - phony.Block(l.tcp, func() { - for l := range l.tcp._listeners { - _ = l.Close() + phony.Block(l, func() { + for listener := range l._listeners { + _ = listener.listener.Close() } - }) - phony.Block(l.tls, func() { - for l := range l.tls._listeners { - _ = l.Close() - } - }) - phony.Block(l.unix, func() { - for l := range l.unix._listeners { - _ = l.Close() + for _, link := range l._links { + _ = link._conn.Close() } }) } @@ -457,11 +438,18 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { options.password = []byte(p) } + phony.Block(l, func() { + l._listeners[li] = cancel + }) + go func() { - l.core.log.Infof("%s listener started on %s", strings.ToUpper(u.Scheme), listener.Addr()) - defer l.core.log.Infof("%s listener stopped on %s", strings.ToUpper(u.Scheme), listener.Addr()) + l.core.log.Infof("%s listener started on %s", strings.ToUpper(u.Scheme), li.listener.Addr()) + defer l.core.log.Infof("%s listener stopped on %s", strings.ToUpper(u.Scheme), li.listener.Addr()) + defer phony.Block(l, func() { + delete(l._listeners, li) + }) for { - conn, err := listener.Accept() + conn, err := li.listener.Accept() if err != nil { return } @@ -517,13 +505,22 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { // Store the state of the link so that it can be queried later. l._links[info] = state }) + defer phony.Block(l, func() { + if l._links[info] == state { + delete(l._links, info) + } + }) if lc == nil { return } // Give the connection to the handler. The handler will block // for the lifetime of the connection. - if err = l.handler(linkTypeIncoming, options, lc, nil); err != nil && err != io.EOF { + switch err = l.handler(linkTypeIncoming, options, lc, nil); { + case err == nil: + case errors.Is(err, io.EOF): + case errors.Is(err, net.ErrClosed): + default: l.core.log.Debugf("Link %s error: %s\n", u.Host, err) } @@ -531,11 +528,6 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { // try to close the underlying socket just in case and then // drop the link state. _ = lc.Close() - phony.Block(l, func() { - if l._links[info] == state { - delete(l._links, info) - } - }) }(conn) } }() diff --git a/src/core/link_tcp.go b/src/core/link_tcp.go index f595aeb9..4ee50941 100644 --- a/src/core/link_tcp.go +++ b/src/core/link_tcp.go @@ -15,7 +15,6 @@ type linkTCP struct { phony.Inbox *links listenconfig *net.ListenConfig - _listeners map[*Listener]context.CancelFunc } func (l *links) newLinkTCP() *linkTCP { @@ -24,7 +23,6 @@ func (l *links) newLinkTCP() *linkTCP { listenconfig: &net.ListenConfig{ KeepAlive: -1, }, - _listeners: map[*Listener]context.CancelFunc{}, } lt.listenconfig.Control = lt.tcpContext return lt diff --git a/src/core/link_tls.go b/src/core/link_tls.go index a93227f6..d51d0ce5 100644 --- a/src/core/link_tls.go +++ b/src/core/link_tls.go @@ -13,10 +13,9 @@ import ( type linkTLS struct { phony.Inbox *links - tcp *linkTCP - listener *net.ListenConfig - config *tls.Config - _listeners map[*Listener]context.CancelFunc + tcp *linkTCP + listener *net.ListenConfig + config *tls.Config } func (l *links) newLinkTLS(tcp *linkTCP) *linkTLS { @@ -27,8 +26,7 @@ func (l *links) newLinkTLS(tcp *linkTCP) *linkTLS { Control: tcp.tcpContext, KeepAlive: -1, }, - config: l.core.config.tls.Clone(), - _listeners: map[*Listener]context.CancelFunc{}, + config: l.core.config.tls.Clone(), } return lt } diff --git a/src/core/link_unix.go b/src/core/link_unix.go index 8dde8946..ddbfa0ad 100644 --- a/src/core/link_unix.go +++ b/src/core/link_unix.go @@ -12,9 +12,8 @@ import ( type linkUNIX struct { phony.Inbox *links - dialer *net.Dialer - listener *net.ListenConfig - _listeners map[*Listener]context.CancelFunc + dialer *net.Dialer + listener *net.ListenConfig } func (l *links) newLinkUNIX() *linkUNIX { @@ -27,7 +26,6 @@ func (l *links) newLinkUNIX() *linkUNIX { listener: &net.ListenConfig{ KeepAlive: -1, }, - _listeners: map[*Listener]context.CancelFunc{}, } return lt } diff --git a/src/core/link_ws.go b/src/core/link_ws.go index f323b025..7a7d66f7 100644 --- a/src/core/link_ws.go +++ b/src/core/link_ws.go @@ -14,6 +14,7 @@ import ( type linkWS struct { phony.Inbox *links + listenconfig *net.ListenConfig } type linkWSConn struct { @@ -78,6 +79,9 @@ func (s *wsServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (l *links) newLinkWS() *linkWS { lt := &linkWS{ links: l, + listenconfig: &net.ListenConfig{ + KeepAlive: -1, + }, } return lt } @@ -95,7 +99,7 @@ func (l *linkWS) dial(ctx context.Context, url *url.URL, info linkInfo, options } func (l *linkWS) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) { - nl, err := net.Listen("tcp", url.Host) + nl, err := l.listenconfig.Listen(ctx, "tcp", url.Host) if err != nil { return nil, err } From 340cedbe145ec40de2648dab658795ec21e11992 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 12 Aug 2024 19:17:40 +0100 Subject: [PATCH 233/293] Yggdrasil 0.5.8 --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 323206d9..34c7fe30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - in case of vulnerabilities. --> +## [0.5.8] - 2024-08-12 + +### Fixed + +* A bug which caused startup problems on Windows and FreeBSD should be fixed +* Resolved some minor link state and listener management bugs during shutdown + ## [0.5.7] - 2024-08-05 ### Added From 947b6ad7aa93eb2174bf16ddac844c7afaf2677d Mon Sep 17 00:00:00 2001 From: Sergey Bobrenok Date: Fri, 16 Aug 2024 20:28:57 +0300 Subject: [PATCH 234/293] Restore local peer discovery mechanism on Android 11+ (#1158) This solution is bases on https://github.com/wlynxg/anet project. `github.com/wlynxg/anet` is a partial alternative implementation of the `golang.org/x/net` module. The goal of `anet` module is to provide workarounds of the issues https://github.com/golang/go/issues/40569 and https://github.com/golang/go/issues/68082 on Android 11+. Tested on AOSP 13. Resolves: #1149 --- go.mod | 1 + go.sum | 2 ++ src/multicast/multicast.go | 10 +++++++--- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 5ff4479a..52b699d8 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/kardianos/minwinsvc v1.0.2 github.com/quic-go/quic-go v0.45.1 github.com/vishvananda/netlink v1.1.0 + github.com/wlynxg/anet v0.0.4-0.20240806025826-e684438fc7c6 golang.org/x/crypto v0.25.0 golang.org/x/mobile v0.0.0-20240716161057-1ad2df20a8b6 golang.org/x/net v0.27.0 diff --git a/go.sum b/go.sum index 1cb8f5b8..a3c2888e 100644 --- a/go.sum +++ b/go.sum @@ -70,6 +70,8 @@ github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYp github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f h1:p4VB7kIXpOQvVn1ZaTIVp+3vuYAXFe3OJEvjbUYJLaA= github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= +github.com/wlynxg/anet v0.0.4-0.20240806025826-e684438fc7c6 h1:c/wkXIJvpg2oot7iFqPESTBAO9UvhWTBnW97y9aPgyU= +github.com/wlynxg/anet v0.0.4-0.20240806025826-e684438fc7c6/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= diff --git a/src/multicast/multicast.go b/src/multicast/multicast.go index 0fe72dc7..902d7729 100644 --- a/src/multicast/multicast.go +++ b/src/multicast/multicast.go @@ -14,6 +14,7 @@ import ( "github.com/Arceliar/phony" "github.com/gologme/log" + "github.com/wlynxg/anet" "github.com/yggdrasil-network/yggdrasil-go/src/core" "golang.org/x/crypto/blake2b" @@ -148,7 +149,8 @@ func (m *Multicast) _stop() error { func (m *Multicast) _updateInterfaces() { interfaces := m._getAllowedInterfaces() for name, info := range interfaces { - addrs, err := info.iface.Addrs() + // 'anet' package is used here to avoid https://github.com/golang/go/issues/40569 + addrs, err := anet.InterfaceAddrsByInterface(&info.iface) if err != nil { m.log.Warnf("Failed up get addresses for interface %s: %s", name, err) delete(interfaces, name) @@ -156,6 +158,7 @@ func (m *Multicast) _updateInterfaces() { } info.addrs = addrs interfaces[name] = info + m.log.Debugf("Discovered addresses for interface %s: %s", name, addrs) } m._interfaces = interfaces } @@ -174,10 +177,11 @@ func (m *Multicast) Interfaces() map[string]net.Interface { func (m *Multicast) _getAllowedInterfaces() map[string]*interfaceInfo { interfaces := make(map[string]*interfaceInfo) // Ask the system for network interfaces - allifaces, err := net.Interfaces() + // 'anet' package is used here to avoid https://github.com/golang/go/issues/40569 + allifaces, err := anet.Interfaces() if err != nil { // Don't panic, since this may be from e.g. too many open files (from too much connection spam) - // TODO? log something + m.log.Debugf("Failed to get interfaces: %s", err) return nil } // Work out which interfaces to announce on From c4b29b735cff4448e0343fe0fbfdb4e668d13660 Mon Sep 17 00:00:00 2001 From: Neil Date: Sat, 21 Sep 2024 23:05:23 +0100 Subject: [PATCH 235/293] Link costing based on average RTT (#1171) This PR updates Ironwood to include the new RTT-based link costing and updates `yggdrasilctl` to report the cost in `getPeers`. Co-authored-by: Neil Alexander --- .github/workflows/ci.yml | 12 ++++++------ .golangci.yml | 5 +++-- cmd/yggdrasilctl/main.go | 3 ++- contrib/mobile/mobile.go | 2 -- go.mod | 5 ++--- go.sum | 6 ++---- src/admin/getpeers.go | 2 ++ src/core/api.go | 2 ++ 8 files changed, 19 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a3f34789..57a0d2a7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,10 +17,10 @@ jobs: steps: - uses: actions/setup-go@v5 with: - go-version: 1.21 + go-version: stable - uses: actions/checkout@v4 - name: golangci-lint - uses: golangci/golangci-lint-action@v3 + uses: golangci/golangci-lint-action@v6 with: args: --issues-exit-code=1 @@ -51,7 +51,7 @@ jobs: strategy: fail-fast: false matrix: - goversion: ["1.21", "1.22"] + goversion: ["1.21", "1.22", "1.23"] name: Build & Test (Linux, Go ${{ matrix.goversion }}) needs: [lint] @@ -75,7 +75,7 @@ jobs: strategy: fail-fast: false matrix: - goversion: ["1.21", "1.22"] + goversion: ["1.21", "1.22", "1.23"] name: Build & Test (Windows, Go ${{ matrix.goversion }}) needs: [lint] @@ -99,7 +99,7 @@ jobs: strategy: fail-fast: false matrix: - goversion: ["1.21", "1.22"] + goversion: ["1.21", "1.22", "1.23"] name: Build & Test (macOS, Go ${{ matrix.goversion }}) needs: [lint] @@ -123,7 +123,7 @@ jobs: strategy: fail-fast: false matrix: - goversion: ["1.21", "1.22"] + goversion: ["1.21", "1.22", "1.23"] goos: - freebsd - openbsd diff --git a/.golangci.yml b/.golangci.yml index c35edee4..836af618 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -2,9 +2,10 @@ run: build-tags: - lint issues-exit-code: 0 # TODO: change this to 1 when we want it to fail builds - skip-dirs: +issues: + exclude-dirs: - contrib/ - misc/ linters: disable: - - gocyclo \ No newline at end of file + - gocyclo diff --git a/cmd/yggdrasilctl/main.go b/cmd/yggdrasilctl/main.go index 2a1d70b1..8a30f438 100644 --- a/cmd/yggdrasilctl/main.go +++ b/cmd/yggdrasilctl/main.go @@ -174,7 +174,7 @@ func run() int { if err := json.Unmarshal(recv.Response, &resp); err != nil { panic(err) } - table.SetHeader([]string{"URI", "State", "Dir", "IP Address", "Uptime", "RTT", "RX", "TX", "Pr", "Last Error"}) + table.SetHeader([]string{"URI", "State", "Dir", "IP Address", "Uptime", "RTT", "RX", "TX", "Pr", "Cost", "Last Error"}) for _, peer := range resp.Peers { state, lasterr, dir, rtt := "Up", "-", "Out", "-" if !peer.Up { @@ -200,6 +200,7 @@ func run() int { peer.RXBytes.String(), peer.TXBytes.String(), fmt.Sprintf("%d", peer.Priority), + fmt.Sprintf("%d", peer.Cost), lasterr, }) } diff --git a/contrib/mobile/mobile.go b/contrib/mobile/mobile.go index 82e73485..06f48027 100644 --- a/contrib/mobile/mobile.go +++ b/contrib/mobile/mobile.go @@ -15,8 +15,6 @@ import ( "github.com/yggdrasil-network/yggdrasil-go/src/multicast" "github.com/yggdrasil-network/yggdrasil-go/src/tun" "github.com/yggdrasil-network/yggdrasil-go/src/version" - - _ "golang.org/x/mobile/bind" ) // Yggdrasil mobile package is meant to "plug the gap" for mobile support, as diff --git a/go.mod b/go.mod index 52b699d8..e10feb95 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/yggdrasil-network/yggdrasil-go go 1.21 require ( - github.com/Arceliar/ironwood v0.0.0-20240529054413-b8e59574e2b2 + github.com/Arceliar/ironwood v0.0.0-20240921214443-277f642d5db3 github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d github.com/cheggaaa/pb/v3 v3.1.5 github.com/gologme/log v1.3.0 @@ -14,10 +14,10 @@ require ( github.com/vishvananda/netlink v1.1.0 github.com/wlynxg/anet v0.0.4-0.20240806025826-e684438fc7c6 golang.org/x/crypto v0.25.0 - golang.org/x/mobile v0.0.0-20240716161057-1ad2df20a8b6 golang.org/x/net v0.27.0 golang.org/x/sys v0.22.0 golang.org/x/text v0.16.0 + golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 golang.zx2c4.com/wireguard/windows v0.5.3 nhooyr.io/websocket v1.8.11 @@ -36,7 +36,6 @@ require ( golang.org/x/mod v0.19.0 // indirect golang.org/x/sync v0.7.0 // indirect golang.org/x/tools v0.23.0 // indirect - golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect ) require ( diff --git a/go.sum b/go.sum index a3c2888e..5ddf4169 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Arceliar/ironwood v0.0.0-20240529054413-b8e59574e2b2 h1:SBdYBKeXYUUFef5wi2CMhYmXFVGiYaRpTvbki0Bu+JQ= -github.com/Arceliar/ironwood v0.0.0-20240529054413-b8e59574e2b2/go.mod h1:6WP4799FX0OuWdENGQAh+0RXp9FLh0y7NZ7tM9cJyXk= +github.com/Arceliar/ironwood v0.0.0-20240921214443-277f642d5db3 h1:OJ49qTfdw5MNkpnRraNEsVQbHahSvShh8Z9WYpZrYa0= +github.com/Arceliar/ironwood v0.0.0-20240921214443-277f642d5db3/go.mod h1:6WP4799FX0OuWdENGQAh+0RXp9FLh0y7NZ7tM9cJyXk= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= @@ -83,8 +83,6 @@ golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= -golang.org/x/mobile v0.0.0-20240716161057-1ad2df20a8b6 h1:/VlmIrkuLf2wzPjkZ8imSpckHoW7Y71h66dxbLHSpi8= -golang.org/x/mobile v0.0.0-20240716161057-1ad2df20a8b6/go.mod h1:TCsc78+c4cqb8IKEosz2LwJ6YRNkIjMuAYeHYjchGDE= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= diff --git a/src/admin/getpeers.go b/src/admin/getpeers.go index e44428c3..48234fb1 100644 --- a/src/admin/getpeers.go +++ b/src/admin/getpeers.go @@ -24,6 +24,7 @@ type PeerEntry struct { PublicKey string `json:"key"` Port uint64 `json:"port"` Priority uint64 `json:"priority"` + Cost uint64 `json:"cost"` RXBytes DataUnit `json:"bytes_recvd,omitempty"` TXBytes DataUnit `json:"bytes_sent,omitempty"` Uptime float64 `json:"uptime,omitempty"` @@ -41,6 +42,7 @@ func (a *AdminSocket) getPeersHandler(_ *GetPeersRequest, res *GetPeersResponse) Up: p.Up, Inbound: p.Inbound, Priority: uint64(p.Priority), // can't be uint8 thanks to gobind + Cost: p.Cost, URI: p.URI, RXBytes: DataUnit(p.RXBytes), TXBytes: DataUnit(p.TXBytes), diff --git a/src/core/api.go b/src/core/api.go index 875d7bf2..c236b1b5 100644 --- a/src/core/api.go +++ b/src/core/api.go @@ -30,6 +30,7 @@ type PeerInfo struct { Coords []uint64 Port uint64 Priority uint8 + Cost uint64 RXBytes uint64 TXBytes uint64 Uptime time.Duration @@ -94,6 +95,7 @@ func (c *Core) GetPeers() []PeerInfo { peerinfo.Port = p.Port peerinfo.Priority = p.Priority peerinfo.Latency = p.Latency + peerinfo.Cost = p.Cost } peers = append(peers, peerinfo) } From 34f087de1c24e39fce7201bfe8d238f7e5bc85cf Mon Sep 17 00:00:00 2001 From: cathugger Date: Sun, 22 Sep 2024 15:46:54 +0000 Subject: [PATCH 236/293] argument to change uid/gid (#927) different from https://github.com/yggdrasil-network/yggdrasil-go/pull/817 in that it can resolve user names, automatically use user's primary gid & allows specifying gid in the same argument, with `:` eg `username:groupname`. feel free to criticize & suggest different argument name & description because i didn't put much of thought to that. --------- Co-authored-by: Neil Co-authored-by: VNAT Co-authored-by: Neil Alexander --- cmd/yggdrasil/chuser_other.go | 10 ++++ cmd/yggdrasil/chuser_unix.go | 87 +++++++++++++++++++++++++++++++++++ cmd/yggdrasil/main.go | 9 ++++ 3 files changed, 106 insertions(+) create mode 100644 cmd/yggdrasil/chuser_other.go create mode 100644 cmd/yggdrasil/chuser_unix.go diff --git a/cmd/yggdrasil/chuser_other.go b/cmd/yggdrasil/chuser_other.go new file mode 100644 index 00000000..702f3715 --- /dev/null +++ b/cmd/yggdrasil/chuser_other.go @@ -0,0 +1,10 @@ +//go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris +// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris + +package main + +import "errors" + +func chuser(user string) error { + return errors.New("setting uid/gid is not supported on this platform") +} diff --git a/cmd/yggdrasil/chuser_unix.go b/cmd/yggdrasil/chuser_unix.go new file mode 100644 index 00000000..6e802c69 --- /dev/null +++ b/cmd/yggdrasil/chuser_unix.go @@ -0,0 +1,87 @@ +//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris + +package main + +import ( + "errors" + "fmt" + "math" + osuser "os/user" + "strconv" + "strings" + "syscall" +) + +func chuser(user string) error { + group := "" + if i := strings.IndexByte(user, ':'); i >= 0 { + user, group = user[:i], user[i+1:] + } + + u := (*osuser.User)(nil) + g := (*osuser.Group)(nil) + + if user != "" { + if _, err := strconv.ParseUint(user, 10, 32); err == nil { + u, err = osuser.LookupId(user) + if err != nil { + return fmt.Errorf("failed to lookup user by id %q: %v", user, err) + } + } else { + u, err = osuser.Lookup(user) + if err != nil { + return fmt.Errorf("failed to lookup user by name %q: %v", user, err) + } + } + } + if group != "" { + if _, err := strconv.ParseUint(group, 10, 32); err == nil { + g, err = osuser.LookupGroupId(group) + if err != nil { + return fmt.Errorf("failed to lookup group by id %q: %v", user, err) + } + } else { + g, err = osuser.LookupGroup(group) + if err != nil { + return fmt.Errorf("failed to lookup group by name %q: %v", user, err) + } + } + } + + if g != nil { + gid, _ := strconv.ParseUint(g.Gid, 10, 32) + var err error + if gid < math.MaxInt { + err = syscall.Setgid(int(gid)) + } else { + err = errors.New("gid too big") + } + + if err != nil { + return fmt.Errorf("failed to setgid %d: %v", gid, err) + } + } else if u != nil { + gid, _ := strconv.ParseUint(u.Gid, 10, 32) + err := syscall.Setgid(int(uint32(gid))) + if err != nil { + return fmt.Errorf("failed to setgid %d: %v", gid, err) + } + } + + if u != nil { + uid, _ := strconv.ParseUint(u.Uid, 10, 32) + var err error + if uid < math.MaxInt { + err = syscall.Setuid(int(uid)) + } else { + err = errors.New("uid too big") + } + + if err != nil { + return fmt.Errorf("failed to setuid %d: %v", uid, err) + } + } + + return nil +} diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 29afdf5d..3a00b3fc 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -52,6 +52,7 @@ func main() { getsnet := flag.Bool("subnet", false, "use in combination with either -useconf or -useconffile, outputs your IPv6 subnet") getpkey := flag.Bool("publickey", false, "use in combination with either -useconf or -useconffile, outputs your public key") loglevel := flag.String("loglevel", "info", "loglevel to enable") + chuserto := flag.String("user", "", "user (and, optionally, group) to set UID/GID to") flag.Parse() done := make(chan struct{}) @@ -280,6 +281,14 @@ func main() { <-done }) + // Change user if requested + if *chuserto != "" { + err = chuser(*chuserto) + if err != nil { + panic(err) + } + } + // Block until we are told to shut down. <-ctx.Done() From 5461bb380e9519a724b3c6e45b5ac913ab7528c3 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 22 Sep 2024 16:51:04 +0100 Subject: [PATCH 237/293] Update dependencies --- go.mod | 20 ++++++++++---------- go.sum | 45 ++++++++++++++++++++++----------------------- 2 files changed, 32 insertions(+), 33 deletions(-) diff --git a/go.mod b/go.mod index e10feb95..4fb6f6c9 100644 --- a/go.mod +++ b/go.mod @@ -10,17 +10,17 @@ require ( github.com/hashicorp/go-syslog v1.0.0 github.com/hjson/hjson-go/v4 v4.4.0 github.com/kardianos/minwinsvc v1.0.2 - github.com/quic-go/quic-go v0.45.1 - github.com/vishvananda/netlink v1.1.0 - github.com/wlynxg/anet v0.0.4-0.20240806025826-e684438fc7c6 - golang.org/x/crypto v0.25.0 - golang.org/x/net v0.27.0 - golang.org/x/sys v0.22.0 - golang.org/x/text v0.16.0 + github.com/quic-go/quic-go v0.46.0 + github.com/vishvananda/netlink v1.3.0 + github.com/wlynxg/anet v0.0.4 + golang.org/x/crypto v0.27.0 + golang.org/x/net v0.29.0 + golang.org/x/sys v0.25.0 + golang.org/x/text v0.18.0 golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 golang.zx2c4.com/wireguard/windows v0.5.3 - nhooyr.io/websocket v1.8.11 + nhooyr.io/websocket v1.8.17 ) require ( @@ -34,7 +34,7 @@ require ( go.uber.org/mock v0.4.0 // indirect golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect golang.org/x/mod v0.19.0 // indirect - golang.org/x/sync v0.7.0 // indirect + golang.org/x/sync v0.8.0 // indirect golang.org/x/tools v0.23.0 // indirect ) @@ -44,5 +44,5 @@ require ( github.com/mattn/go-isatty v0.0.19 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect github.com/olekukonko/tablewriter v0.0.5 - github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f // indirect + github.com/vishvananda/netns v0.0.4 // indirect ) diff --git a/go.sum b/go.sum index 5ddf4169..f95b4c71 100644 --- a/go.sum +++ b/go.sum @@ -56,8 +56,8 @@ github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/quic-go/quic-go v0.45.1 h1:tPfeYCk+uZHjmDRwHHQmvHRYL2t44ROTujLeFVBmjCA= -github.com/quic-go/quic-go v0.45.1/go.mod h1:1dLehS7TIR64+vxGR70GDcatWTOtMX2PUtnKsjbTurI= +github.com/quic-go/quic-go v0.46.0 h1:uuwLClEEyk1DNvchH8uCByQVjo3yKL9opKulExNDs7Y= +github.com/quic-go/quic-go v0.46.0/go.mod h1:1dLehS7TIR64+vxGR70GDcatWTOtMX2PUtnKsjbTurI= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -65,13 +65,12 @@ github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= -github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= -github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= -github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= -github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f h1:p4VB7kIXpOQvVn1ZaTIVp+3vuYAXFe3OJEvjbUYJLaA= -github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= -github.com/wlynxg/anet v0.0.4-0.20240806025826-e684438fc7c6 h1:c/wkXIJvpg2oot7iFqPESTBAO9UvhWTBnW97y9aPgyU= -github.com/wlynxg/anet v0.0.4-0.20240806025826-e684438fc7c6/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= +github.com/vishvananda/netlink v1.3.0 h1:X7l42GfcV4S6E4vHTsw48qbrV+9PVojNfIhZcwQdrZk= +github.com/vishvananda/netlink v1.3.0/go.mod h1:i6NetklAujEcC6fK0JPjT8qSwWyO0HLn4UKG+hGqeJs= +github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8= +github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= +github.com/wlynxg/anet v0.0.4 h1:0de1OFQxnNqAu+x2FAKKCVIrnfGKQbs7FQz++tB0+Uw= +github.com/wlynxg/anet v0.0.4/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= @@ -79,8 +78,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= -golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -93,29 +92,29 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= -golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -129,8 +128,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -154,5 +153,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 h1:TbRPT0HtzFP3Cno1zZo7yPzEEnfu8EjLfl6IU9VfqkQ= gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259/go.mod h1:AVgIgHMwK63XvmAzWG9vLQ41YnVHN0du0tEC46fI7yY= -nhooyr.io/websocket v1.8.11 h1:f/qXNc2/3DpoSZkHt1DQu6rj4zGC8JmkkLkWss0MgN0= -nhooyr.io/websocket v1.8.11/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c= +nhooyr.io/websocket v1.8.17 h1:KEVeLJkUywCKVsnLIDlD/5gtayKp8VoCkksHCGGfT9Y= +nhooyr.io/websocket v1.8.17/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c= From 361b9fd6fc7ef792880728a9fe34a0620cff7143 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 22 Sep 2024 16:54:58 +0100 Subject: [PATCH 238/293] Update WebSocket dependency to new import path --- go.mod | 2 +- go.sum | 4 ++-- src/core/link_ws.go | 2 +- src/core/link_wss.go | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 4fb6f6c9..11e7374f 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/Arceliar/ironwood v0.0.0-20240921214443-277f642d5db3 github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d github.com/cheggaaa/pb/v3 v3.1.5 + github.com/coder/websocket v1.8.12 github.com/gologme/log v1.3.0 github.com/hashicorp/go-syslog v1.0.0 github.com/hjson/hjson-go/v4 v4.4.0 @@ -20,7 +21,6 @@ require ( golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 golang.zx2c4.com/wireguard/windows v0.5.3 - nhooyr.io/websocket v1.8.17 ) require ( diff --git a/go.sum b/go.sum index f95b4c71..8d0046a0 100644 --- a/go.sum +++ b/go.sum @@ -14,6 +14,8 @@ github.com/cheggaaa/pb/v3 v3.1.5/go.mod h1:CrxkeghYTXi1lQBEI7jSn+3svI3cuc19haAj6 github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo= +github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -153,5 +155,3 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 h1:TbRPT0HtzFP3Cno1zZo7yPzEEnfu8EjLfl6IU9VfqkQ= gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259/go.mod h1:AVgIgHMwK63XvmAzWG9vLQ41YnVHN0du0tEC46fI7yY= -nhooyr.io/websocket v1.8.17 h1:KEVeLJkUywCKVsnLIDlD/5gtayKp8VoCkksHCGGfT9Y= -nhooyr.io/websocket v1.8.17/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c= diff --git a/src/core/link_ws.go b/src/core/link_ws.go index 7a7d66f7..0602ed28 100644 --- a/src/core/link_ws.go +++ b/src/core/link_ws.go @@ -8,7 +8,7 @@ import ( "time" "github.com/Arceliar/phony" - "nhooyr.io/websocket" + "github.com/coder/websocket" ) type linkWS struct { diff --git a/src/core/link_wss.go b/src/core/link_wss.go index a9a8df24..0bdb4f3a 100644 --- a/src/core/link_wss.go +++ b/src/core/link_wss.go @@ -7,7 +7,7 @@ import ( "net/url" "github.com/Arceliar/phony" - "nhooyr.io/websocket" + "github.com/coder/websocket" ) type linkWSS struct { From e138fa679c8bf32e02f07cc0c3bba5a2dd5e9e9c Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 22 Sep 2024 17:05:25 +0100 Subject: [PATCH 239/293] Fix link panic when shutting down (closes #1168) --- src/core/link.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core/link.go b/src/core/link.go index 1ead4e32..2eb480d2 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -108,7 +108,9 @@ func (l *links) shutdown() { _ = listener.listener.Close() } for _, link := range l._links { - _ = link._conn.Close() + if link._conn != nil { + _ = link._conn.Close() + } } }) } From b8ab843a98e25d7212beccff3db2b56f0726546c Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 23 Sep 2024 22:40:52 +0100 Subject: [PATCH 240/293] Update admin socket response sorting --- src/admin/getpaths.go | 6 +++--- src/admin/getpeers.go | 32 +++++++++++++++++++++----------- src/admin/getsessions.go | 6 +++--- src/admin/gettree.go | 6 +++--- 4 files changed, 30 insertions(+), 20 deletions(-) diff --git a/src/admin/getpaths.go b/src/admin/getpaths.go index 34de4532..250f4c6a 100644 --- a/src/admin/getpaths.go +++ b/src/admin/getpaths.go @@ -3,7 +3,7 @@ package admin import ( "encoding/hex" "net" - "sort" + "slices" "strings" "github.com/yggdrasil-network/yggdrasil-go/src/address" @@ -35,8 +35,8 @@ func (a *AdminSocket) getPathsHandler(_ *GetPathsRequest, res *GetPathsResponse) Sequence: p.Sequence, }) } - sort.SliceStable(res.Paths, func(i, j int) bool { - return strings.Compare(res.Paths[i].PublicKey, res.Paths[j].PublicKey) < 0 + slices.SortStableFunc(res.Paths, func(a, b PathEntry) int { + return strings.Compare(a.PublicKey, b.PublicKey) }) return nil } diff --git a/src/admin/getpeers.go b/src/admin/getpeers.go index 48234fb1..2c2f8d8a 100644 --- a/src/admin/getpeers.go +++ b/src/admin/getpeers.go @@ -3,7 +3,8 @@ package admin import ( "encoding/hex" "net" - "sort" + "slices" + "strings" "time" "github.com/yggdrasil-network/yggdrasil-go/src/address" @@ -61,17 +62,26 @@ func (a *AdminSocket) getPeersHandler(_ *GetPeersRequest, res *GetPeersResponse) } res.Peers = append(res.Peers, peer) } - sort.Slice(res.Peers, func(i, j int) bool { - if res.Peers[i].Inbound == res.Peers[j].Inbound { - if res.Peers[i].PublicKey == res.Peers[j].PublicKey { - if res.Peers[i].Priority == res.Peers[j].Priority { - return res.Peers[i].Uptime > res.Peers[j].Uptime - } - return res.Peers[i].Priority < res.Peers[j].Priority - } - return res.Peers[i].PublicKey < res.Peers[j].PublicKey + slices.SortStableFunc(res.Peers, func(a, b PeerEntry) int { + if !a.Inbound && b.Inbound { + return -1 } - return !res.Peers[i].Inbound && res.Peers[j].Inbound + if a.Inbound && !b.Inbound { + return 1 + } + if d := strings.Compare(a.PublicKey, b.PublicKey); d != 0 { + return d + } + if d := a.Priority - b.Priority; d != 0 { + return int(d) + } + if d := a.Cost - b.Cost; d != 0 { + return int(d) + } + if d := a.Uptime - b.Uptime; d != 0 { + return int(d) + } + return 0 }) return nil } diff --git a/src/admin/getsessions.go b/src/admin/getsessions.go index e6702f88..2d76a35b 100644 --- a/src/admin/getsessions.go +++ b/src/admin/getsessions.go @@ -3,7 +3,7 @@ package admin import ( "encoding/hex" "net" - "sort" + "slices" "strings" "github.com/yggdrasil-network/yggdrasil-go/src/address" @@ -36,8 +36,8 @@ func (a *AdminSocket) getSessionsHandler(_ *GetSessionsRequest, res *GetSessions Uptime: s.Uptime.Seconds(), }) } - sort.SliceStable(res.Sessions, func(i, j int) bool { - return strings.Compare(res.Sessions[i].PublicKey, res.Sessions[j].PublicKey) < 0 + slices.SortStableFunc(res.Sessions, func(a, b SessionEntry) int { + return strings.Compare(a.PublicKey, b.PublicKey) }) return nil } diff --git a/src/admin/gettree.go b/src/admin/gettree.go index 4b6f32a8..993827d9 100644 --- a/src/admin/gettree.go +++ b/src/admin/gettree.go @@ -3,7 +3,7 @@ package admin import ( "encoding/hex" "net" - "sort" + "slices" "strings" "github.com/yggdrasil-network/yggdrasil-go/src/address" @@ -34,8 +34,8 @@ func (a *AdminSocket) getTreeHandler(_ *GetTreeRequest, res *GetTreeResponse) er Sequence: d.Sequence, }) } - sort.SliceStable(res.Tree, func(i, j int) bool { - return strings.Compare(res.Tree[i].PublicKey, res.Tree[j].PublicKey) < 0 + slices.SortStableFunc(res.Tree, func(a, b TreeEntry) int { + return strings.Compare(a.PublicKey, b.PublicKey) }) return nil } From 43a1a3de64acfacdadc5c9ff00ca8fdb4b7fdc40 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sat, 28 Sep 2024 18:52:04 -0500 Subject: [PATCH 241/293] update ironwood dependency --- cmd/yggdrasil/main.go | 6 +++--- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 3a00b3fc..0ae8ab42 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -54,10 +54,10 @@ func main() { loglevel := flag.String("loglevel", "info", "loglevel to enable") chuserto := flag.String("user", "", "user (and, optionally, group) to set UID/GID to") flag.Parse() - + done := make(chan struct{}) defer close(done) - + // Catch interrupts from the operating system to exit gracefully. ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) @@ -272,7 +272,7 @@ func main() { n.tun.SetupAdminHandlers(n.admin) } } - + //Windows service shutdown minwinsvc.SetOnExit(func() { logger.Infof("Shutting down service ...") diff --git a/go.mod b/go.mod index 11e7374f..b46110bf 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/yggdrasil-network/yggdrasil-go go 1.21 require ( - github.com/Arceliar/ironwood v0.0.0-20240921214443-277f642d5db3 + github.com/Arceliar/ironwood v0.0.0-20240928234310-ca35d0d0d13e github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d github.com/cheggaaa/pb/v3 v3.1.5 github.com/coder/websocket v1.8.12 diff --git a/go.sum b/go.sum index 8d0046a0..91acd28e 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Arceliar/ironwood v0.0.0-20240921214443-277f642d5db3 h1:OJ49qTfdw5MNkpnRraNEsVQbHahSvShh8Z9WYpZrYa0= -github.com/Arceliar/ironwood v0.0.0-20240921214443-277f642d5db3/go.mod h1:6WP4799FX0OuWdENGQAh+0RXp9FLh0y7NZ7tM9cJyXk= +github.com/Arceliar/ironwood v0.0.0-20240928234310-ca35d0d0d13e h1:fXwNf5X37q+1nKCCDkQ7XTdcCmUef3H3zvaMt+QUO7o= +github.com/Arceliar/ironwood v0.0.0-20240928234310-ca35d0d0d13e/go.mod h1:6WP4799FX0OuWdENGQAh+0RXp9FLh0y7NZ7tM9cJyXk= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= From c00779c7d3959ddad37ab63923aad567afb176fb Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 29 Sep 2024 20:58:10 +0100 Subject: [PATCH 242/293] Multicast interface detection and shutdown tweaks May help with #1173. --- src/core/link.go | 5 ++++- src/multicast/multicast.go | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/core/link.go b/src/core/link.go index 2eb480d2..4a8df538 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -422,7 +422,10 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { li := &Listener{ listener: listener, ctx: ctx, - Cancel: cancel, + Cancel: func() { + cancel() + _ = listener.Close() + }, } var options linkOptions diff --git a/src/multicast/multicast.go b/src/multicast/multicast.go index 902d7729..32f5dcad 100644 --- a/src/multicast/multicast.go +++ b/src/multicast/multicast.go @@ -190,6 +190,8 @@ func (m *Multicast) _getAllowedInterfaces() map[string]*interfaceInfo { switch { case iface.Flags&net.FlagUp == 0: continue // Ignore interfaces that are down + case iface.Flags&net.FlagRunning == 0: + continue // Ignore interfaces that are not running case iface.Flags&net.FlagMulticast == 0: continue // Ignore non-multicast interfaces case iface.Flags&net.FlagPointToPoint != 0: From 98a6fdb4f2b372c3feb620f0c49f3d7bf696907e Mon Sep 17 00:00:00 2001 From: Klemens Nanni Date: Sun, 29 Sep 2024 23:05:38 +0300 Subject: [PATCH 243/293] tun: bsd: remove redundant ioctl to set MTU (#1172) wireguard's CreateTUN() sets the MTU using the same ioctl(2), on both FreeBSD and OpenBSD. Tested on OpenBSD (outputwith this patch): ``` # ktrace ./yggdrasil -autoconf | grep Interface 2024/09/24 17:26:29 Interface name: tun0 2024/09/24 17:26:29 Interface IPv6: 201:26e:68f0:502e:f445:13eb:2fe1:f7cd/7 2024/09/24 17:26:29 Interface MTU: 16384 ``` ``` $ ifconfig tun0 | head -n1 tun0: flags=8051 mtu 16384 ``` ``` # kdump | grep ioctl 53097 yggdrasil CALL ioctl(10,SIOCGIFMTU,0xc0000376b8) 53097 yggdrasil RET ioctl 0 53097 yggdrasil CALL ioctl(10,SIOCSIFMTU,0xc0000376c0) 53097 yggdrasil RET ioctl 0 53097 yggdrasil CALL ioctl(10,SIOCGIFMTU,0xc0000377f8) 53097 yggdrasil RET ioctl 0 53097 yggdrasil CALL ioctl(10,_IOW('i',12,0x20),0xc00003777c) 53097 yggdrasil RET ioctl -1 errno 25 Inappropriate ioctl for device "2024/09/24 17:26:29 Error in SIOCSIFADDR_IN6: inappropriate ioctl for device ``` (The completely broken address ioctl is another story...) --- src/tun/tun_bsd.go | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/src/tun/tun_bsd.go b/src/tun/tun_bsd.go index da5b3297..3064546b 100644 --- a/src/tun/tun_bsd.go +++ b/src/tun/tun_bsd.go @@ -54,11 +54,6 @@ struct in6_ifreq { 290 }; */ -type in6_ifreq_mtu struct { - ifr_name [syscall.IFNAMSIZ]byte - ifru_mtu int -} - type in6_ifreq_addr struct { ifr_name [syscall.IFNAMSIZ]byte ifru_addr sockaddr_in6 @@ -112,26 +107,6 @@ func (tun *TunAdapter) setupAddress(addr string) error { tun.log.Infof("Interface IPv6: %s", addr) tun.log.Infof("Interface MTU: %d", tun.mtu) - // Create the MTU request - var ir in6_ifreq_mtu - copy(ir.ifr_name[:], tun.Name()) - ir.ifru_mtu = int(tun.mtu) - - // Set the MTU - if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(sfd), uintptr(syscall.SIOCSIFMTU), uintptr(unsafe.Pointer(&ir))); errno != 0 { - err = errno - tun.log.Errorf("Error in SIOCSIFMTU: %v", errno) - - // Fall back to ifconfig to set the MTU - cmd := exec.Command("ifconfig", tun.Name(), "mtu", string(tun.mtu)) - tun.log.Warnf("Using ifconfig as fallback: %v", strings.Join(cmd.Args, " ")) - output, err := cmd.CombinedOutput() - if err != nil { - tun.log.Errorf("SIOCSIFMTU fallback failed: %v.", err) - tun.log.Traceln(string(output)) - } - } - // Create the address request // FIXME: I don't work! var ar in6_ifreq_addr From d6fd305f125ed27708ed4b8afc798bbf3bac67b8 Mon Sep 17 00:00:00 2001 From: Sergey Bobrenok Date: Sun, 29 Sep 2024 23:06:36 +0300 Subject: [PATCH 244/293] Fix Android build with Go 1.23.0 or later (#1166) The `github.com/wlynxg/anet` library depends on the `//go:linkname` linker feature [1]. However, since Go 1.23.0, the usage of `//go:linkname` has been restricted [2]. And now it's necessary to explicitly specify `-checklinkname=0` linker flag to use it. [1] https://github.com/wlynxg/anet/blob/main/README.md#how-to-build-with-go-1230-or-later [2] https://tip.golang.org/doc/go1.23#linker Resolves: #1165 --- contrib/mobile/build | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/contrib/mobile/build b/contrib/mobile/build index 3f6b9bfc..9be9529b 100755 --- a/contrib/mobile/build +++ b/contrib/mobile/build @@ -7,6 +7,7 @@ set -ef PKGSRC=${PKGSRC:-github.com/yggdrasil-network/yggdrasil-go/src/version} PKGNAME=${PKGNAME:-$(sh contrib/semver/name.sh)} PKGVER=${PKGVER:-$(sh contrib/semver/version.sh --bare)} +GOVER=$(go version | { read _ _ version _; echo ${version#go}; }) LDFLAGS="-X $PKGSRC.buildName=$PKGNAME -X $PKGSRC.buildVersion=$PKGVER" ARGS="-v" @@ -33,6 +34,15 @@ if [ ! $IOS ] && [ ! $ANDROID ]; then exit 1 fi +ver_le() { + printf "$1\n$2\n" | sort -VC +} + +if [ $ANDROID ] && ver_le 1.23.0 $GOVER ; then + # github.com/wlynxg/anet library relies on //go:linkname + LDFLAGS="$LDFLAGS -checklinkname=0" +fi + if [ $IOS ]; then echo "Building framework for iOS" go get golang.org/x/mobile/bind From d1b849588f1dc4bcc2d5c2b3551628d42beeb67b Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 29 Sep 2024 21:23:45 +0100 Subject: [PATCH 245/293] Fix bug where ephemeral links would try to reconnect in a fast loop Helps #1141, although not a complete solution. --- src/core/link.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/link.go b/src/core/link.go index 4a8df538..66cf517d 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -359,8 +359,9 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error { if backoffNow() { continue } - return } + // Ephemeral or incoming connections don't reconnect. + return } }() }) From 377bc664c9b2458ea2906fd9e4f3a6788cd1327f Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 29 Sep 2024 21:38:56 +0100 Subject: [PATCH 246/293] The `AllowedPublicKeys` option should not apply to multicast listeners Another fix for #1141. --- src/core/api.go | 9 ++++++++- src/core/core.go | 2 +- src/core/link.go | 34 ++++++++++++++++++---------------- src/multicast/multicast.go | 2 +- 4 files changed, 28 insertions(+), 19 deletions(-) diff --git a/src/core/api.go b/src/core/api.go index c236b1b5..2aa1ba87 100644 --- a/src/core/api.go +++ b/src/core/api.go @@ -150,7 +150,14 @@ func (c *Core) GetSessions() []SessionInfo { // parsed from a string of the form e.g. "tcp://a.b.c.d:e". In the case of a // link-local address, the interface should be provided as the second argument. func (c *Core) Listen(u *url.URL, sintf string) (*Listener, error) { - return c.links.listen(u, sintf) + return c.links.listen(u, sintf, false) +} + +// ListenLocal starts a listener, like the Listen function, but is used for +// more trustworthy situations where you want to ignore AllowedPublicKeys, i.e. +// with multicast listeners. +func (c *Core) ListenLocal(u *url.URL, sintf string) (*Listener, error) { + return c.links.listen(u, sintf, true) } // Address gets the IPv6 address of the Yggdrasil node. This is always a /128 diff --git a/src/core/core.go b/src/core/core.go index 41858cb1..2b206ee1 100644 --- a/src/core/core.go +++ b/src/core/core.go @@ -127,7 +127,7 @@ func New(cert *tls.Certificate, logger Logger, opts ...SetupOption) (*Core, erro c.log.Errorf("Invalid listener URI %q specified, ignoring\n", listenaddr) continue } - if _, err = c.links.listen(u, ""); err != nil { + if _, err = c.links.listen(u, "", false); err != nil { c.log.Errorf("Failed to start listener %q: %s\n", listenaddr, err) } } diff --git a/src/core/link.go b/src/core/link.go index 66cf517d..04fe0266 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -336,7 +336,7 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error { // Give the connection to the handler. The handler will block // for the lifetime of the connection. - if err = l.handler(linkType, options, lc, resetBackoff); err != nil && err != io.EOF { + if err = l.handler(linkType, options, lc, resetBackoff, false); err != nil && err != io.EOF { l.core.log.Debugf("Link %s error: %s\n", info.uri, err) } @@ -395,7 +395,7 @@ func (l *links) remove(u *url.URL, sintf string, _ linkType) error { return retErr } -func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { +func (l *links) listen(u *url.URL, sintf string, local bool) (*Listener, error) { ctx, cancel := context.WithCancel(l.core.ctx) var protocol linkProtocol switch strings.ToLower(u.Scheme) { @@ -522,7 +522,7 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { // Give the connection to the handler. The handler will block // for the lifetime of the connection. - switch err = l.handler(linkTypeIncoming, options, lc, nil); { + switch err = l.handler(linkTypeIncoming, options, lc, nil, local); { case err == nil: case errors.Is(err, io.EOF): case errors.Is(err, net.ErrClosed): @@ -563,7 +563,7 @@ func (l *links) connect(ctx context.Context, u *url.URL, info linkInfo, options return dialer.dial(ctx, u, info, options) } -func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn, success func()) error { +func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn, success func(), local bool) error { meta := version_getBaseMetadata() meta.publicKey = l.core.public meta.priority = options.priority @@ -606,19 +606,21 @@ func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn, s } } // Check if we're authorized to connect to this key / IP - var allowed map[[32]byte]struct{} - phony.Block(l.core, func() { - allowed = l.core.config._allowedPublicKeys - }) - isallowed := len(allowed) == 0 - for k := range allowed { - if bytes.Equal(k[:], meta.publicKey) { - isallowed = true - break + if !local { + var allowed map[[32]byte]struct{} + phony.Block(l.core, func() { + allowed = l.core.config._allowedPublicKeys + }) + isallowed := len(allowed) == 0 + for k := range allowed { + if bytes.Equal(k[:], meta.publicKey) { + isallowed = true + break + } + } + if linkType == linkTypeIncoming && !isallowed { + return fmt.Errorf("node public key %q is not in AllowedPublicKeys", hex.EncodeToString(meta.publicKey)) } - } - if linkType == linkTypeIncoming && !isallowed { - return fmt.Errorf("node public key %q is not in AllowedPublicKeys", hex.EncodeToString(meta.publicKey)) } dir := "outbound" diff --git a/src/multicast/multicast.go b/src/multicast/multicast.go index 32f5dcad..77ea8a50 100644 --- a/src/multicast/multicast.go +++ b/src/multicast/multicast.go @@ -327,7 +327,7 @@ func (m *Multicast) _announce() { Host: net.JoinHostPort(addrIP.String(), fmt.Sprintf("%d", info.port)), RawQuery: v.Encode(), } - if li, err := m.core.Listen(u, iface.Name); err == nil { + if li, err := m.core.ListenLocal(u, iface.Name); err == nil { m.log.Debugln("Started multicasting on", iface.Name) // Store the listener so that we can stop it later if needed linfo = &listenerInfo{listener: li, time: time.Now(), port: info.port} From 6d5243bd9afb841f8968a481fbfd0b1c79ebfe69 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 29 Sep 2024 22:04:41 +0100 Subject: [PATCH 247/293] Add unit test for `AllowedPublicKeys` --- src/core/core_test.go | 87 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/src/core/core_test.go b/src/core/core_test.go index cece33c2..f186f43f 100644 --- a/src/core/core_test.go +++ b/src/core/core_test.go @@ -25,6 +25,27 @@ func GetLoggerWithPrefix(prefix string, verbose bool) *log.Logger { return l } +func require_NoError(t *testing.T, err error) { + t.Helper() + if err != nil { + t.Fatal(err) + } +} + +func require_Equal[T comparable](t *testing.T, a, b T) { + t.Helper() + if a != b { + t.Fatalf("%v != %v", a, b) + } +} + +func require_True(t *testing.T, a bool) { + t.Helper() + if !a { + t.Fatal("expected true") + } +} + // CreateAndConnectTwo creates two nodes. nodeB connects to nodeA. // Verbosity flag is passed to logger. func CreateAndConnectTwo(t testing.TB, verbose bool) (nodeA *Core, nodeB *Core) { @@ -201,3 +222,69 @@ func BenchmarkCore_Start_Transfer(b *testing.B) { } <-done } + +func TestAllowedPublicKeys(t *testing.T) { + logger := GetLoggerWithPrefix("", false) + cfgA, cfgB := config.GenerateConfig(), config.GenerateConfig() + require_NoError(t, cfgA.GenerateSelfSignedCertificate()) + require_NoError(t, cfgB.GenerateSelfSignedCertificate()) + + nodeA, err := New(cfgA.Certificate, logger, AllowedPublicKey("abcdef")) + require_NoError(t, err) + defer nodeA.Stop() + + nodeB, err := New(cfgB.Certificate, logger) + require_NoError(t, err) + defer nodeB.Stop() + + u, err := url.Parse("tcp://localhost:0") + require_NoError(t, err) + + l, err := nodeA.Listen(u, "") + require_NoError(t, err) + + u, err = url.Parse("tcp://" + l.Addr().String()) + require_NoError(t, err) + + require_NoError(t, nodeB.AddPeer(u, "")) + + time.Sleep(time.Second) + + peers := nodeB.GetPeers() + require_Equal(t, len(peers), 1) + require_True(t, !peers[0].Up) + require_True(t, peers[0].LastError != nil) +} + +func TestAllowedPublicKeysLocal(t *testing.T) { + logger := GetLoggerWithPrefix("", false) + cfgA, cfgB := config.GenerateConfig(), config.GenerateConfig() + require_NoError(t, cfgA.GenerateSelfSignedCertificate()) + require_NoError(t, cfgB.GenerateSelfSignedCertificate()) + + nodeA, err := New(cfgA.Certificate, logger, AllowedPublicKey("abcdef")) + require_NoError(t, err) + defer nodeA.Stop() + + nodeB, err := New(cfgB.Certificate, logger) + require_NoError(t, err) + defer nodeB.Stop() + + u, err := url.Parse("tcp://localhost:0") + require_NoError(t, err) + + l, err := nodeA.ListenLocal(u, "") + require_NoError(t, err) + + u, err = url.Parse("tcp://" + l.Addr().String()) + require_NoError(t, err) + + require_NoError(t, nodeB.AddPeer(u, "")) + + time.Sleep(time.Second) + + peers := nodeB.GetPeers() + require_Equal(t, len(peers), 1) + require_True(t, peers[0].Up) + require_True(t, peers[0].LastError == nil) +} From ccda1075c08d95804e6f84a789930589356cc9da Mon Sep 17 00:00:00 2001 From: Klemens Nanni Date: Mon, 30 Sep 2024 16:24:20 +0300 Subject: [PATCH 248/293] Fix ioctl(2) code for OpenBSD (#1175) This cleans up the mess to configure an IP address on a tun(4) device. Handrolling a hardcoded ioctl(2) request is far from perfect, but Go (golang.org/sys/unix) is to blame here. Tested on OpenBSD 7.6 -current where yggdrasil now drives the interface would use of ifconfig or other helpers. --- src/tun/{tun_bsd.go => tun_freebsd.go} | 4 +- src/tun/tun_openbsd.go | 122 +++++++++++++++++++++++++ 2 files changed, 124 insertions(+), 2 deletions(-) rename src/tun/{tun_bsd.go => tun_freebsd.go} (98%) create mode 100644 src/tun/tun_openbsd.go diff --git a/src/tun/tun_bsd.go b/src/tun/tun_freebsd.go similarity index 98% rename from src/tun/tun_bsd.go rename to src/tun/tun_freebsd.go index 3064546b..7b8ab50c 100644 --- a/src/tun/tun_bsd.go +++ b/src/tun/tun_freebsd.go @@ -1,5 +1,5 @@ -//go:build openbsd || freebsd -// +build openbsd freebsd +//go:build freebsd +// +build freebsd package tun diff --git a/src/tun/tun_openbsd.go b/src/tun/tun_openbsd.go new file mode 100644 index 00000000..714db3a8 --- /dev/null +++ b/src/tun/tun_openbsd.go @@ -0,0 +1,122 @@ +//go:build openbsd +// +build openbsd + +package tun + +import ( + "fmt" + "net" + "syscall" + "unsafe" + + "golang.org/x/sys/unix" + + wgtun "golang.zx2c4.com/wireguard/tun" +) + +const ( + SIOCAIFADDR_IN6 = 0x8080691a + ND6_INFINITE_LIFETIME = 0xffffffff +) + +type in6_addrlifetime struct { + ia6t_expire int64 + ia6t_preferred int64 + ia6t_vltime uint32 + ia6t_pltime uint32 +} + +// Match types from the net package, effectively being [16]byte for IPv6 addresses. +type in6_addr [16]uint8 + +type sockaddr_in6 struct { + sin6_len uint8 + sin6_family uint8 + sin6_port uint16 + sin6_flowinfo uint32 + sin6_addr in6_addr + sin6_scope_id uint32 +} + +func (sa6 *sockaddr_in6) setSockaddr(addr [/*16*/]byte /* net.IP or net.IPMask */) { + sa6.sin6_len = uint8(unsafe.Sizeof(*sa6)) + sa6.sin6_family = unix.AF_INET6 + + for i := range sa6.sin6_addr { + sa6.sin6_addr[i] = addr[i] + } +} + +type in6_aliasreq struct { + ifra_name [syscall.IFNAMSIZ]byte + ifra_addr sockaddr_in6 + ifra_dstaddr sockaddr_in6 + ifra_prefixmask sockaddr_in6 + ifra_flags int32 + ifra_lifetime in6_addrlifetime +} + +// Configures the TUN adapter with the correct IPv6 address and MTU. +func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error { + iface, err := wgtun.CreateTUN(ifname, int(mtu)) + if err != nil { + return fmt.Errorf("failed to create TUN: %w", err) + } + tun.iface = iface + if mtu, err := iface.MTU(); err == nil { + tun.mtu = getSupportedMTU(uint64(mtu)) + } else { + tun.mtu = 0 + } + if addr != "" { + return tun.setupAddress(addr) + } + return nil +} + +// Configures the "utun" adapter from an existing file descriptor. +func (tun *TunAdapter) setupFD(fd int32, addr string, mtu uint64) error { + return fmt.Errorf("setup via FD not supported on this platform") +} + +func (tun *TunAdapter) setupAddress(addr string) error { + var sfd int + var err error + + ip, prefix, err := net.ParseCIDR(addr) + if err != nil { + tun.log.Errorf("Error in ParseCIDR: %v", err) + return err + } + + // Create system socket + if sfd, err = unix.Socket(unix.AF_INET6, unix.SOCK_DGRAM, 0); err != nil { + tun.log.Printf("Create AF_INET6 socket failed: %v", err) + return err + } + + // Friendly output + tun.log.Infof("Interface name: %s", tun.Name()) + tun.log.Infof("Interface IPv6: %s", addr) + tun.log.Infof("Interface MTU: %d", tun.mtu) + + // Create the address request + var ar in6_aliasreq + copy(ar.ifra_name[:], tun.Name()) + + ar.ifra_addr.setSockaddr(ip) + + prefixmask := net.CIDRMask(prefix.Mask.Size()) + ar.ifra_prefixmask.setSockaddr(prefixmask) + + ar.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME + ar.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME + + // Set the interface address + if err = unix.IoctlSetInt(sfd, SIOCAIFADDR_IN6, int(uintptr(unsafe.Pointer(&ar)))); err != nil { + tun.log.Errorf("Error in SIOCAIFADDR_IN6: %v", err) + return err + } + + return nil +} From 874083da790f69132c468a4f7fa5c07fbd97b5a3 Mon Sep 17 00:00:00 2001 From: Klemens Nanni Date: Mon, 30 Sep 2024 16:25:04 +0300 Subject: [PATCH 249/293] Replace repeated subscripts with single TrimPrefix (#1176) This stood out to me while reading the code: [7:] is skipping "unix://", so why not do that? Doing so reveals a bug in the last line changed, where chmod(2) failure would print just the prefix, not everything but it... easy to miss, but now this kind of bug can no longer happen. --- src/admin/admin.go | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/admin/admin.go b/src/admin/admin.go index 7cca1bbb..0b9e5e71 100644 --- a/src/admin/admin.go +++ b/src/admin/admin.go @@ -238,27 +238,28 @@ func (a *AdminSocket) listen() { if err == nil { switch strings.ToLower(u.Scheme) { case "unix": - if _, err := os.Stat(listenaddr[7:]); err == nil { - a.log.Debugln("Admin socket", listenaddr[7:], "already exists, trying to clean up") - if _, err := net.DialTimeout("unix", listenaddr[7:], time.Second*2); err == nil || err.(net.Error).Timeout() { - a.log.Errorln("Admin socket", listenaddr[7:], "already exists and is in use by another process") + file := strings.TrimPrefix(listenaddr, "unix://") + if _, err := os.Stat(file); err == nil { + a.log.Debugln("Admin socket", file, "already exists, trying to clean up") + if _, err := net.DialTimeout("unix", file, time.Second*2); err == nil || err.(net.Error).Timeout() { + a.log.Errorln("Admin socket", file, "already exists and is in use by another process") os.Exit(1) } else { - if err := os.Remove(listenaddr[7:]); err == nil { - a.log.Debugln(listenaddr[7:], "was cleaned up") + if err := os.Remove(file); err == nil { + a.log.Debugln(file, "was cleaned up") } else { - a.log.Errorln(listenaddr[7:], "already exists and was not cleaned up:", err) + a.log.Errorln(file, "already exists and was not cleaned up:", err) os.Exit(1) } } } - a.listener, err = net.Listen("unix", listenaddr[7:]) + a.listener, err = net.Listen("unix", file) if err == nil { - switch listenaddr[7:8] { + switch file[:1] { case "@": // maybe abstract namespace default: - if err := os.Chmod(listenaddr[7:], 0660); err != nil { - a.log.Warnln("WARNING:", listenaddr[:7], "may have unsafe permissions!") + if err := os.Chmod(file, 0660); err != nil { + a.log.Warnln("WARNING:", file, "may have unsafe permissions!") } } } From d22dc9ecc96d7434e33a7ec3205d5993034183f5 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 10 Oct 2024 09:23:13 +0100 Subject: [PATCH 250/293] TUN: Skip `ErrTooManySegments` --- src/tun/iface.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/tun/iface.go b/src/tun/iface.go index 3a4c55f4..f1898281 100644 --- a/src/tun/iface.go +++ b/src/tun/iface.go @@ -1,5 +1,11 @@ package tun +import ( + "errors" + + wgtun "golang.zx2c4.com/wireguard/tun" +) + const TUN_OFFSET_BYTES = 80 // sizeof(virtio_net_hdr) func (tun *TunAdapter) read() { @@ -12,6 +18,10 @@ func (tun *TunAdapter) read() { for { n, err := tun.iface.Read(bufs, sizes, TUN_OFFSET_BYTES) if err != nil { + if errors.Is(err, wgtun.ErrTooManySegments) { + tun.log.Debugln("TUN segments dropped: %v", err) + continue + } tun.log.Errorln("Error reading TUN:", err) return } From 01e73792fe6e505b11e82d2fc05aecba43550908 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 13 Oct 2024 20:04:21 +0100 Subject: [PATCH 251/293] Update to Arceliar/ironwood@0ac2ff3eef3b6be16426f6cf7e0e20cbd75d61aa --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index b46110bf..8d524210 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/yggdrasil-network/yggdrasil-go go 1.21 require ( - github.com/Arceliar/ironwood v0.0.0-20240928234310-ca35d0d0d13e + github.com/Arceliar/ironwood v0.0.0-20241013112336-0ac2ff3eef3b github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d github.com/cheggaaa/pb/v3 v3.1.5 github.com/coder/websocket v1.8.12 diff --git a/go.sum b/go.sum index 91acd28e..9d9cfa89 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Arceliar/ironwood v0.0.0-20240928234310-ca35d0d0d13e h1:fXwNf5X37q+1nKCCDkQ7XTdcCmUef3H3zvaMt+QUO7o= -github.com/Arceliar/ironwood v0.0.0-20240928234310-ca35d0d0d13e/go.mod h1:6WP4799FX0OuWdENGQAh+0RXp9FLh0y7NZ7tM9cJyXk= +github.com/Arceliar/ironwood v0.0.0-20241013112336-0ac2ff3eef3b h1:s2Je0/f6HwM0KI0naVKHvCecAkGwK1FawHtIu/mMPnA= +github.com/Arceliar/ironwood v0.0.0-20241013112336-0ac2ff3eef3b/go.mod h1:6WP4799FX0OuWdENGQAh+0RXp9FLh0y7NZ7tM9cJyXk= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= From a038a6a8ef90e61840dc96a7ffea39a67b8814c9 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 13 Oct 2024 21:33:47 +0100 Subject: [PATCH 252/293] Update to Arceliar/ironwood@4ea1ec6d68200a2ff6697b408c094f1a5b95bb60 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 8d524210..dcce403e 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/yggdrasil-network/yggdrasil-go go 1.21 require ( - github.com/Arceliar/ironwood v0.0.0-20241013112336-0ac2ff3eef3b + github.com/Arceliar/ironwood v0.0.0-20241013195543-4ea1ec6d6820 github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d github.com/cheggaaa/pb/v3 v3.1.5 github.com/coder/websocket v1.8.12 diff --git a/go.sum b/go.sum index 9d9cfa89..c8603166 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Arceliar/ironwood v0.0.0-20241013112336-0ac2ff3eef3b h1:s2Je0/f6HwM0KI0naVKHvCecAkGwK1FawHtIu/mMPnA= -github.com/Arceliar/ironwood v0.0.0-20241013112336-0ac2ff3eef3b/go.mod h1:6WP4799FX0OuWdENGQAh+0RXp9FLh0y7NZ7tM9cJyXk= +github.com/Arceliar/ironwood v0.0.0-20241013195543-4ea1ec6d6820 h1:RMcTThCWnFIATUiCYNYZrxL//ZdwDj8Rj9TOk2FAFuM= +github.com/Arceliar/ironwood v0.0.0-20241013195543-4ea1ec6d6820/go.mod h1:6WP4799FX0OuWdENGQAh+0RXp9FLh0y7NZ7tM9cJyXk= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= From 81e345c1ae96a7fbddfebf07587676333ab8fab9 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 16 Oct 2024 09:46:22 +0100 Subject: [PATCH 253/293] Update to Arceliar/ironwood@f6fb9da97a170f97922e65c6b5afbcc499939503 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index dcce403e..7b756860 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/yggdrasil-network/yggdrasil-go go 1.21 require ( - github.com/Arceliar/ironwood v0.0.0-20241013195543-4ea1ec6d6820 + github.com/Arceliar/ironwood v0.0.0-20241016082300-f6fb9da97a17 github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d github.com/cheggaaa/pb/v3 v3.1.5 github.com/coder/websocket v1.8.12 diff --git a/go.sum b/go.sum index c8603166..2c36a60d 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Arceliar/ironwood v0.0.0-20241013195543-4ea1ec6d6820 h1:RMcTThCWnFIATUiCYNYZrxL//ZdwDj8Rj9TOk2FAFuM= -github.com/Arceliar/ironwood v0.0.0-20241013195543-4ea1ec6d6820/go.mod h1:6WP4799FX0OuWdENGQAh+0RXp9FLh0y7NZ7tM9cJyXk= +github.com/Arceliar/ironwood v0.0.0-20241016082300-f6fb9da97a17 h1:uOvHqPwu09ndYZQDUL6QvyDcz0M9kwooKYa/PEfLwIU= +github.com/Arceliar/ironwood v0.0.0-20241016082300-f6fb9da97a17/go.mod h1:6WP4799FX0OuWdENGQAh+0RXp9FLh0y7NZ7tM9cJyXk= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= From 1ee61dcefaa083d79b24e659b2688aeffa837b06 Mon Sep 17 00:00:00 2001 From: Klemens Nanni Date: Thu, 17 Oct 2024 15:22:22 +0300 Subject: [PATCH 254/293] zap obsolete nonexistent command from usage (#1184) --- cmd/yggdrasilctl/cmd_line_env.go | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd/yggdrasilctl/cmd_line_env.go b/cmd/yggdrasilctl/cmd_line_env.go index b350b7e7..e929b0ba 100644 --- a/cmd/yggdrasilctl/cmd_line_env.go +++ b/cmd/yggdrasilctl/cmd_line_env.go @@ -38,7 +38,6 @@ func (cmdLineEnv *CmdLineEnv) parseFlagsAndArgs() { fmt.Println("Examples:") fmt.Println(" - ", os.Args[0], "list") fmt.Println(" - ", os.Args[0], "getPeers") - fmt.Println(" - ", os.Args[0], "setTunTap name=auto mtu=1500 tap_mode=false") fmt.Println(" - ", os.Args[0], "-endpoint=tcp://localhost:9001 getPeers") fmt.Println(" - ", os.Args[0], "-endpoint=unix:///var/run/ygg.sock getPeers") } From a6429390da831e77c47863e124d39a70e94b47e6 Mon Sep 17 00:00:00 2001 From: Klemens Nanni Date: Thu, 17 Oct 2024 15:22:46 +0300 Subject: [PATCH 255/293] Use UNIX socket patch from url struct (#1186) No need to extract it again when the url package provides it for us: ``` $ jq -n '{"AdminListen":"unix:///tmp/ygg.sock"}' | ./yggdrasil -useconf | grep 'admin socket' 2024/10/08 22:41:11 UNIX admin socket listening on /tmp/ygg.sock ``` Follow-up on #1176 --- src/admin/admin.go | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/admin/admin.go b/src/admin/admin.go index 0b9e5e71..8823fc5e 100644 --- a/src/admin/admin.go +++ b/src/admin/admin.go @@ -238,28 +238,27 @@ func (a *AdminSocket) listen() { if err == nil { switch strings.ToLower(u.Scheme) { case "unix": - file := strings.TrimPrefix(listenaddr, "unix://") - if _, err := os.Stat(file); err == nil { - a.log.Debugln("Admin socket", file, "already exists, trying to clean up") - if _, err := net.DialTimeout("unix", file, time.Second*2); err == nil || err.(net.Error).Timeout() { - a.log.Errorln("Admin socket", file, "already exists and is in use by another process") + if _, err := os.Stat(u.Path); err == nil { + a.log.Debugln("Admin socket", u.Path, "already exists, trying to clean up") + if _, err := net.DialTimeout("unix", u.Path, time.Second*2); err == nil || err.(net.Error).Timeout() { + a.log.Errorln("Admin socket", u.Path, "already exists and is in use by another process") os.Exit(1) } else { - if err := os.Remove(file); err == nil { - a.log.Debugln(file, "was cleaned up") + if err := os.Remove(u.Path); err == nil { + a.log.Debugln(u.Path, "was cleaned up") } else { - a.log.Errorln(file, "already exists and was not cleaned up:", err) + a.log.Errorln(u.Path, "already exists and was not cleaned up:", err) os.Exit(1) } } } - a.listener, err = net.Listen("unix", file) + a.listener, err = net.Listen("unix", u.Path) if err == nil { - switch file[:1] { + switch u.Path[:1] { case "@": // maybe abstract namespace default: - if err := os.Chmod(file, 0660); err != nil { - a.log.Warnln("WARNING:", file, "may have unsafe permissions!") + if err := os.Chmod(u.Path, 0660); err != nil { + a.log.Warnln("WARNING:", u.Path, "may have unsafe permissions!") } } } From 0b9469100cf4dbfc5ea904f0182d1575d5c95963 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 17 Oct 2024 13:23:11 +0100 Subject: [PATCH 256/293] Update dependencies --- go.mod | 10 +++++----- go.sum | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index 7b756860..76641a60 100644 --- a/go.mod +++ b/go.mod @@ -13,11 +13,11 @@ require ( github.com/kardianos/minwinsvc v1.0.2 github.com/quic-go/quic-go v0.46.0 github.com/vishvananda/netlink v1.3.0 - github.com/wlynxg/anet v0.0.4 - golang.org/x/crypto v0.27.0 - golang.org/x/net v0.29.0 - golang.org/x/sys v0.25.0 - golang.org/x/text v0.18.0 + github.com/wlynxg/anet v0.0.5 + golang.org/x/crypto v0.28.0 + golang.org/x/net v0.30.0 + golang.org/x/sys v0.26.0 + golang.org/x/text v0.19.0 golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 golang.zx2c4.com/wireguard/windows v0.5.3 diff --git a/go.sum b/go.sum index 2c36a60d..89dd0c7b 100644 --- a/go.sum +++ b/go.sum @@ -71,8 +71,8 @@ github.com/vishvananda/netlink v1.3.0 h1:X7l42GfcV4S6E4vHTsw48qbrV+9PVojNfIhZcwQ github.com/vishvananda/netlink v1.3.0/go.mod h1:i6NetklAujEcC6fK0JPjT8qSwWyO0HLn4UKG+hGqeJs= github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8= github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= -github.com/wlynxg/anet v0.0.4 h1:0de1OFQxnNqAu+x2FAKKCVIrnfGKQbs7FQz++tB0+Uw= -github.com/wlynxg/anet v0.0.4/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= +github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU= +github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= @@ -80,8 +80,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= -golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -94,8 +94,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= -golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -115,8 +115,8 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -130,8 +130,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= -golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= From 0b9c8bd020f971847604e7e1f0e0cfc66e65da49 Mon Sep 17 00:00:00 2001 From: Neil Date: Sat, 19 Oct 2024 17:09:46 +0100 Subject: [PATCH 257/293] Yggdrasil 0.5.9 (#1191) Changelog updates. Co-authored-by: Neil Alexander --- CHANGELOG.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 34c7fe30..39cfa960 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,31 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - in case of vulnerabilities. --> +## [0.5.9] - 2024-10-19 + +### Added + +* New command line option `-user` for changing the process UID/GID + +### Changed + +* The routing algorithm has been updated with RTT-aware link costing, which should prefer lower latency links over higher latency links where possible + * The calculated cost is an average of the link RTT, but newly established links are costed higher to begin with, such that unstable peerings can be avoided + * Link costs are only used where multiple next-hops are available and will be ignored if there is only one loop-free path to the destination + * This is protocol-compatible with existing v0.5.x nodes but will have the best results when peering with nodes that are also running the latest version + * The `getPeers` endpoint will now report the calculated link cost for each given peer +* Upgrade dependencies + +### Fixed + +* Multicast discovery should now work again when building Yggdrasil as an Android framework +* Multicast discovery will now correctly ignore interfaces that are not marked as running +* Ephemeral links, such as those added by multicast, will no longer try to reconnect in a fast loop, fixing a high CPU issue +* The TUN interface will no longer stop working when hitting a segment read error from vectorised reads +* The `AllowedPublicKeys` option will once again no longer apply to multicast peerings, as was originally intended +* A potential panic when shutting down peering links has been fixed +* A redundant system call for setting MTU on OpenBSD has been removed + ## [0.5.8] - 2024-08-12 ### Fixed From b20ad846a136d619e435a97380a14af3f97376f8 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 20 Oct 2024 21:27:20 +0100 Subject: [PATCH 258/293] When `IfName` is `none`, start queue goroutine, otherwise `iprwc` blocks and some handlers don't run --- src/tun/tun.go | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tun/tun.go b/src/tun/tun.go index e7ba3a0f..d15a0674 100644 --- a/src/tun/tun.go +++ b/src/tun/tun.go @@ -125,6 +125,7 @@ func (tun *TunAdapter) _start() error { if tun.config.name == "none" || tun.config.name == "dummy" { tun.log.Debugln("Not starting TUN as ifname is none or dummy") tun.isEnabled = false + go tun.queue() go tun.write() return nil } From ef110b018109655633a52b08472b1453fe61068a Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 27 Oct 2024 20:38:15 +0000 Subject: [PATCH 259/293] Update Debian package metadata --- contrib/deb/generate.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/contrib/deb/generate.sh b/contrib/deb/generate.sh index 8d39eb1d..5731827c 100644 --- a/contrib/deb/generate.sh +++ b/contrib/deb/generate.sh @@ -50,11 +50,12 @@ echo 9 > /tmp/$PKGNAME/debian/compat cat > /tmp/$PKGNAME/debian/control << EOF Package: $PKGNAME Version: $PKGVERSION -Section: contrib/net -Priority: extra +Section: golang +Priority: optional Architecture: $PKGARCH Replaces: $PKGREPLACES Conflicts: $PKGREPLACES +Depends: systemd Maintainer: Neil Alexander Description: Yggdrasil Network Yggdrasil is an early-stage implementation of a fully end-to-end encrypted IPv6 @@ -102,7 +103,7 @@ then echo "Normalising and updating /etc/yggdrasil/yggdrasil.conf" /usr/bin/yggdrasil -useconf -normaliseconf < /var/backups/yggdrasil.conf.`date +%Y%m%d` > /etc/yggdrasil/yggdrasil.conf - + chown root:yggdrasil /etc/yggdrasil/yggdrasil.conf chmod 640 /etc/yggdrasil/yggdrasil.conf else From ff0ef7ff567b772c6a45a60eb37ed9982a55aac7 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 27 Oct 2024 20:59:05 +0000 Subject: [PATCH 260/293] Update comments in default configuration file --- src/config/config.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/config/config.go b/src/config/config.go index 9a7f7180..64961547 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -43,17 +43,17 @@ type NodeConfig struct { PrivateKey KeyBytes `json:",omitempty" comment:"Your private key. DO NOT share this with anyone!"` PrivateKeyPath string `json:",omitempty" comment:"The path to your private key file in PEM format."` Certificate *tls.Certificate `json:"-"` - Peers []string `comment:"List of connection strings for outbound peer connections in URI format,\ne.g. tls://a.b.c.d:e or socks://a.b.c.d:e/f.g.h.i:j. These connections\nwill obey the operating system routing table, therefore you should\nuse this section when you may connect via different interfaces."` - InterfacePeers map[string][]string `comment:"List of connection strings for outbound peer connections in URI format,\narranged by source interface, e.g. { \"eth0\": [ \"tls://a.b.c.d:e\" ] }.\nNote that SOCKS peerings will NOT be affected by this option and should\ngo in the \"Peers\" section instead."` - Listen []string `comment:"Listen addresses for incoming connections. You will need to add\nlisteners in order to accept incoming peerings from non-local nodes.\nMulticast peer discovery will work regardless of any listeners set\nhere. Each listener should be specified in URI format as above, e.g.\ntls://0.0.0.0:0 or tls://[::]:0 to listen on all interfaces."` + Peers []string `comment:"List of outbound peer connection strings (e.g. tls://a.b.c.d:e or\nsocks://a.b.c.d:e/f.g.h.i:j). Connection strings can contain options,\nsee https://yggdrasil-network.github.io/configurationref.html#peers.\nYggdrasil has no concept of bootstrap nodes - all network traffic\nwill transit peer connections. Therefore make sure to only peer with\nnearby nodes that have good connectivity and low latency. Avoid adding\npeers to this list from distant countries as this will worsen your\nnode's connectivity and performance considerably."` + InterfacePeers map[string][]string `comment:"List of connection strings for outbound peer connections in URI format,\narranged by source interface, e.g. { \"eth0\": [ \"tls://a.b.c.d:e\" ] }.\nYou should only use this option if your machine is multi-homed and you\nwant to establish outbound peer connections on different interfaces.\nOtherwise you should use \"Peers\"."` + Listen []string `comment:"Listen addresses for incoming connections. You will need to add\nlisteners in order to accept incoming peerings from non-local nodes.\nThis is not required if you wish to establish outbound peerings only.\nMulticast peer discovery will work regardless of any listeners set\nhere. Each listener should be specified in URI format as above, e.g.\ntls://0.0.0.0:0 or tls://[::]:0 to listen on all interfaces."` AdminListen string `json:",omitempty" 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."` MulticastInterfaces []MulticastInterfaceConfig `comment:"Configuration for which interfaces multicast peer discovery should be\nenabled on. Each entry in the list should be a json object which may\ncontain Regex, Beacon, Listen, and Port. Regex is a regular expression\nwhich is matched against an interface name, and interfaces use the\nfirst configuration that they match gainst. Beacon configures whether\nor not the node should send link-local multicast beacons to advertise\ntheir presence, while listening for incoming connections on Port.\nListen controls whether or not the node listens for multicast beacons\nand opens outgoing connections."` - AllowedPublicKeys []string `comment:"List of peer public keys to allow incoming peering connections\nfrom. If left empty/undefined then all connections will be allowed\nby default. This does not affect outgoing peerings, nor does it\naffect link-local peers discovered via multicast."` + AllowedPublicKeys []string `comment:"List of peer public keys to allow incoming peering connections\nfrom. If left empty/undefined then all connections will be allowed\nby default. This does not affect outgoing peerings, nor does it\naffect link-local peers discovered via multicast.\nWARNING: THIS IS NOT A FIREWALL and DOES NOT limit who can reach\nopen ports or services running on your machine!"` IfName string `comment:"Local network interface name for TUN adapter, or \"auto\" to select\nan interface automatically, or \"none\" to run without TUN."` IfMTU uint64 `comment:"Maximum Transmission Unit (MTU) size for your local TUN interface.\nDefault is the largest supported size for your platform. The lowest\npossible value is 1280."` LogLookups bool `json:",omitempty"` NodeInfoPrivacy bool `comment:"By default, nodeinfo contains some defaults including the platform,\narchitecture and Yggdrasil version. These can help when surveying\nthe network and diagnosing network routing problems. Enabling\nnodeinfo privacy prevents this, so that only items specified in\n\"NodeInfo\" are sent back if specified."` - NodeInfo map[string]interface{} `comment:"Optional node info. This must be a { \"key\": \"value\", ... } map\nor set as null. This is entirely optional but, if set, is visible\nto the whole network on request."` + NodeInfo map[string]interface{} `comment:"Optional nodeinfo. This must be a { \"key\": \"value\", ... } map\nor set as null. This is entirely optional but, if set, is visible\nto the whole network on request."` } type MulticastInterfaceConfig struct { From eef613993fdaa330faeaa187c2d2bca6389c4b86 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 27 Oct 2024 21:06:56 +0000 Subject: [PATCH 261/293] Raise link error when SNI supplied on unsupported link type Closes #1196 --- src/core/link.go | 1 + src/core/link_socks.go | 3 +++ src/core/link_tcp.go | 3 +++ src/core/link_unix.go | 3 +++ src/core/link_ws.go | 3 +++ src/core/link_wss.go | 3 +++ 6 files changed, 16 insertions(+) diff --git a/src/core/link.go b/src/core/link.go index 04fe0266..c2267f24 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -126,6 +126,7 @@ const ErrLinkPinnedKeyInvalid = linkError("pinned public key is invalid") const ErrLinkPasswordInvalid = linkError("invalid password supplied") const ErrLinkUnrecognisedSchema = linkError("link schema unknown") const ErrLinkMaxBackoffInvalid = linkError("max backoff duration invalid") +const ErrLinkSNINotSupported = linkError("SNI not supported on this link type") func (l *links) add(u *url.URL, sintf string, linkType linkType) error { var retErr error diff --git a/src/core/link_socks.go b/src/core/link_socks.go index b92374d4..0f66661b 100644 --- a/src/core/link_socks.go +++ b/src/core/link_socks.go @@ -23,6 +23,9 @@ func (l *links) newLinkSOCKS() *linkSOCKS { } func (l *linkSOCKS) dial(_ context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) { + if url.Scheme != "sockstls" && options.tlsSNI != "" { + return nil, ErrLinkSNINotSupported + } var proxyAuth *proxy.Auth if url.User != nil && url.User.Username() != "" { proxyAuth = &proxy.Auth{ diff --git a/src/core/link_tcp.go b/src/core/link_tcp.go index 4ee50941..38c42def 100644 --- a/src/core/link_tcp.go +++ b/src/core/link_tcp.go @@ -67,6 +67,9 @@ func (l *linkTCP) dialersFor(url *url.URL, info linkInfo) ([]*tcpDialer, error) } func (l *linkTCP) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) { + if options.tlsSNI != "" { + return nil, ErrLinkSNINotSupported + } dialers, err := l.dialersFor(url, info) if err != nil { return nil, err diff --git a/src/core/link_unix.go b/src/core/link_unix.go index ddbfa0ad..1da6931d 100644 --- a/src/core/link_unix.go +++ b/src/core/link_unix.go @@ -31,6 +31,9 @@ func (l *links) newLinkUNIX() *linkUNIX { } func (l *linkUNIX) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) { + if options.tlsSNI != "" { + return nil, ErrLinkSNINotSupported + } addr, err := net.ResolveUnixAddr("unix", url.Path) if err != nil { return nil, err diff --git a/src/core/link_ws.go b/src/core/link_ws.go index 0602ed28..59816098 100644 --- a/src/core/link_ws.go +++ b/src/core/link_ws.go @@ -87,6 +87,9 @@ func (l *links) newLinkWS() *linkWS { } func (l *linkWS) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) { + if options.tlsSNI != "" { + return nil, ErrLinkSNINotSupported + } wsconn, _, err := websocket.Dial(ctx, url.String(), &websocket.DialOptions{ Subprotocols: []string{"ygg-ws"}, }) diff --git a/src/core/link_wss.go b/src/core/link_wss.go index 0bdb4f3a..f09d7955 100644 --- a/src/core/link_wss.go +++ b/src/core/link_wss.go @@ -27,6 +27,9 @@ func (l *links) newLinkWSS() *linkWSS { } func (l *linkWSS) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) { + if options.tlsSNI != "" { + return nil, ErrLinkSNINotSupported + } wsconn, _, err := websocket.Dial(ctx, url.String(), &websocket.DialOptions{ Subprotocols: []string{"ygg-ws"}, }) From 834680045a9738df7940cfbab74ccc5e94ff6509 Mon Sep 17 00:00:00 2001 From: Klemens Nanni Date: Mon, 11 Nov 2024 22:27:02 +0300 Subject: [PATCH 262/293] Create admin socket synchronously before privdrop (#1201) Creating UNIX sockets the listen() goroutine that races against the main one dropping to an unprivileged user may cause startup failure when privdrop happens before privileged filesystem access. Setup or fail in New() and only do listen(2) in listen() to avoid this. ``` # yggdrasil -autoconf -user nobody 2024/11/03 21:15:27 Build name: yggdrasil-go 2024/11/03 21:15:27 Build version: 0.5.9 ... 2024/11/03 21:15:27 Admin socket failed to listen: listen unix /var/run/yggdrasil.sock: bind: permission denied ``` Rerun, now the order is flipped: ``` # yggdrasil -autoconf -user nobody 2024/11/03 21:15:34 Build name: yggdrasil-go 2024/11/03 21:15:34 Build version: 0.5.9 [...] 2024/11/03 21:15:34 UNIX admin socket listening on /var/run/yggdrasil.sock [...] ``` Fixes #927. --- src/admin/admin.go | 90 +++++++++++++++++++++++----------------------- 1 file changed, 46 insertions(+), 44 deletions(-) diff --git a/src/admin/admin.go b/src/admin/admin.go index 8823fc5e..996cabd5 100644 --- a/src/admin/admin.go +++ b/src/admin/admin.go @@ -83,6 +83,52 @@ func New(c *core.Core, log core.Logger, opts ...SetupOption) (*AdminSocket, erro if a.config.listenaddr == "none" || a.config.listenaddr == "" { return nil, nil } + + listenaddr := string(a.config.listenaddr) + u, err := url.Parse(listenaddr) + if err == nil { + switch strings.ToLower(u.Scheme) { + case "unix": + if _, err := os.Stat(u.Path); err == nil { + a.log.Debugln("Admin socket", u.Path, "already exists, trying to clean up") + if _, err := net.DialTimeout("unix", u.Path, time.Second*2); err == nil || err.(net.Error).Timeout() { + a.log.Errorln("Admin socket", u.Path, "already exists and is in use by another process") + os.Exit(1) + } else { + if err := os.Remove(u.Path); err == nil { + a.log.Debugln(u.Path, "was cleaned up") + } else { + a.log.Errorln(u.Path, "already exists and was not cleaned up:", err) + os.Exit(1) + } + } + } + a.listener, err = net.Listen("unix", u.Path) + if err == nil { + switch u.Path[:1] { + case "@": // maybe abstract namespace + default: + if err := os.Chmod(u.Path, 0660); err != nil { + a.log.Warnln("WARNING:", u.Path, "may have unsafe permissions!") + } + } + } + case "tcp": + a.listener, err = net.Listen("tcp", u.Host) + default: + a.listener, err = net.Listen("tcp", listenaddr) + } + } else { + a.listener, err = net.Listen("tcp", listenaddr) + } + if err != nil { + a.log.Errorf("Admin socket failed to listen: %v", err) + os.Exit(1) + } + a.log.Infof("%s admin socket listening on %s", + strings.ToUpper(a.listener.Addr().Network()), + a.listener.Addr().String()) + _ = a.AddHandler("list", "List available commands", []string{}, func(_ json.RawMessage) (interface{}, error) { res := &ListResponse{} for name, handler := range a.handlers { @@ -233,50 +279,6 @@ func (a *AdminSocket) Stop() error { // listen is run by start and manages API connections. func (a *AdminSocket) listen() { - listenaddr := string(a.config.listenaddr) - u, err := url.Parse(listenaddr) - if err == nil { - switch strings.ToLower(u.Scheme) { - case "unix": - if _, err := os.Stat(u.Path); err == nil { - a.log.Debugln("Admin socket", u.Path, "already exists, trying to clean up") - if _, err := net.DialTimeout("unix", u.Path, time.Second*2); err == nil || err.(net.Error).Timeout() { - a.log.Errorln("Admin socket", u.Path, "already exists and is in use by another process") - os.Exit(1) - } else { - if err := os.Remove(u.Path); err == nil { - a.log.Debugln(u.Path, "was cleaned up") - } else { - a.log.Errorln(u.Path, "already exists and was not cleaned up:", err) - os.Exit(1) - } - } - } - a.listener, err = net.Listen("unix", u.Path) - if err == nil { - switch u.Path[:1] { - case "@": // maybe abstract namespace - default: - if err := os.Chmod(u.Path, 0660); err != nil { - a.log.Warnln("WARNING:", u.Path, "may have unsafe permissions!") - } - } - } - case "tcp": - a.listener, err = net.Listen("tcp", u.Host) - default: - a.listener, err = net.Listen("tcp", listenaddr) - } - } else { - a.listener, err = net.Listen("tcp", listenaddr) - } - if err != nil { - a.log.Errorf("Admin socket failed to listen: %v", err) - os.Exit(1) - } - a.log.Infof("%s admin socket listening on %s", - strings.ToUpper(a.listener.Addr().Network()), - a.listener.Addr().String()) defer a.listener.Close() for { conn, err := a.listener.Accept() From 75d2080e53d8b2e98fb471bcc4e561d08ad7f7bc Mon Sep 17 00:00:00 2001 From: Klemens Nanni Date: Mon, 11 Nov 2024 22:28:28 +0300 Subject: [PATCH 263/293] Set groups when dropping privileges to not leak supplementary group access (#1202) Changing the real and effective user/group IDs and the saved set-user/group-ID is not enough to get rid of intial access permissions. The list of groups must be cleared also, otherwise a process changing from, e.g. `root:root` to `nobody:nobody` retains rights to access `:wheel` files (assuming `root` is a member of the `wheel` group). For example: ``` # id uid=0(root) gid=0(wheel) groups=0(wheel), 2(kmem), 3(sys), 4(tty), 5(operator), 20(staff), 31(guest) # ./yggdrasil -autoconf -logto /dev/null -user nobody & [1] 4337 # ps -o command,user,group,supgrp -U nobody COMMAND USER GROUP SUPGRP ./yggdrasil -aut nobody nobody wheel,kmem,sys,tty,operator,staff,guest ``` Fix that so the process runs as mere ``` COMMAND USER GROUP SUPGRP ./yggdrasil -aut nobody nobody nobody ``` Fixes #927. --- cmd/yggdrasil/chuser_unix.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cmd/yggdrasil/chuser_unix.go b/cmd/yggdrasil/chuser_unix.go index 6e802c69..69803773 100644 --- a/cmd/yggdrasil/chuser_unix.go +++ b/cmd/yggdrasil/chuser_unix.go @@ -53,6 +53,9 @@ func chuser(user string) error { gid, _ := strconv.ParseUint(g.Gid, 10, 32) var err error if gid < math.MaxInt { + if err := syscall.Setgroups([]int{int(gid)}); err != nil { + return fmt.Errorf("failed to setgroups %d: %v", gid, err) + } err = syscall.Setgid(int(gid)) } else { err = errors.New("gid too big") @@ -63,6 +66,9 @@ func chuser(user string) error { } } else if u != nil { gid, _ := strconv.ParseUint(u.Gid, 10, 32) + if err := syscall.Setgroups([]int{int(uint32(gid))}); err != nil { + return fmt.Errorf("failed to setgroups %d: %v", gid, err) + } err := syscall.Setgid(int(uint32(gid))) if err != nil { return fmt.Errorf("failed to setgid %d: %v", gid, err) From 42873be09b5912cfbff0053fa94a1cb03112e0cf Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 16 Nov 2024 22:59:03 +0000 Subject: [PATCH 264/293] Reusable peer lookup/dial logic --- cmd/yggdrasil/main.go | 7 ++++++ contrib/mobile/mobile.go | 10 +++++++- src/core/core.go | 1 + src/core/link.go | 38 ++++++++++++++++++++++++++++ src/core/link_quic.go | 29 ++++++++++++--------- src/core/link_socks.go | 46 ++++++++++++++++++++-------------- src/core/link_tcp.go | 54 +++++----------------------------------- src/core/link_tls.go | 36 +++++++++++++-------------- src/core/link_unix.go | 3 --- src/core/link_ws.go | 40 +++++++++++++++++++++-------- src/core/link_wss.go | 48 ++++++++++++++++++++++++++--------- src/core/options.go | 5 ++++ 12 files changed, 193 insertions(+), 124 deletions(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 0ae8ab42..3ec6414c 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -191,9 +191,16 @@ func main() { // Set up the Yggdrasil node itself. { + iprange := net.IPNet{ + IP: net.ParseIP("200::"), + Mask: net.CIDRMask(7, 128), + } options := []core.SetupOption{ core.NodeInfo(cfg.NodeInfo), core.NodeInfoPrivacy(cfg.NodeInfoPrivacy), + core.PeerFilter(func(ip net.IP) bool { + return !iprange.Contains(ip) + }), } for _, addr := range cfg.Listen { options = append(options, core.ListenAddress(addr)) diff --git a/contrib/mobile/mobile.go b/contrib/mobile/mobile.go index 06f48027..abc89f1c 100644 --- a/contrib/mobile/mobile.go +++ b/contrib/mobile/mobile.go @@ -53,7 +53,15 @@ func (m *Yggdrasil) StartJSON(configjson []byte) error { } // Set up the Yggdrasil node itself. { - options := []core.SetupOption{} + iprange := net.IPNet{ + IP: net.ParseIP("200::"), + Mask: net.CIDRMask(7, 128), + } + options := []core.SetupOption{ + core.PeerFilter(func(ip net.IP) bool { + return !iprange.Contains(ip) + }), + } for _, peer := range m.config.Peers { options = append(options, core.Peer{URI: peer}) } diff --git a/src/core/core.go b/src/core/core.go index 2b206ee1..a7f9fe96 100644 --- a/src/core/core.go +++ b/src/core/core.go @@ -40,6 +40,7 @@ type Core struct { tls *tls.Config // immutable after startup //_peers map[Peer]*linkInfo // configurable after startup _listeners map[ListenAddress]struct{} // configurable after startup + peerFilter func(ip net.IP) bool // immutable after startup nodeinfo NodeInfo // immutable after startup nodeinfoPrivacy NodeInfoPrivacy // immutable after startup _allowedPublicKeys map[[32]byte]struct{} // configurable after startup diff --git a/src/core/link.go b/src/core/link.go index c2267f24..d7e5b110 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -127,6 +127,7 @@ const ErrLinkPasswordInvalid = linkError("invalid password supplied") const ErrLinkUnrecognisedSchema = linkError("link schema unknown") const ErrLinkMaxBackoffInvalid = linkError("max backoff duration invalid") const ErrLinkSNINotSupported = linkError("SNI not supported on this link type") +const ErrLinkNoSuitableIPs = linkError("no suitable remote IPs") func (l *links) add(u *url.URL, sintf string, linkType linkType) error { var retErr error @@ -653,6 +654,43 @@ func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn, s return err } +func (l *links) findSuitableIP(url *url.URL, fn func(hostname string, ip net.IP, port int) (net.Conn, error)) (net.Conn, error) { + host, p, err := net.SplitHostPort(url.Host) + if err != nil { + return nil, err + } + port, err := strconv.Atoi(p) + if err != nil { + return nil, err + } + resp, err := net.LookupIP(host) + if err != nil { + return nil, err + } + var _ips [64]net.IP + ips := _ips[:0] + for _, ip := range resp { + if l.core.config.peerFilter != nil && !l.core.config.peerFilter(ip) { + continue + } + ips = append(ips, ip) + } + if len(ips) == 0 { + return nil, ErrLinkNoSuitableIPs + } + for _, ip := range ips { + var conn net.Conn + if conn, err = fn(host, ip, port); err != nil { + url := *url + url.RawQuery = "" + l.core.log.Debugln("Dialling", url.Redacted(), "reported error:", err) + continue + } + return conn, nil + } + return nil, err +} + func urlForLinkInfo(u url.URL) url.URL { u.RawQuery = "" return u diff --git a/src/core/link_quic.go b/src/core/link_quic.go index 9ad5456d..ffb69a6d 100644 --- a/src/core/link_quic.go +++ b/src/core/link_quic.go @@ -51,18 +51,23 @@ func (l *links) newLinkQUIC() *linkQUIC { } func (l *linkQUIC) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) { - qc, err := quic.DialAddr(ctx, url.Host, l.tlsconfig, l.quicconfig) - if err != nil { - return nil, err - } - qs, err := qc.OpenStreamSync(ctx) - if err != nil { - return nil, err - } - return &linkQUICStream{ - Connection: qc, - Stream: qs, - }, nil + tlsconfig := l.tlsconfig.Clone() + return l.links.findSuitableIP(url, func(hostname string, ip net.IP, port int) (net.Conn, error) { + tlsconfig.ServerName = hostname + hostport := net.JoinHostPort(ip.String(), fmt.Sprintf("%d", port)) + qc, err := quic.DialAddr(ctx, hostport, l.tlsconfig, l.quicconfig) + if err != nil { + return nil, err + } + qs, err := qc.OpenStreamSync(ctx) + if err != nil { + return nil, err + } + return &linkQUICStream{ + Connection: qc, + Stream: qs, + }, nil + }) } func (l *linkQUIC) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) { diff --git a/src/core/link_socks.go b/src/core/link_socks.go index 0f66661b..f33cd190 100644 --- a/src/core/link_socks.go +++ b/src/core/link_socks.go @@ -23,9 +23,6 @@ func (l *links) newLinkSOCKS() *linkSOCKS { } func (l *linkSOCKS) dial(_ context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) { - if url.Scheme != "sockstls" && options.tlsSNI != "" { - return nil, ErrLinkSNINotSupported - } var proxyAuth *proxy.Auth if url.User != nil && url.User.Username() != "" { proxyAuth = &proxy.Auth{ @@ -33,21 +30,34 @@ func (l *linkSOCKS) dial(_ context.Context, url *url.URL, info linkInfo, options } proxyAuth.Password, _ = url.User.Password() } - dialer, err := proxy.SOCKS5("tcp", url.Host, proxyAuth, proxy.Direct) - if err != nil { - return nil, fmt.Errorf("failed to configure proxy") - } - pathtokens := strings.Split(strings.Trim(url.Path, "/"), "/") - conn, err := dialer.Dial("tcp", pathtokens[0]) - if err != nil { - return nil, fmt.Errorf("failed to dial: %w", err) - } - if url.Scheme == "sockstls" { - tlsconfig := l.tls.config.Clone() - tlsconfig.ServerName = options.tlsSNI - conn = tls.Client(conn, tlsconfig) - } - return conn, nil + tlsconfig := l.tls.config.Clone() + return l.links.findSuitableIP(url, func(hostname string, ip net.IP, port int) (net.Conn, error) { + hostport := net.JoinHostPort(ip.String(), fmt.Sprintf("%d", port)) + dialer, err := l.tcp.dialerFor(&net.TCPAddr{ + IP: ip, + Port: port, + }, info.sintf) + if err != nil { + return nil, err + } + proxy, err := proxy.SOCKS5("tcp", hostport, proxyAuth, dialer) + if err != nil { + return nil, err + } + pathtokens := strings.Split(strings.Trim(url.Path, "/"), "/") + conn, err := proxy.Dial("tcp", pathtokens[0]) + if err != nil { + return nil, err + } + if url.Scheme == "sockstls" { + tlsconfig.ServerName = hostname + if sni := options.tlsSNI; sni != "" { + tlsconfig.ServerName = sni + } + conn = tls.Client(conn, tlsconfig) + } + return conn, nil + }) } func (l *linkSOCKS) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) { diff --git a/src/core/link_tcp.go b/src/core/link_tcp.go index 38c42def..3315f5c7 100644 --- a/src/core/link_tcp.go +++ b/src/core/link_tcp.go @@ -5,7 +5,6 @@ import ( "fmt" "net" "net/url" - "strconv" "time" "github.com/Arceliar/phony" @@ -34,59 +33,18 @@ type tcpDialer struct { addr *net.TCPAddr } -func (l *linkTCP) dialersFor(url *url.URL, info linkInfo) ([]*tcpDialer, error) { - host, p, err := net.SplitHostPort(url.Host) - if err != nil { - return nil, err - } - port, err := strconv.Atoi(p) - if err != nil { - return nil, err - } - ips, err := net.LookupIP(host) - if err != nil { - return nil, err - } - dialers := make([]*tcpDialer, 0, len(ips)) - for _, ip := range ips { +func (l *linkTCP) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) { + return l.links.findSuitableIP(url, func(hostname string, ip net.IP, port int) (net.Conn, error) { addr := &net.TCPAddr{ IP: ip, Port: port, } - dialer, err := l.dialerFor(addr, info.sintf) + dialer, err := l.tcp.dialerFor(addr, info.sintf) if err != nil { - continue + return nil, err } - dialers = append(dialers, &tcpDialer{ - info: info, - dialer: dialer, - addr: addr, - }) - } - return dialers, nil -} - -func (l *linkTCP) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) { - if options.tlsSNI != "" { - return nil, ErrLinkSNINotSupported - } - dialers, err := l.dialersFor(url, info) - if err != nil { - return nil, err - } - if len(dialers) == 0 { - return nil, nil - } - for _, d := range dialers { - var conn net.Conn - conn, err = d.dialer.DialContext(ctx, "tcp", d.addr.String()) - if err != nil { - l.core.log.Warnf("Failed to connect to %s: %s", d.addr, err) - continue - } - return conn, nil - } - return nil, err + return dialer.DialContext(ctx, "tcp", addr.String()) + }) } func (l *linkTCP) listen(ctx context.Context, url *url.URL, sintf string) (net.Listener, error) { diff --git a/src/core/link_tls.go b/src/core/link_tls.go index d51d0ce5..da3c7791 100644 --- a/src/core/link_tls.go +++ b/src/core/link_tls.go @@ -32,28 +32,26 @@ func (l *links) newLinkTLS(tcp *linkTCP) *linkTLS { } func (l *linkTLS) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) { - dialers, err := l.tcp.dialersFor(url, info) - if err != nil { - return nil, err - } - if len(dialers) == 0 { - return nil, nil - } - for _, d := range dialers { - tlsconfig := l.config.Clone() - tlsconfig.ServerName = options.tlsSNI + tlsconfig := l.config.Clone() + return l.links.findSuitableIP(url, func(hostname string, ip net.IP, port int) (net.Conn, error) { + tlsconfig.ServerName = hostname + if sni := options.tlsSNI; sni != "" { + tlsconfig.ServerName = sni + } + addr := &net.TCPAddr{ + IP: ip, + Port: port, + } + dialer, err := l.tcp.dialerFor(addr, info.sintf) + if err != nil { + return nil, err + } tlsdialer := &tls.Dialer{ - NetDialer: d.dialer, + NetDialer: dialer, Config: tlsconfig, } - var conn net.Conn - conn, err = tlsdialer.DialContext(ctx, "tcp", d.addr.String()) - if err != nil { - continue - } - return conn, nil - } - return nil, err + return tlsdialer.DialContext(ctx, "tcp", addr.String()) + }) } func (l *linkTLS) listen(ctx context.Context, url *url.URL, sintf string) (net.Listener, error) { diff --git a/src/core/link_unix.go b/src/core/link_unix.go index 1da6931d..ddbfa0ad 100644 --- a/src/core/link_unix.go +++ b/src/core/link_unix.go @@ -31,9 +31,6 @@ func (l *links) newLinkUNIX() *linkUNIX { } func (l *linkUNIX) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) { - if options.tlsSNI != "" { - return nil, ErrLinkSNINotSupported - } addr, err := net.ResolveUnixAddr("unix", url.Path) if err != nil { return nil, err diff --git a/src/core/link_ws.go b/src/core/link_ws.go index 59816098..86f065a6 100644 --- a/src/core/link_ws.go +++ b/src/core/link_ws.go @@ -2,6 +2,7 @@ package core import ( "context" + "fmt" "net" "net/http" "net/url" @@ -87,18 +88,35 @@ func (l *links) newLinkWS() *linkWS { } func (l *linkWS) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) { - if options.tlsSNI != "" { - return nil, ErrLinkSNINotSupported - } - wsconn, _, err := websocket.Dial(ctx, url.String(), &websocket.DialOptions{ - Subprotocols: []string{"ygg-ws"}, + return l.links.findSuitableIP(url, func(hostname string, ip net.IP, port int) (net.Conn, error) { + u := *url + u.Host = net.JoinHostPort(ip.String(), fmt.Sprintf("%d", port)) + addr := &net.TCPAddr{ + IP: ip, + Port: port, + } + dialer, err := l.tcp.dialerFor(addr, info.sintf) + if err != nil { + return nil, err + } + wsconn, _, err := websocket.Dial(ctx, u.String(), &websocket.DialOptions{ + HTTPClient: &http.Client{ + Transport: &http.Transport{ + Proxy: http.ProxyFromEnvironment, + Dial: dialer.Dial, + DialContext: dialer.DialContext, + }, + }, + Subprotocols: []string{"ygg-ws"}, + Host: hostname, + }) + if err != nil { + return nil, err + } + return &linkWSConn{ + Conn: websocket.NetConn(ctx, wsconn, websocket.MessageBinary), + }, nil }) - if err != nil { - return nil, err - } - return &linkWSConn{ - Conn: websocket.NetConn(ctx, wsconn, websocket.MessageBinary), - }, nil } func (l *linkWS) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) { diff --git a/src/core/link_wss.go b/src/core/link_wss.go index f09d7955..4d968c2f 100644 --- a/src/core/link_wss.go +++ b/src/core/link_wss.go @@ -2,8 +2,10 @@ package core import ( "context" + "crypto/tls" "fmt" "net" + "net/http" "net/url" "github.com/Arceliar/phony" @@ -13,6 +15,7 @@ import ( type linkWSS struct { phony.Inbox *links + tlsconfig *tls.Config } type linkWSSConn struct { @@ -21,24 +24,45 @@ type linkWSSConn struct { func (l *links) newLinkWSS() *linkWSS { lwss := &linkWSS{ - links: l, + links: l, + tlsconfig: l.core.config.tls.Clone(), } return lwss } func (l *linkWSS) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) { - if options.tlsSNI != "" { - return nil, ErrLinkSNINotSupported - } - wsconn, _, err := websocket.Dial(ctx, url.String(), &websocket.DialOptions{ - Subprotocols: []string{"ygg-ws"}, + tlsconfig := l.tlsconfig.Clone() + return l.links.findSuitableIP(url, func(hostname string, ip net.IP, port int) (net.Conn, error) { + tlsconfig.ServerName = hostname + u := *url + u.Host = net.JoinHostPort(ip.String(), fmt.Sprintf("%d", port)) + addr := &net.TCPAddr{ + IP: ip, + Port: port, + } + dialer, err := l.tcp.dialerFor(addr, info.sintf) + if err != nil { + return nil, err + } + wsconn, _, err := websocket.Dial(ctx, u.String(), &websocket.DialOptions{ + HTTPClient: &http.Client{ + Transport: &http.Transport{ + Proxy: http.ProxyFromEnvironment, + Dial: dialer.Dial, + DialContext: dialer.DialContext, + TLSClientConfig: tlsconfig, + }, + }, + Subprotocols: []string{"ygg-ws"}, + Host: hostname, + }) + if err != nil { + return nil, err + } + return &linkWSConn{ + Conn: websocket.NetConn(ctx, wsconn, websocket.MessageBinary), + }, nil }) - if err != nil { - return nil, err - } - return &linkWSSConn{ - Conn: websocket.NetConn(ctx, wsconn, websocket.MessageBinary), - }, nil } func (l *linkWSS) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) { diff --git a/src/core/options.go b/src/core/options.go index 581c033b..7e67bfb4 100644 --- a/src/core/options.go +++ b/src/core/options.go @@ -3,6 +3,7 @@ package core import ( "crypto/ed25519" "fmt" + "net" "net/url" ) @@ -24,6 +25,8 @@ func (c *Core) _applyOption(opt SetupOption) (err error) { } case ListenAddress: c.config._listeners[v] = struct{}{} + case PeerFilter: + c.config.peerFilter = v case NodeInfo: c.config.nodeinfo = v case NodeInfoPrivacy: @@ -48,9 +51,11 @@ type Peer struct { type NodeInfo map[string]interface{} type NodeInfoPrivacy bool type AllowedPublicKey ed25519.PublicKey +type PeerFilter func(net.IP) bool func (a ListenAddress) isSetupOption() {} func (a Peer) isSetupOption() {} func (a NodeInfo) isSetupOption() {} func (a NodeInfoPrivacy) isSetupOption() {} func (a AllowedPublicKey) isSetupOption() {} +func (a PeerFilter) isSetupOption() {} From 67ec5a92b33089d1b821717d6e9f96e28f06fe26 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 17 Nov 2024 21:29:26 +0000 Subject: [PATCH 265/293] Fix some lint issues --- src/core/link_tcp.go | 6 ------ src/core/link_wss.go | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/core/link_tcp.go b/src/core/link_tcp.go index 3315f5c7..e50912d3 100644 --- a/src/core/link_tcp.go +++ b/src/core/link_tcp.go @@ -27,12 +27,6 @@ func (l *links) newLinkTCP() *linkTCP { return lt } -type tcpDialer struct { - info linkInfo - dialer *net.Dialer - addr *net.TCPAddr -} - func (l *linkTCP) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) { return l.links.findSuitableIP(url, func(hostname string, ip net.IP, port int) (net.Conn, error) { addr := &net.TCPAddr{ diff --git a/src/core/link_wss.go b/src/core/link_wss.go index 4d968c2f..1a8d571f 100644 --- a/src/core/link_wss.go +++ b/src/core/link_wss.go @@ -59,7 +59,7 @@ func (l *linkWSS) dial(ctx context.Context, url *url.URL, info linkInfo, options if err != nil { return nil, err } - return &linkWSConn{ + return &linkWSSConn{ Conn: websocket.NetConn(ctx, wsconn, websocket.MessageBinary), }, nil }) From c22a746a1d166b0060ce4b0b0496b82a1a96e29b Mon Sep 17 00:00:00 2001 From: Klemens Nanni Date: Mon, 18 Nov 2024 00:37:07 +0300 Subject: [PATCH 266/293] Rewrite chuser() for simplicity and correctness (#1203) - Use unambiguous variable names (w/o package name conflict). - Fail on invalid input such as the empty string or `:`. - Do not change group without user, i.e. fail on `:group`. - Parse input using mnemonic APIs. - Do not juggle between integer types. - Unset supplementary groups. - Use set[ug]id(2) to follow the idiom of OpenBSD base programs. (cannot use setres[ug]id(2) as macOS does not have them.) Includes/Supersedes #1202. Fixes #927. I only tested on OpenBSD (so far), but other systems should just work. --- cmd/yggdrasil/chuser_unix.go | 98 ++++++++++--------------------- cmd/yggdrasil/chuser_unix_test.go | 80 +++++++++++++++++++++++++ 2 files changed, 111 insertions(+), 67 deletions(-) create mode 100644 cmd/yggdrasil/chuser_unix_test.go diff --git a/cmd/yggdrasil/chuser_unix.go b/cmd/yggdrasil/chuser_unix.go index 69803773..fc3e5c2c 100644 --- a/cmd/yggdrasil/chuser_unix.go +++ b/cmd/yggdrasil/chuser_unix.go @@ -4,89 +4,53 @@ package main import ( - "errors" "fmt" - "math" - osuser "os/user" + "os/user" "strconv" "strings" - "syscall" + + "golang.org/x/sys/unix" ) -func chuser(user string) error { - group := "" - if i := strings.IndexByte(user, ':'); i >= 0 { - user, group = user[:i], user[i+1:] - } +func chuser(input string) error { + givenUser, givenGroup, _ := strings.Cut(input, ":") - u := (*osuser.User)(nil) - g := (*osuser.Group)(nil) + var ( + err error + usr *user.User + grp *user.Group + uid, gid int + ) - if user != "" { - if _, err := strconv.ParseUint(user, 10, 32); err == nil { - u, err = osuser.LookupId(user) - if err != nil { - return fmt.Errorf("failed to lookup user by id %q: %v", user, err) - } - } else { - u, err = osuser.Lookup(user) - if err != nil { - return fmt.Errorf("failed to lookup user by name %q: %v", user, err) - } + if usr, err = user.LookupId(givenUser); err != nil { + if usr, err = user.Lookup(givenUser); err != nil { + return err } } - if group != "" { - if _, err := strconv.ParseUint(group, 10, 32); err == nil { - g, err = osuser.LookupGroupId(group) - if err != nil { - return fmt.Errorf("failed to lookup group by id %q: %v", user, err) - } - } else { - g, err = osuser.LookupGroup(group) - if err != nil { - return fmt.Errorf("failed to lookup group by name %q: %v", user, err) - } - } + if uid, err = strconv.Atoi(usr.Uid); err != nil { + return err } - if g != nil { - gid, _ := strconv.ParseUint(g.Gid, 10, 32) - var err error - if gid < math.MaxInt { - if err := syscall.Setgroups([]int{int(gid)}); err != nil { - return fmt.Errorf("failed to setgroups %d: %v", gid, err) + if givenGroup != "" { + if grp, err = user.LookupGroupId(givenGroup); err != nil { + if grp, err = user.LookupGroup(givenGroup); err != nil { + return err } - err = syscall.Setgid(int(gid)) - } else { - err = errors.New("gid too big") } - if err != nil { - return fmt.Errorf("failed to setgid %d: %v", gid, err) - } - } else if u != nil { - gid, _ := strconv.ParseUint(u.Gid, 10, 32) - if err := syscall.Setgroups([]int{int(uint32(gid))}); err != nil { - return fmt.Errorf("failed to setgroups %d: %v", gid, err) - } - err := syscall.Setgid(int(uint32(gid))) - if err != nil { - return fmt.Errorf("failed to setgid %d: %v", gid, err) - } + gid, _ = strconv.Atoi(grp.Gid) + } else { + gid, _ = strconv.Atoi(usr.Gid) } - if u != nil { - uid, _ := strconv.ParseUint(u.Uid, 10, 32) - var err error - if uid < math.MaxInt { - err = syscall.Setuid(int(uid)) - } else { - err = errors.New("uid too big") - } - - if err != nil { - return fmt.Errorf("failed to setuid %d: %v", uid, err) - } + if err := unix.Setgroups([]int{gid}); err != nil { + return fmt.Errorf("setgroups: %d: %v", gid, err) + } + if err := unix.Setgid(gid); err != nil { + return fmt.Errorf("setgid: %d: %v", gid, err) + } + if err := unix.Setuid(uid); err != nil { + return fmt.Errorf("setuid: %d: %v", uid, err) } return nil diff --git a/cmd/yggdrasil/chuser_unix_test.go b/cmd/yggdrasil/chuser_unix_test.go new file mode 100644 index 00000000..ad2e3517 --- /dev/null +++ b/cmd/yggdrasil/chuser_unix_test.go @@ -0,0 +1,80 @@ +//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris + +package main + +import ( + "testing" + "os/user" +) + +// Usernames must not contain a number sign. +func TestEmptyString (t *testing.T) { + if chuser("") == nil { + t.Fatal("the empty string is not a valid user") + } +} + +// Either omit delimiter and group, or omit both. +func TestEmptyGroup (t *testing.T) { + if chuser("0:") == nil { + t.Fatal("the empty group is not allowed") + } +} + +// Either user only or user and group. +func TestGroupOnly (t *testing.T) { + if chuser(":0") == nil { + t.Fatal("group only is not allowed") + } +} + +// Usenames must not contain the number sign. +func TestInvalidUsername (t *testing.T) { + const username = "#user" + if chuser(username) == nil { + t.Fatalf("'%s' is not a valid username", username) + } +} + +// User IDs must be non-negative. +func TestInvalidUserid (t *testing.T) { + if chuser("-1") == nil { + t.Fatal("User ID cannot be negative") + } +} + +// Change to the current user by ID. +func TestCurrentUserid (t *testing.T) { + usr, err := user.Current() + if err != nil { + t.Fatal(err) + } + + if usr.Uid != "0" { + t.Skip("setgroups(2): Only the superuser may set new groups.") + } + + if err = chuser(usr.Uid); err != nil { + t.Fatal(err) + } +} + +// Change to a common user by name. +func TestCommonUsername (t *testing.T) { + usr, err := user.Current() + if err != nil { + t.Fatal(err) + } + + if usr.Uid != "0" { + t.Skip("setgroups(2): Only the superuser may set new groups.") + } + + if err := chuser("nobody"); err != nil { + if _, ok := err.(user.UnknownUserError); ok { + t.Skip(err) + } + t.Fatal(err) + } +} From 9398cae230170990b5b048f63a22653a714e30ee Mon Sep 17 00:00:00 2001 From: Neil Date: Tue, 19 Nov 2024 08:42:27 +0000 Subject: [PATCH 267/293] Expose download/upload rate per peer (#1206) --- cmd/yggdrasilctl/main.go | 12 ++++++++++-- src/admin/admin.go | 16 +++++++++------- src/admin/getpeers.go | 4 ++++ src/core/api.go | 4 ++++ src/core/link.go | 37 ++++++++++++++++++++++++++++++++++--- 5 files changed, 61 insertions(+), 12 deletions(-) diff --git a/cmd/yggdrasilctl/main.go b/cmd/yggdrasilctl/main.go index 8a30f438..b73708e6 100644 --- a/cmd/yggdrasilctl/main.go +++ b/cmd/yggdrasilctl/main.go @@ -174,9 +174,9 @@ func run() int { if err := json.Unmarshal(recv.Response, &resp); err != nil { panic(err) } - table.SetHeader([]string{"URI", "State", "Dir", "IP Address", "Uptime", "RTT", "RX", "TX", "Pr", "Cost", "Last Error"}) + table.SetHeader([]string{"URI", "State", "Dir", "IP Address", "Uptime", "RTT", "RX", "TX", "Down", "Up", "Pr", "Cost", "Last Error"}) for _, peer := range resp.Peers { - state, lasterr, dir, rtt := "Up", "-", "Out", "-" + state, lasterr, dir, rtt, rxr, txr := "Up", "-", "Out", "-", "-", "-" if !peer.Up { state, lasterr = "Down", fmt.Sprintf("%s ago: %s", peer.LastErrorTime.Round(time.Second), peer.LastError) } else if rttms := float64(peer.Latency.Microseconds()) / 1000; rttms > 0 { @@ -190,6 +190,12 @@ func run() int { uri.RawQuery = "" uristring = uri.String() } + if peer.RXRate > 0 { + rxr = peer.RXRate.String() + "/s" + } + if peer.TXRate > 0 { + txr = peer.TXRate.String() + "/s" + } table.Append([]string{ uristring, state, @@ -199,6 +205,8 @@ func run() int { rtt, peer.RXBytes.String(), peer.TXBytes.String(), + rxr, + txr, fmt.Sprintf("%d", peer.Priority), fmt.Sprintf("%d", peer.Cost), lasterr, diff --git a/src/admin/admin.go b/src/admin/admin.go index 996cabd5..54c1a124 100644 --- a/src/admin/admin.go +++ b/src/admin/admin.go @@ -356,13 +356,15 @@ type DataUnit uint64 func (d DataUnit) String() string { switch { - case d > 1024*1024*1024*1024: - return fmt.Sprintf("%2.ftb", float64(d)/1024/1024/1024/1024) - case d > 1024*1024*1024: - return fmt.Sprintf("%2.fgb", float64(d)/1024/1024/1024) - case d > 1024*1024: - return fmt.Sprintf("%2.fmb", float64(d)/1024/1024) + case d >= 1024*1024*1024*1024: + return fmt.Sprintf("%2.1fTB", float64(d)/1024/1024/1024/1024) + case d >= 1024*1024*1024: + return fmt.Sprintf("%2.1fGB", float64(d)/1024/1024/1024) + case d >= 1024*1024: + return fmt.Sprintf("%2.1fMB", float64(d)/1024/1024) + case d >= 100: + return fmt.Sprintf("%2.1fKB", float64(d)/1024) default: - return fmt.Sprintf("%2.fkb", float64(d)/1024) + return fmt.Sprintf("%dB", d) } } diff --git a/src/admin/getpeers.go b/src/admin/getpeers.go index 2c2f8d8a..34eca243 100644 --- a/src/admin/getpeers.go +++ b/src/admin/getpeers.go @@ -28,6 +28,8 @@ type PeerEntry struct { Cost uint64 `json:"cost"` RXBytes DataUnit `json:"bytes_recvd,omitempty"` TXBytes DataUnit `json:"bytes_sent,omitempty"` + RXRate DataUnit `json:"rate_recvd,omitempty"` + TXRate DataUnit `json:"rate_sent,omitempty"` Uptime float64 `json:"uptime,omitempty"` Latency time.Duration `json:"latency_ms,omitempty"` LastErrorTime time.Duration `json:"last_error_time,omitempty"` @@ -47,6 +49,8 @@ func (a *AdminSocket) getPeersHandler(_ *GetPeersRequest, res *GetPeersResponse) URI: p.URI, RXBytes: DataUnit(p.RXBytes), TXBytes: DataUnit(p.TXBytes), + RXRate: DataUnit(p.RXRate), + TXRate: DataUnit(p.TXRate), Uptime: p.Uptime.Seconds(), } if p.Latency > 0 { diff --git a/src/core/api.go b/src/core/api.go index 2aa1ba87..cc1bde32 100644 --- a/src/core/api.go +++ b/src/core/api.go @@ -33,6 +33,8 @@ type PeerInfo struct { Cost uint64 RXBytes uint64 TXBytes uint64 + RXRate uint64 + TXRate uint64 Uptime time.Duration Latency time.Duration } @@ -87,6 +89,8 @@ func (c *Core) GetPeers() []PeerInfo { peerinfo.Inbound = state.linkType == linkTypeIncoming peerinfo.RXBytes = atomic.LoadUint64(&c.rx) peerinfo.TXBytes = atomic.LoadUint64(&c.tx) + peerinfo.RXRate = atomic.LoadUint64(&c.rxrate) + peerinfo.TXRate = atomic.LoadUint64(&c.txrate) peerinfo.Uptime = time.Since(c.up) } if p, ok := conns[conn]; ok { diff --git a/src/core/link.go b/src/core/link.go index d7e5b110..c21b8db5 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -99,9 +99,36 @@ func (l *links) init(c *Core) error { l._links = make(map[linkInfo]*link) l._listeners = make(map[*Listener]context.CancelFunc) + l.Act(nil, l._updateAverages) return nil } +func (l *links) _updateAverages() { + select { + case <-l.core.ctx.Done(): + return + default: + } + + for _, l := range l._links { + if l._conn == nil { + continue + } + rx := atomic.LoadUint64(&l._conn.rx) + tx := atomic.LoadUint64(&l._conn.tx) + lastrx := atomic.LoadUint64(&l._conn.lastrx) + lasttx := atomic.LoadUint64(&l._conn.lasttx) + atomic.StoreUint64(&l._conn.rxrate, rx-lastrx) + atomic.StoreUint64(&l._conn.txrate, tx-lasttx) + atomic.StoreUint64(&l._conn.lastrx, rx) + atomic.StoreUint64(&l._conn.lasttx, tx) + } + + time.AfterFunc(time.Second, func() { + l.Act(nil, l._updateAverages) + }) +} + func (l *links) shutdown() { phony.Block(l, func() { for listener := range l._listeners { @@ -699,9 +726,13 @@ func urlForLinkInfo(u url.URL) url.URL { type linkConn struct { // tx and rx are at the beginning of the struct to ensure 64-bit alignment // on 32-bit platforms, see https://pkg.go.dev/sync/atomic#pkg-note-BUG - rx uint64 - tx uint64 - up time.Time + rx uint64 + tx uint64 + rxrate uint64 + txrate uint64 + lastrx uint64 + lasttx uint64 + up time.Time net.Conn } From ff9e90c5aa2d7e177577f2d1044c25a278ae2036 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Fri, 22 Nov 2024 09:31:38 +0000 Subject: [PATCH 268/293] Update link cost calculation and next-hop selection (update to Arceliar/ironwood@75a6e82) --- go.mod | 2 +- go.sum | 47 ++--------------------------------------------- 2 files changed, 3 insertions(+), 46 deletions(-) diff --git a/go.mod b/go.mod index 76641a60..27fe1110 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/yggdrasil-network/yggdrasil-go go 1.21 require ( - github.com/Arceliar/ironwood v0.0.0-20241016082300-f6fb9da97a17 + github.com/Arceliar/ironwood v0.0.0-20241122002527-75a6e82fa380 github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d github.com/cheggaaa/pb/v3 v3.1.5 github.com/coder/websocket v1.8.12 diff --git a/go.sum b/go.sum index 89dd0c7b..19674559 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Arceliar/ironwood v0.0.0-20241016082300-f6fb9da97a17 h1:uOvHqPwu09ndYZQDUL6QvyDcz0M9kwooKYa/PEfLwIU= -github.com/Arceliar/ironwood v0.0.0-20241016082300-f6fb9da97a17/go.mod h1:6WP4799FX0OuWdENGQAh+0RXp9FLh0y7NZ7tM9cJyXk= +github.com/Arceliar/ironwood v0.0.0-20241122002527-75a6e82fa380 h1:WRLvBMWzs6NOiPUYA7fMu8XqZFg/clXKorUumfbJNv0= +github.com/Arceliar/ironwood v0.0.0-20241122002527-75a6e82fa380/go.mod h1:SrrElc3FFMpYCODSr11jWbLFeOM8WsY+DbDY/l2AXF0= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= @@ -73,74 +73,31 @@ github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1Y github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU= github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= -golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4= From b98f98318f9c9f0137f734adc5c6728cc4ae7a2e Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Fri, 22 Nov 2024 09:44:30 +0000 Subject: [PATCH 269/293] Tweaks to link handling --- src/core/link.go | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/core/link.go b/src/core/link.go index c21b8db5..899d73bd 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -154,7 +154,7 @@ const ErrLinkPasswordInvalid = linkError("invalid password supplied") const ErrLinkUnrecognisedSchema = linkError("link schema unknown") const ErrLinkMaxBackoffInvalid = linkError("max backoff duration invalid") const ErrLinkSNINotSupported = linkError("SNI not supported on this link type") -const ErrLinkNoSuitableIPs = linkError("no suitable remote IPs") +const ErrLinkNoSuitableIPs = linkError("peer has no suitable addresses") func (l *links) add(u *url.URL, sintf string, linkType linkType) error { var retErr error @@ -365,8 +365,12 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error { // Give the connection to the handler. The handler will block // for the lifetime of the connection. - if err = l.handler(linkType, options, lc, resetBackoff, false); err != nil && err != io.EOF { - l.core.log.Debugf("Link %s error: %s\n", info.uri, err) + switch err = l.handler(linkType, options, lc, resetBackoff, false); { + case err == nil: + case errors.Is(err, io.EOF): + case errors.Is(err, net.ErrClosed): + default: + l.core.log.Debugf("Link %s error: %s\n", u.Host, err) } // The handler has stopped running so the connection is dead, @@ -697,7 +701,16 @@ func (l *links) findSuitableIP(url *url.URL, fn func(hostname string, ip net.IP, var _ips [64]net.IP ips := _ips[:0] for _, ip := range resp { - if l.core.config.peerFilter != nil && !l.core.config.peerFilter(ip) { + switch { + case ip.IsUnspecified(): + continue + case ip.IsMulticast(): + continue + case ip.IsLinkLocalMulticast(): + continue + case ip.IsInterfaceLocalMulticast(): + continue + case l.core.config.peerFilter != nil && !l.core.config.peerFilter(ip): continue } ips = append(ips, ip) From 2454970e4dbd55d8e9788e0022f4f0ca065e7af5 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Fri, 22 Nov 2024 09:47:33 +0000 Subject: [PATCH 270/293] Tweaks to configuration --- src/config/config.go | 6 +++--- src/config/defaults_darwin.go | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/config/config.go b/src/config/config.go index 64961547..5dd7b3d4 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -47,7 +47,7 @@ type NodeConfig struct { InterfacePeers map[string][]string `comment:"List of connection strings for outbound peer connections in URI format,\narranged by source interface, e.g. { \"eth0\": [ \"tls://a.b.c.d:e\" ] }.\nYou should only use this option if your machine is multi-homed and you\nwant to establish outbound peer connections on different interfaces.\nOtherwise you should use \"Peers\"."` Listen []string `comment:"Listen addresses for incoming connections. You will need to add\nlisteners in order to accept incoming peerings from non-local nodes.\nThis is not required if you wish to establish outbound peerings only.\nMulticast peer discovery will work regardless of any listeners set\nhere. Each listener should be specified in URI format as above, e.g.\ntls://0.0.0.0:0 or tls://[::]:0 to listen on all interfaces."` AdminListen string `json:",omitempty" 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."` - MulticastInterfaces []MulticastInterfaceConfig `comment:"Configuration for which interfaces multicast peer discovery should be\nenabled on. Each entry in the list should be a json object which may\ncontain Regex, Beacon, Listen, and Port. Regex is a regular expression\nwhich is matched against an interface name, and interfaces use the\nfirst configuration that they match gainst. Beacon configures whether\nor not the node should send link-local multicast beacons to advertise\ntheir presence, while listening for incoming connections on Port.\nListen controls whether or not the node listens for multicast beacons\nand opens outgoing connections."` + MulticastInterfaces []MulticastInterfaceConfig `comment:"Configuration for which interfaces multicast peer discovery should be\nenabled on. Regex is a regular expression which is matched against an\ninterface name, and interfaces use the first configuration that they\nmatch against. Beacon controls whether or not your node advertises its\npresence to others, whereas Listen controls whether or not your node\nlistens out for and tries to connect to other advertising nodes. See\nhttps://yggdrasil-network.github.io/configurationref.html#multicastinterfaces\nfor more supported options."` AllowedPublicKeys []string `comment:"List of peer public keys to allow incoming peering connections\nfrom. If left empty/undefined then all connections will be allowed\nby default. This does not affect outgoing peerings, nor does it\naffect link-local peers discovered via multicast.\nWARNING: THIS IS NOT A FIREWALL and DOES NOT limit who can reach\nopen ports or services running on your machine!"` IfName string `comment:"Local network interface name for TUN adapter, or \"auto\" to select\nan interface automatically, or \"none\" to run without TUN."` IfMTU uint64 `comment:"Maximum Transmission Unit (MTU) size for your local TUN interface.\nDefault is the largest supported size for your platform. The lowest\npossible value is 1280."` @@ -60,8 +60,8 @@ type MulticastInterfaceConfig struct { Regex string Beacon bool Listen bool - Port uint16 - Priority uint64 // really uint8, but gobind won't export it + Port uint16 `json:",omitempty"` + Priority uint64 `json:",omitempty"` // really uint8, but gobind won't export it Password string } diff --git a/src/config/defaults_darwin.go b/src/config/defaults_darwin.go index e851d6b9..11da7484 100644 --- a/src/config/defaults_darwin.go +++ b/src/config/defaults_darwin.go @@ -8,7 +8,7 @@ package config func getDefaults() platformDefaultParameters { return platformDefaultParameters{ // Admin - DefaultAdminListen: "unix:///var/run/yggdrasil.sock", + DefaultAdminListen: "tcp://localhost:9001", // Configuration (used for yggdrasilctl) DefaultConfigFile: "/etc/yggdrasil.conf", @@ -17,6 +17,7 @@ func getDefaults() platformDefaultParameters { DefaultMulticastInterfaces: []MulticastInterfaceConfig{ {Regex: "en.*", Beacon: true, Listen: true}, {Regex: "bridge.*", Beacon: true, Listen: true}, + {Regex: "awdl0", Beacon: false, Listen: false}, }, // TUN From d3b4de46ea156b63e925909b957ff6138e6b07ba Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 23 Nov 2024 13:43:34 +0000 Subject: [PATCH 271/293] Improvements to how link shutdowns are handled --- src/core/link.go | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/core/link.go b/src/core/link.go index 899d73bd..f30016f9 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -131,8 +131,8 @@ func (l *links) _updateAverages() { func (l *links) shutdown() { phony.Block(l, func() { - for listener := range l._listeners { - _ = listener.listener.Close() + for _, cancel := range l._listeners { + cancel() } for _, link := range l._links { if link._conn != nil { @@ -429,7 +429,7 @@ func (l *links) remove(u *url.URL, sintf string, _ linkType) error { } func (l *links) listen(u *url.URL, sintf string, local bool) (*Listener, error) { - ctx, cancel := context.WithCancel(l.core.ctx) + ctx, ctxcancel := context.WithCancel(l.core.ctx) var protocol linkProtocol switch strings.ToLower(u.Scheme) { case "tcp": @@ -445,21 +445,25 @@ func (l *links) listen(u *url.URL, sintf string, local bool) (*Listener, error) case "wss": protocol = l.wss default: - cancel() + ctxcancel() return nil, ErrLinkUnrecognisedSchema } listener, err := protocol.listen(ctx, u, sintf) if err != nil { - cancel() + ctxcancel() return nil, err } + addr := listener.Addr() + cancel := func() { + ctxcancel() + if err := listener.Close(); err != nil && !errors.Is(err, net.ErrClosed) { + l.core.log.Warnf("Error closing %s listener %s: %s", strings.ToUpper(u.Scheme), addr, err) + } + } li := &Listener{ listener: listener, ctx: ctx, - Cancel: func() { - cancel() - _ = listener.Close() - }, + Cancel: cancel, } var options linkOptions @@ -482,10 +486,11 @@ func (l *links) listen(u *url.URL, sintf string, local bool) (*Listener, error) }) go func() { - l.core.log.Infof("%s listener started on %s", strings.ToUpper(u.Scheme), li.listener.Addr()) - defer l.core.log.Infof("%s listener stopped on %s", strings.ToUpper(u.Scheme), li.listener.Addr()) + l.core.log.Infof("%s listener started on %s", strings.ToUpper(u.Scheme), addr) defer phony.Block(l, func() { + cancel() delete(l._listeners, li) + l.core.log.Infof("%s listener stopped on %s", strings.ToUpper(u.Scheme), addr) }) for { conn, err := li.listener.Accept() From 7790a19e4c78da866bb29c653558a3bfba363ffe Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 23 Nov 2024 14:49:48 +0000 Subject: [PATCH 272/293] New detail in `getMulticastInterfaces` admin endpoint --- cmd/yggdrasilctl/main.go | 16 +++++++++++++-- src/config/defaults_darwin.go | 2 +- src/multicast/admin.go | 37 ++++++++++++++++++++++++++++++----- src/multicast/multicast.go | 18 +++++++++-------- 4 files changed, 57 insertions(+), 16 deletions(-) diff --git a/cmd/yggdrasilctl/main.go b/cmd/yggdrasilctl/main.go index b73708e6..ca0bce1a 100644 --- a/cmd/yggdrasilctl/main.go +++ b/cmd/yggdrasilctl/main.go @@ -281,9 +281,21 @@ func run() int { if err := json.Unmarshal(recv.Response, &resp); err != nil { panic(err) } - table.SetHeader([]string{"Interface"}) + fmtBool := func(b bool) string { + if b { + return "Yes" + } + return "-" + } + table.SetHeader([]string{"Name", "Listen Address", "Beacon", "Listen", "Password"}) for _, p := range resp.Interfaces { - table.Append([]string{p}) + table.Append([]string{ + p.Name, + p.Address, + fmtBool(p.Beacon), + fmtBool(p.Listen), + fmtBool(p.Password), + }) } table.Render() diff --git a/src/config/defaults_darwin.go b/src/config/defaults_darwin.go index 11da7484..5f44ef59 100644 --- a/src/config/defaults_darwin.go +++ b/src/config/defaults_darwin.go @@ -8,7 +8,7 @@ package config func getDefaults() platformDefaultParameters { return platformDefaultParameters{ // Admin - DefaultAdminListen: "tcp://localhost:9001", + DefaultAdminListen: "unix:///var/run/yggdrasil.sock", // Configuration (used for yggdrasilctl) DefaultConfigFile: "/etc/yggdrasil.conf", diff --git a/src/multicast/admin.go b/src/multicast/admin.go index 8b29716d..0042f519 100644 --- a/src/multicast/admin.go +++ b/src/multicast/admin.go @@ -2,20 +2,47 @@ package multicast import ( "encoding/json" + "slices" + "strings" + "github.com/Arceliar/phony" "github.com/yggdrasil-network/yggdrasil-go/src/admin" ) type GetMulticastInterfacesRequest struct{} type GetMulticastInterfacesResponse struct { - Interfaces []string `json:"multicast_interfaces"` + Interfaces []MulticastInterfaceState `json:"multicast_interfaces"` +} + +type MulticastInterfaceState struct { + Name string `json:"name"` + Address string `json:"address"` + Beacon bool `json:"beacon"` + Listen bool `json:"listen"` + Password bool `json:"password"` } func (m *Multicast) getMulticastInterfacesHandler(_ *GetMulticastInterfacesRequest, res *GetMulticastInterfacesResponse) error { - res.Interfaces = []string{} - for _, v := range m.Interfaces() { - res.Interfaces = append(res.Interfaces, v.Name) - } + res.Interfaces = []MulticastInterfaceState{} + phony.Block(m, func() { + for name, intf := range m._interfaces { + is := MulticastInterfaceState{ + Name: intf.iface.Name, + Beacon: intf.beacon, + Listen: intf.listen, + Password: len(intf.password) > 0, + } + if li := m._listeners[name]; li != nil && li.listener != nil { + is.Address = li.listener.Addr().String() + } else { + is.Address = "-" + } + res.Interfaces = append(res.Interfaces, is) + } + }) + slices.SortStableFunc(res.Interfaces, func(a, b MulticastInterfaceState) int { + return strings.Compare(a.Name, b.Name) + }) return nil } diff --git a/src/multicast/multicast.go b/src/multicast/multicast.go index 77ea8a50..9134501c 100644 --- a/src/multicast/multicast.go +++ b/src/multicast/multicast.go @@ -156,7 +156,13 @@ func (m *Multicast) _updateInterfaces() { delete(interfaces, name) continue } - info.addrs = addrs + for _, addr := range addrs { + addrIP, _, err := net.ParseCIDR(addr.String()) + if err != nil || addrIP.To4() != nil || !addrIP.IsLinkLocalUnicast() { + continue + } + info.addrs = append(info.addrs, addr) + } interfaces[name] = info m.log.Debugf("Discovered addresses for interface %s: %s", name, addrs) } @@ -299,13 +305,9 @@ func (m *Multicast) _announce() { for _, info := range m._interfaces { iface := info.iface for _, addr := range info.addrs { - addrIP, _, _ := net.ParseCIDR(addr.String()) - // Ignore IPv4 addresses - if addrIP.To4() != nil { - continue - } - // Ignore non-link-local addresses - if !addrIP.IsLinkLocalUnicast() { + addrIP, _, err := net.ParseCIDR(addr.String()) + // Ignore IPv4 addresses or non-link-local addresses + if err != nil || addrIP.To4() != nil || !addrIP.IsLinkLocalUnicast() { continue } if info.listen { From bdb2d399c5776a80ea81164f1e9b8013fa934775 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 23 Nov 2024 14:55:14 +0000 Subject: [PATCH 273/293] Update dependencies --- go.mod | 10 +++++----- go.sum | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index 27fe1110..987a3554 100644 --- a/go.mod +++ b/go.mod @@ -14,10 +14,10 @@ require ( github.com/quic-go/quic-go v0.46.0 github.com/vishvananda/netlink v1.3.0 github.com/wlynxg/anet v0.0.5 - golang.org/x/crypto v0.28.0 - golang.org/x/net v0.30.0 - golang.org/x/sys v0.26.0 - golang.org/x/text v0.19.0 + golang.org/x/crypto v0.29.0 + golang.org/x/net v0.31.0 + golang.org/x/sys v0.27.0 + golang.org/x/text v0.20.0 golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 golang.zx2c4.com/wireguard/windows v0.5.3 @@ -34,7 +34,7 @@ require ( go.uber.org/mock v0.4.0 // indirect golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect golang.org/x/mod v0.19.0 // indirect - golang.org/x/sync v0.8.0 // indirect + golang.org/x/sync v0.9.0 // indirect golang.org/x/tools v0.23.0 // indirect ) diff --git a/go.sum b/go.sum index 19674559..8531a2a3 100644 --- a/go.sum +++ b/go.sum @@ -75,25 +75,25 @@ github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU= github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= -golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= -golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= +golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= -golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= +golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= +golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= +golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= +golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= From 3ed4a92288ee47dc388af9bf23e0bf66bbab96ae Mon Sep 17 00:00:00 2001 From: Neil Date: Sun, 24 Nov 2024 12:56:24 +0000 Subject: [PATCH 274/293] Yggdrasil 0.5.10 (#1207) Changelog updates. Co-authored-by: Neil Alexander --- CHANGELOG.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 39cfa960..c442353e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,31 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - in case of vulnerabilities. --> +## [0.5.10] - 2024-11-24 + +### Added + +* The `getPeers` admin endpoint will now report the current transmit/receive rate for each given peer +* The `getMulticastInterfaces` admin endpoint now reports much more useful information about each interface, rather than just a list of interface names + +### Changed + +* Minor tweaks to the routing algorithm: + * The next-hop selection will now prefer shorter paths when the costed distance is otherwise equal, tiebreaking on peering uptime to fall back to more stable paths + * Link cost calculations have been smoothed out, making the costs less sensitive to sudden spikes in latency +* Reusable name lookup and peer connection logic across different peering types for more consistent behaviour +* Some comments in the configuration file have been revised for clarity +* Upgrade dependencies + +### Fixed + +* Nodes with `IfName` set to `none` will now correctly respond to debug RPC requests +* The admin socket will now be created reliably before dropping privileges with `-user` +* Clear supplementary groups when providing a group ID as well as a user ID to `-user` +* SOCKS and WebSocket peerings should now use the correct source interface when specified in `InterfacePeers` +* `Peers` and `InterfacePeers` addresses that are obviously invalid (such as unspecified or multicast addresses) will now be correctly ignored +* Listeners should now shut down correctly, which should resolve issues where multicast listeners for specific interfaces would not come back up or would log errors + ## [0.5.9] - 2024-10-19 ### Added From b436052b2d7ff9f3cc3acd3c8a55396fa2308563 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 10 Dec 2024 19:02:13 +0000 Subject: [PATCH 275/293] Update to Arceliar/ironwood@9deb08d --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 987a3554..a2909018 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/yggdrasil-network/yggdrasil-go go 1.21 require ( - github.com/Arceliar/ironwood v0.0.0-20241122002527-75a6e82fa380 + github.com/Arceliar/ironwood v0.0.0-20241210120540-9deb08d9f8f9 github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d github.com/cheggaaa/pb/v3 v3.1.5 github.com/coder/websocket v1.8.12 diff --git a/go.sum b/go.sum index 8531a2a3..d62e2ddb 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Arceliar/ironwood v0.0.0-20241122002527-75a6e82fa380 h1:WRLvBMWzs6NOiPUYA7fMu8XqZFg/clXKorUumfbJNv0= -github.com/Arceliar/ironwood v0.0.0-20241122002527-75a6e82fa380/go.mod h1:SrrElc3FFMpYCODSr11jWbLFeOM8WsY+DbDY/l2AXF0= +github.com/Arceliar/ironwood v0.0.0-20241210120540-9deb08d9f8f9 h1:myI8fs7+Iw6g/ywvY9QNQOEzny51AklMz4sF0ErtTm8= +github.com/Arceliar/ironwood v0.0.0-20241210120540-9deb08d9f8f9/go.mod h1:SrrElc3FFMpYCODSr11jWbLFeOM8WsY+DbDY/l2AXF0= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= From 83ec58afc763ff89d9664876ed2e95fb5842985a Mon Sep 17 00:00:00 2001 From: Klemens Nanni Date: Thu, 12 Dec 2024 21:37:02 +0300 Subject: [PATCH 276/293] Use unveil(2) on OpenBSD (#1194) After #1175 removed ioctl(2) fallback code shelling out to ifconfig(8), there is no code left (compiled on OpenBSD) that would fork(2) or execve(2). Drop the ability to run any executable file to double down on this, thus reducing the attack surface of this this experimental, internet facing daemon running as root. pledge(2) is doable, but needs more polish. unveil(2), however, is as simple as it gets. On other systems, this code is a NOOP, but can still help to implement similar safety belts. --- cmd/yggdrasil/main.go | 16 ++++++++++++++++ go.mod | 1 + go.sum | 2 ++ 3 files changed, 19 insertions(+) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 3ec6414c..b3cbecf0 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -14,6 +14,8 @@ import ( "strings" "syscall" + "suah.dev/protect" + "github.com/gologme/log" gsyslog "github.com/hashicorp/go-syslog" "github.com/hjson/hjson-go/v4" @@ -39,6 +41,20 @@ type node struct { // The main function is responsible for configuring and starting Yggdrasil. func main() { + // Not all operations are coverable with pledge(2), so immediately + // limit file system access with unveil(2), effectively preventing + // "proc exec" promises right from the start: + // + // - read arbitrary config file + // - create/write arbitrary log file + // - read/write/chmod/remove admin socket, if at all + if err := protect.Unveil("/", "rwc"); err != nil { + panic(fmt.Sprintf("unveil: / rwc: %v", err)) + } + if err := protect.UnveilBlock(); err != nil { + panic(fmt.Sprintf("unveil: %v", err)) + } + genconf := flag.Bool("genconf", false, "print a new config to stdout") useconf := flag.Bool("useconf", false, "read HJSON/JSON config from stdin") useconffile := flag.String("useconffile", "", "read HJSON/JSON config from specified file path") diff --git a/go.mod b/go.mod index a2909018..5608f588 100644 --- a/go.mod +++ b/go.mod @@ -45,4 +45,5 @@ require ( github.com/mattn/go-runewidth v0.0.15 // indirect github.com/olekukonko/tablewriter v0.0.5 github.com/vishvananda/netns v0.0.4 // indirect + suah.dev/protect v1.2.4 ) diff --git a/go.sum b/go.sum index d62e2ddb..061a0769 100644 --- a/go.sum +++ b/go.sum @@ -112,3 +112,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 h1:TbRPT0HtzFP3Cno1zZo7yPzEEnfu8EjLfl6IU9VfqkQ= gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259/go.mod h1:AVgIgHMwK63XvmAzWG9vLQ41YnVHN0du0tEC46fI7yY= +suah.dev/protect v1.2.4 h1:iVZG/zQB63FKNpITDYM/cXoAeCTIjCiXHuFVByJFDzg= +suah.dev/protect v1.2.4/go.mod h1:vVrquYO3u1Ep9Ez2z8x+6N6/czm+TBmWKZfiXU2tb54= From b2b0396d48ee5d2803a14a5d9d9bd4f86b11d792 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 12 Dec 2024 18:42:53 +0000 Subject: [PATCH 277/293] Update dependencies --- go.mod | 18 +++++++++--------- go.sum | 36 +++++++++++++++++++----------------- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/go.mod b/go.mod index 5608f588..a77ca47d 100644 --- a/go.mod +++ b/go.mod @@ -14,10 +14,10 @@ require ( github.com/quic-go/quic-go v0.46.0 github.com/vishvananda/netlink v1.3.0 github.com/wlynxg/anet v0.0.5 - golang.org/x/crypto v0.29.0 - golang.org/x/net v0.31.0 - golang.org/x/sys v0.27.0 - golang.org/x/text v0.20.0 + golang.org/x/crypto v0.31.0 + golang.org/x/net v0.32.0 + golang.org/x/sys v0.28.0 + golang.org/x/text v0.21.0 golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 golang.zx2c4.com/wireguard/windows v0.5.3 @@ -34,16 +34,16 @@ require ( go.uber.org/mock v0.4.0 // indirect golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect golang.org/x/mod v0.19.0 // indirect - golang.org/x/sync v0.9.0 // indirect + golang.org/x/sync v0.10.0 // indirect golang.org/x/tools v0.23.0 // indirect ) require ( github.com/VividCortex/ewma v1.2.0 // indirect - github.com/fatih/color v1.15.0 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect - github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/fatih/color v1.18.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect github.com/olekukonko/tablewriter v0.0.5 - github.com/vishvananda/netns v0.0.4 // indirect + github.com/vishvananda/netns v0.0.5 // indirect suah.dev/protect v1.2.4 ) diff --git a/go.sum b/go.sum index 061a0769..8aae0a77 100644 --- a/go.sum +++ b/go.sum @@ -19,8 +19,8 @@ github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3C github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= -github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= @@ -45,11 +45,11 @@ github.com/kardianos/minwinsvc v1.0.2/go.mod h1:LUZNYhNmxujx2tR7FbdxqYJ9XDDoCd3M github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= -github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= @@ -69,31 +69,33 @@ github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= github.com/vishvananda/netlink v1.3.0 h1:X7l42GfcV4S6E4vHTsw48qbrV+9PVojNfIhZcwQdrZk= github.com/vishvananda/netlink v1.3.0/go.mod h1:i6NetklAujEcC6fK0JPjT8qSwWyO0HLn4UKG+hGqeJs= -github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8= github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= +github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY= +github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU= github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= -golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= -golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= -golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= -golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= -golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= +golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= -golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= From 2d587740c11e136dbc094be206bfaeeda8559d3f Mon Sep 17 00:00:00 2001 From: Klemens Nanni Date: Thu, 12 Dec 2024 21:48:24 +0300 Subject: [PATCH 278/293] genkeys, yggdrasilctl: Use pledge(2) on OpenBSD (#1193) Restrict system operations of CLI tools with https://man.openbsd.org/pledge.2. https://pkg.go.dev/suah.dev/protect abstracts the OS specific code, i.e. is a NOOP on non-OpenBSD systems. This PR is to gauge upstream interest in this direction; my OpenBSD port of yggdrasil already pledges the daemon, resulting in minimal runtime privileges, but there are still a few rough edges: https://github.com/jasperla/openbsd-wip/blob/master/net/yggdrasil/patches/patch-cmd_yggdrasil_main_go#L80 --------- Co-authored-by: Neil --- cmd/genkeys/main.go | 6 ++++++ cmd/yggdrasilctl/main.go | 12 ++++++++++++ 2 files changed, 18 insertions(+) diff --git a/cmd/genkeys/main.go b/cmd/genkeys/main.go index 36107c0a..d1a15346 100644 --- a/cmd/genkeys/main.go +++ b/cmd/genkeys/main.go @@ -18,6 +18,8 @@ import ( "runtime" "time" + "suah.dev/protect" + "github.com/yggdrasil-network/yggdrasil-go/src/address" ) @@ -27,6 +29,10 @@ type keySet struct { } func main() { + if err := protect.Pledge("stdio"); err != nil { + panic(err) + } + threads := runtime.GOMAXPROCS(0) fmt.Println("Threads:", threads) start := time.Now() diff --git a/cmd/yggdrasilctl/main.go b/cmd/yggdrasilctl/main.go index ca0bce1a..51c25dcd 100644 --- a/cmd/yggdrasilctl/main.go +++ b/cmd/yggdrasilctl/main.go @@ -13,6 +13,8 @@ import ( "strings" "time" + "suah.dev/protect" + "github.com/olekukonko/tablewriter" "github.com/yggdrasil-network/yggdrasil-go/src/admin" "github.com/yggdrasil-network/yggdrasil-go/src/core" @@ -22,6 +24,11 @@ import ( ) func main() { + // read config, speak DNS/TCP and/or over a UNIX socket + if err := protect.Pledge("stdio rpath inet unix dns"); err != nil { + panic(err) + } + // makes sure we can use defer and still return an error code to the OS os.Exit(run()) } @@ -78,6 +85,11 @@ func run() int { panic(err) } + // config and socket are done, work without unprivileges + if err := protect.Pledge("stdio"); err != nil { + panic(err) + } + logger.Println("Connected") defer conn.Close() From 69451fe969bfadb779bed4eecc46f604800bdd0d Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 12 Dec 2024 19:07:55 +0000 Subject: [PATCH 279/293] Specify TLS 1.2-TLS 1.3 supported range for client connections Should fix #1208. --- src/core/link_quic.go | 2 ++ src/core/link_socks.go | 2 ++ src/core/link_tls.go | 2 ++ src/core/link_wss.go | 2 ++ 4 files changed, 8 insertions(+) diff --git a/src/core/link_quic.go b/src/core/link_quic.go index ffb69a6d..d23ab184 100644 --- a/src/core/link_quic.go +++ b/src/core/link_quic.go @@ -54,6 +54,8 @@ func (l *linkQUIC) dial(ctx context.Context, url *url.URL, info linkInfo, option tlsconfig := l.tlsconfig.Clone() return l.links.findSuitableIP(url, func(hostname string, ip net.IP, port int) (net.Conn, error) { tlsconfig.ServerName = hostname + tlsconfig.MinVersion = tls.VersionTLS12 + tlsconfig.MaxVersion = tls.VersionTLS13 hostport := net.JoinHostPort(ip.String(), fmt.Sprintf("%d", port)) qc, err := quic.DialAddr(ctx, hostport, l.tlsconfig, l.quicconfig) if err != nil { diff --git a/src/core/link_socks.go b/src/core/link_socks.go index f33cd190..495c8233 100644 --- a/src/core/link_socks.go +++ b/src/core/link_socks.go @@ -51,6 +51,8 @@ func (l *linkSOCKS) dial(_ context.Context, url *url.URL, info linkInfo, options } if url.Scheme == "sockstls" { tlsconfig.ServerName = hostname + tlsconfig.MinVersion = tls.VersionTLS12 + tlsconfig.MaxVersion = tls.VersionTLS13 if sni := options.tlsSNI; sni != "" { tlsconfig.ServerName = sni } diff --git a/src/core/link_tls.go b/src/core/link_tls.go index da3c7791..55da8597 100644 --- a/src/core/link_tls.go +++ b/src/core/link_tls.go @@ -35,6 +35,8 @@ func (l *linkTLS) dial(ctx context.Context, url *url.URL, info linkInfo, options tlsconfig := l.config.Clone() return l.links.findSuitableIP(url, func(hostname string, ip net.IP, port int) (net.Conn, error) { tlsconfig.ServerName = hostname + tlsconfig.MinVersion = tls.VersionTLS12 + tlsconfig.MaxVersion = tls.VersionTLS13 if sni := options.tlsSNI; sni != "" { tlsconfig.ServerName = sni } diff --git a/src/core/link_wss.go b/src/core/link_wss.go index 1a8d571f..1d618324 100644 --- a/src/core/link_wss.go +++ b/src/core/link_wss.go @@ -34,6 +34,8 @@ func (l *linkWSS) dial(ctx context.Context, url *url.URL, info linkInfo, options tlsconfig := l.tlsconfig.Clone() return l.links.findSuitableIP(url, func(hostname string, ip net.IP, port int) (net.Conn, error) { tlsconfig.ServerName = hostname + tlsconfig.MinVersion = tls.VersionTLS12 + tlsconfig.MaxVersion = tls.VersionTLS13 u := *url u.Host = net.JoinHostPort(ip.String(), fmt.Sprintf("%d", port)) addr := &net.TCPAddr{ From 7adf5f18b7ca234c48236e23b2a025be8e8afc13 Mon Sep 17 00:00:00 2001 From: Neil Date: Thu, 12 Dec 2024 20:26:54 +0100 Subject: [PATCH 280/293] Yggdrasil 0.5.11 (#1214) Changelog updates. Co-authored-by: Neil Alexander --- CHANGELOG.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c442353e..8169936a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,23 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - in case of vulnerabilities. --> +## [0.5.11] - 2024-12-12 + +### Added + +* Support for `unveil` and `pledge` on OpenBSD + +### Changed + +* The parent selection algorithm now only chooses a new parent if there is a larger cost benefit to doing so, which should help to stabilise the tree +* The bloom filters are now repropagated periodically, to avoid nodes getting stuck with bad state + +### Fixed + +* A memory leak caused by missed cleanup of the peer response map has been fixed +* Other bug fixes with bloom filter propagation for off-tree filters and zero vs one bits +* TLS-based peering connections now support TLS 1.2 again + ## [0.5.10] - 2024-11-24 ### Added From 657f7e0db38e9375a75e8a88bac5ba6e3f02537f Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Fri, 13 Dec 2024 16:54:14 +0000 Subject: [PATCH 281/293] Fix empty user/group detection on `chuser` This should fix #1216. --- cmd/yggdrasil/chuser_unix.go | 6 ++++++ cmd/yggdrasil/chuser_unix_test.go | 16 ++++++++-------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/cmd/yggdrasil/chuser_unix.go b/cmd/yggdrasil/chuser_unix.go index fc3e5c2c..4aa4cc29 100644 --- a/cmd/yggdrasil/chuser_unix.go +++ b/cmd/yggdrasil/chuser_unix.go @@ -14,6 +14,12 @@ import ( func chuser(input string) error { givenUser, givenGroup, _ := strings.Cut(input, ":") + if givenUser == "" { + return fmt.Errorf("user is empty") + } + if strings.Index(input, ":") > -1 && givenGroup == "" { + return fmt.Errorf("group is empty") + } var ( err error diff --git a/cmd/yggdrasil/chuser_unix_test.go b/cmd/yggdrasil/chuser_unix_test.go index ad2e3517..fc624ac2 100644 --- a/cmd/yggdrasil/chuser_unix_test.go +++ b/cmd/yggdrasil/chuser_unix_test.go @@ -4,33 +4,33 @@ package main import ( - "testing" "os/user" + "testing" ) // Usernames must not contain a number sign. -func TestEmptyString (t *testing.T) { +func TestEmptyString(t *testing.T) { if chuser("") == nil { t.Fatal("the empty string is not a valid user") } } // Either omit delimiter and group, or omit both. -func TestEmptyGroup (t *testing.T) { +func TestEmptyGroup(t *testing.T) { if chuser("0:") == nil { t.Fatal("the empty group is not allowed") } } // Either user only or user and group. -func TestGroupOnly (t *testing.T) { +func TestGroupOnly(t *testing.T) { if chuser(":0") == nil { t.Fatal("group only is not allowed") } } // Usenames must not contain the number sign. -func TestInvalidUsername (t *testing.T) { +func TestInvalidUsername(t *testing.T) { const username = "#user" if chuser(username) == nil { t.Fatalf("'%s' is not a valid username", username) @@ -38,14 +38,14 @@ func TestInvalidUsername (t *testing.T) { } // User IDs must be non-negative. -func TestInvalidUserid (t *testing.T) { +func TestInvalidUserid(t *testing.T) { if chuser("-1") == nil { t.Fatal("User ID cannot be negative") } } // Change to the current user by ID. -func TestCurrentUserid (t *testing.T) { +func TestCurrentUserid(t *testing.T) { usr, err := user.Current() if err != nil { t.Fatal(err) @@ -61,7 +61,7 @@ func TestCurrentUserid (t *testing.T) { } // Change to a common user by name. -func TestCommonUsername (t *testing.T) { +func TestCommonUsername(t *testing.T) { usr, err := user.Current() if err != nil { t.Fatal(err) From 04be129878a68d7bbd2fcf2444f1f17b0c52aa36 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Fri, 13 Dec 2024 23:12:36 +0000 Subject: [PATCH 282/293] Update to Arceliar/ironwood@743fe2f --- cmd/yggdrasil/chuser_unix.go | 2 +- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/yggdrasil/chuser_unix.go b/cmd/yggdrasil/chuser_unix.go index 4aa4cc29..24a706df 100644 --- a/cmd/yggdrasil/chuser_unix.go +++ b/cmd/yggdrasil/chuser_unix.go @@ -17,7 +17,7 @@ func chuser(input string) error { if givenUser == "" { return fmt.Errorf("user is empty") } - if strings.Index(input, ":") > -1 && givenGroup == "" { + if strings.Contains(input, ":") && givenGroup == "" { return fmt.Errorf("group is empty") } diff --git a/go.mod b/go.mod index a77ca47d..d8a7321c 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/yggdrasil-network/yggdrasil-go go 1.21 require ( - github.com/Arceliar/ironwood v0.0.0-20241210120540-9deb08d9f8f9 + github.com/Arceliar/ironwood v0.0.0-20241213013129-743fe2fccbd3 github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d github.com/cheggaaa/pb/v3 v3.1.5 github.com/coder/websocket v1.8.12 diff --git a/go.sum b/go.sum index 8aae0a77..d21246b3 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Arceliar/ironwood v0.0.0-20241210120540-9deb08d9f8f9 h1:myI8fs7+Iw6g/ywvY9QNQOEzny51AklMz4sF0ErtTm8= -github.com/Arceliar/ironwood v0.0.0-20241210120540-9deb08d9f8f9/go.mod h1:SrrElc3FFMpYCODSr11jWbLFeOM8WsY+DbDY/l2AXF0= +github.com/Arceliar/ironwood v0.0.0-20241213013129-743fe2fccbd3 h1:d8N0z+udAnbU5PdjpLSNPTWlqeU/nnYsQ42B6+879aw= +github.com/Arceliar/ironwood v0.0.0-20241213013129-743fe2fccbd3/go.mod h1:SrrElc3FFMpYCODSr11jWbLFeOM8WsY+DbDY/l2AXF0= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= From 9c73bacab90806d913df1ef2b6d79b5ebeb0f98b Mon Sep 17 00:00:00 2001 From: Neil Date: Sat, 14 Dec 2024 00:33:26 +0100 Subject: [PATCH 283/293] Update to Go 1.22, quic-go/quic-go@v0.48.2 (#1218) Our dependencies are now moving beyond Go 1.21 so need to update. Co-authored-by: Neil Alexander --- .github/workflows/ci.yml | 8 ++++---- README.md | 2 +- go.mod | 4 ++-- go.sum | 18 ++++++++++++++++++ 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 57a0d2a7..ec613e14 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,7 +51,7 @@ jobs: strategy: fail-fast: false matrix: - goversion: ["1.21", "1.22", "1.23"] + goversion: ["1.22", "1.23"] name: Build & Test (Linux, Go ${{ matrix.goversion }}) needs: [lint] @@ -75,7 +75,7 @@ jobs: strategy: fail-fast: false matrix: - goversion: ["1.21", "1.22", "1.23"] + goversion: ["1.22", "1.23"] name: Build & Test (Windows, Go ${{ matrix.goversion }}) needs: [lint] @@ -99,7 +99,7 @@ jobs: strategy: fail-fast: false matrix: - goversion: ["1.21", "1.22", "1.23"] + goversion: ["1.22", "1.23"] name: Build & Test (macOS, Go ${{ matrix.goversion }}) needs: [lint] @@ -123,7 +123,7 @@ jobs: strategy: fail-fast: false matrix: - goversion: ["1.21", "1.22", "1.23"] + goversion: ["1.22", "1.23"] goos: - freebsd - openbsd diff --git a/README.md b/README.md index 8449f073..ea0e1a38 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ or tools in the `contrib` folder. If you want to build from source, as opposed to installing one of the pre-built packages: -1. Install [Go](https://golang.org) (requires Go 1.21 or later) +1. Install [Go](https://golang.org) (requires Go 1.22 or later) 2. Clone this repository 2. Run `./build` diff --git a/go.mod b/go.mod index d8a7321c..222cb1ef 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/yggdrasil-network/yggdrasil-go -go 1.21 +go 1.22 require ( github.com/Arceliar/ironwood v0.0.0-20241213013129-743fe2fccbd3 @@ -11,7 +11,7 @@ require ( github.com/hashicorp/go-syslog v1.0.0 github.com/hjson/hjson-go/v4 v4.4.0 github.com/kardianos/minwinsvc v1.0.2 - github.com/quic-go/quic-go v0.46.0 + github.com/quic-go/quic-go v0.48.2 github.com/vishvananda/netlink v1.3.0 github.com/wlynxg/anet v0.0.5 golang.org/x/crypto v0.31.0 diff --git a/go.sum b/go.sum index d21246b3..c7a4d38f 100644 --- a/go.sum +++ b/go.sum @@ -23,8 +23,12 @@ github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/gologme/log v1.3.0 h1:l781G4dE+pbigClDSDzSaaYKtiueHCILUa/qSDsmHAo= @@ -35,6 +39,8 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hjson/hjson-go/v4 v4.4.0 h1:D/NPvqOCH6/eisTb5/ztuIS8GUvmpHaLOcNk1Bjr298= @@ -54,12 +60,17 @@ github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= +github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg= +github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= +github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/quic-go/quic-go v0.46.0 h1:uuwLClEEyk1DNvchH8uCByQVjo3yKL9opKulExNDs7Y= github.com/quic-go/quic-go v0.46.0/go.mod h1:1dLehS7TIR64+vxGR70GDcatWTOtMX2PUtnKsjbTurI= +github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE= +github.com/quic-go/quic-go v0.48.2/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -76,12 +87,17 @@ github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU= github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= +go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= +go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +golang.org/x/exp v0.0.0-20241210194714-1829a127f884 h1:Y/Mj/94zIQQGHVSv1tTtQBDaQaJe62U9bkDZKKyhPCU= +golang.org/x/exp v0.0.0-20241210194714-1829a127f884/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= @@ -100,6 +116,8 @@ golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= +golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= +golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4= From 22bc9c44e286568e423305fc67c8f890a351b6a1 Mon Sep 17 00:00:00 2001 From: Peter Gervai Date: Wed, 18 Dec 2024 20:56:46 +0100 Subject: [PATCH 284/293] genkeys print the number of generated keys (#1217) It is good to know how many resources have we carelessly wasted. :-) --- cmd/genkeys/main.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/cmd/genkeys/main.go b/cmd/genkeys/main.go index d1a15346..2d007cb8 100644 --- a/cmd/genkeys/main.go +++ b/cmd/genkeys/main.go @@ -26,6 +26,7 @@ import ( type keySet struct { priv ed25519.PrivateKey pub ed25519.PublicKey + count uint64 } func main() { @@ -36,6 +37,8 @@ func main() { threads := runtime.GOMAXPROCS(0) fmt.Println("Threads:", threads) start := time.Now() + var totalKeys uint64 + totalKeys = 0 var currentBest ed25519.PublicKey newKeys := make(chan keySet, threads) for i := 0; i < threads; i++ { @@ -44,8 +47,9 @@ func main() { for { newKey := <-newKeys if isBetter(currentBest, newKey.pub) || len(currentBest) == 0 { + totalKeys += newKey.count currentBest = newKey.pub - fmt.Println("-----", time.Since(start)) + fmt.Println("-----", time.Since(start), "---", totalKeys, "keys tried") fmt.Println("Priv:", hex.EncodeToString(newKey.priv)) fmt.Println("Pub:", hex.EncodeToString(newKey.pub)) addr := address.AddrForKey(newKey.pub) @@ -68,11 +72,14 @@ func isBetter(oldPub, newPub ed25519.PublicKey) bool { func doKeys(out chan<- keySet) { bestKey := make(ed25519.PublicKey, ed25519.PublicKeySize) + var count uint64 + count = 0 for idx := range bestKey { bestKey[idx] = 0xff } for { pub, priv, err := ed25519.GenerateKey(nil) + count++ if err != nil { panic(err) } @@ -80,6 +87,7 @@ func doKeys(out chan<- keySet) { continue } bestKey = pub - out <- keySet{priv, pub} + out <- keySet{priv, pub, count} + count = 0 } } From 1fbcf3b3c2014f128575af0b4381c592536bc90b Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 18 Dec 2024 22:21:23 +0000 Subject: [PATCH 285/293] Rename `latency_ms` to `latency` in `getPeers` response since it isn't even milliseconds anymore --- src/admin/getpeers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/admin/getpeers.go b/src/admin/getpeers.go index 34eca243..0384b792 100644 --- a/src/admin/getpeers.go +++ b/src/admin/getpeers.go @@ -31,7 +31,7 @@ type PeerEntry struct { RXRate DataUnit `json:"rate_recvd,omitempty"` TXRate DataUnit `json:"rate_sent,omitempty"` Uptime float64 `json:"uptime,omitempty"` - Latency time.Duration `json:"latency_ms,omitempty"` + Latency time.Duration `json:"latency,omitempty"` LastErrorTime time.Duration `json:"last_error_time,omitempty"` LastError string `json:"last_error,omitempty"` } From 213f72b8403ff55a5e38a0fa7d1cd0a093ac4666 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 18 Dec 2024 22:34:30 +0000 Subject: [PATCH 286/293] Yggdrasil 0.5.12 --- CHANGELOG.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8169936a..c7ac1d80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,19 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - in case of vulnerabilities. --> +## [0.5.12] - 2024-12-18 + +* Go 1.22 is now required to build Yggdrasil + +### Changed + +* The `latency_ms` field in the admin socket `getPeers` response has been renamed to `latency` + +### Fixed + +* A timing regression which causes a higher level of idle protocol traffic on each peering has been fixed +* The `-user` flag now correctly detects an empty user/group specification + ## [0.5.11] - 2024-12-12 ### Added From 782c0250d743421c7f1ecaeafdf78fc41db97749 Mon Sep 17 00:00:00 2001 From: Klemens Nanni Date: Sun, 22 Dec 2024 14:04:26 +0300 Subject: [PATCH 287/293] Use pledge(2) on OpenBSD (#1215) Straight forward thanks to all privileged operations being done early enough during startup. --- cmd/yggdrasil/main.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index b3cbecf0..b3c9151d 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -312,6 +312,21 @@ func main() { } } + // Promise final modes of operation. At this point, if at all: + // - raw socket is created/open + // - admin socket is created/open + // - privileges are dropped to non-root user + // + // Peers, InterfacePeers, Listen can be UNIX sockets; + // Go's net.Listen.Close() deletes files on shutdown. + promises := []string{"stdio", "cpath", "inet", "unix", "dns"} + if len(cfg.MulticastInterfaces) > 0 { + promises = append(promises, "mcast") + } + if err := protect.Pledge(strings.Join(promises, " ")); err != nil { + panic(fmt.Sprintf("pledge: %v: %v", promises, err)) + } + // Block until we are told to shut down. <-ctx.Done() From 58b727d1f0e22b10fcac01d128d1d1659d78b14c Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 18 Feb 2025 12:52:21 +0000 Subject: [PATCH 288/293] Add Go 1.24 to CI --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ec613e14..c89445e9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,7 +51,7 @@ jobs: strategy: fail-fast: false matrix: - goversion: ["1.22", "1.23"] + goversion: ["1.22", "1.23", "1.24"] name: Build & Test (Linux, Go ${{ matrix.goversion }}) needs: [lint] @@ -75,7 +75,7 @@ jobs: strategy: fail-fast: false matrix: - goversion: ["1.22", "1.23"] + goversion: ["1.22", "1.23", "1.24"] name: Build & Test (Windows, Go ${{ matrix.goversion }}) needs: [lint] @@ -99,7 +99,7 @@ jobs: strategy: fail-fast: false matrix: - goversion: ["1.22", "1.23"] + goversion: ["1.22", "1.23", "1.24"] name: Build & Test (macOS, Go ${{ matrix.goversion }}) needs: [lint] @@ -123,7 +123,7 @@ jobs: strategy: fail-fast: false matrix: - goversion: ["1.22", "1.23"] + goversion: ["1.22", "1.23", "1.24"] goos: - freebsd - openbsd From 3b18909f70527ee5f927be3683daf6f949b43578 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 18 Feb 2025 12:56:52 +0000 Subject: [PATCH 289/293] Update dependencies --- go.mod | 12 ++++++------ go.sum | 41 ++++++++++++----------------------------- 2 files changed, 18 insertions(+), 35 deletions(-) diff --git a/go.mod b/go.mod index 222cb1ef..672edd6e 100644 --- a/go.mod +++ b/go.mod @@ -14,10 +14,10 @@ require ( github.com/quic-go/quic-go v0.48.2 github.com/vishvananda/netlink v1.3.0 github.com/wlynxg/anet v0.0.5 - golang.org/x/crypto v0.31.0 - golang.org/x/net v0.32.0 - golang.org/x/sys v0.28.0 - golang.org/x/text v0.21.0 + golang.org/x/crypto v0.33.0 + golang.org/x/net v0.35.0 + golang.org/x/sys v0.30.0 + golang.org/x/text v0.22.0 golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 golang.zx2c4.com/wireguard/windows v0.5.3 @@ -31,10 +31,10 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/onsi/ginkgo/v2 v2.9.5 // indirect github.com/rivo/uniseg v0.2.0 // indirect - go.uber.org/mock v0.4.0 // indirect + go.uber.org/mock v0.5.0 // indirect golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect golang.org/x/mod v0.19.0 // indirect - golang.org/x/sync v0.10.0 // indirect + golang.org/x/sync v0.11.0 // indirect golang.org/x/tools v0.23.0 // indirect ) diff --git a/go.sum b/go.sum index c7a4d38f..d843af40 100644 --- a/go.sum +++ b/go.sum @@ -23,12 +23,8 @@ github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= -github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= -github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/gologme/log v1.3.0 h1:l781G4dE+pbigClDSDzSaaYKtiueHCILUa/qSDsmHAo= @@ -39,8 +35,6 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= -github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hjson/hjson-go/v4 v4.4.0 h1:D/NPvqOCH6/eisTb5/ztuIS8GUvmpHaLOcNk1Bjr298= @@ -60,22 +54,18 @@ github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= -github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg= -github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= -github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/quic-go/quic-go v0.46.0 h1:uuwLClEEyk1DNvchH8uCByQVjo3yKL9opKulExNDs7Y= -github.com/quic-go/quic-go v0.46.0/go.mod h1:1dLehS7TIR64+vxGR70GDcatWTOtMX2PUtnKsjbTurI= github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE= github.com/quic-go/quic-go v0.48.2/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= github.com/vishvananda/netlink v1.3.0 h1:X7l42GfcV4S6E4vHTsw48qbrV+9PVojNfIhZcwQdrZk= @@ -85,39 +75,32 @@ github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zd github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU= github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= -go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= -go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= -golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= -golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= +golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= -golang.org/x/exp v0.0.0-20241210194714-1829a127f884 h1:Y/Mj/94zIQQGHVSv1tTtQBDaQaJe62U9bkDZKKyhPCU= -golang.org/x/exp v0.0.0-20241210194714-1829a127f884/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= -golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= -golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= +golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= -golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= -golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4= From 73705ff09ddfef8b458390aafe84315671952f1a Mon Sep 17 00:00:00 2001 From: patrini32 Date: Thu, 20 Feb 2025 12:45:49 +0300 Subject: [PATCH 290/293] Typo fix (#1232) --- src/address/address.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/address/address.go b/src/address/address.go index d56be80d..c9581c70 100644 --- a/src/address/address.go +++ b/src/address/address.go @@ -113,7 +113,7 @@ func SubnetForKey(publicKey ed25519.PublicKey) *Subnet { return &snet } -// GetKet returns the partial ed25519.PublicKey for the Address. +// GetKey returns the partial ed25519.PublicKey for the Address. // This is used for key lookup. func (a *Address) GetKey() ed25519.PublicKey { var key [ed25519.PublicKeySize]byte @@ -141,7 +141,7 @@ func (a *Address) GetKey() ed25519.PublicKey { return ed25519.PublicKey(key[:]) } -// GetKet returns the partial ed25519.PublicKey for the Subnet. +// GetKey returns the partial ed25519.PublicKey for the Subnet. // This is used for key lookup. func (s *Subnet) GetKey() ed25519.PublicKey { var addr Address From 5b8dbc8b1e297cd580eff36557d23df1ac5daab2 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 31 Mar 2025 10:18:57 +0100 Subject: [PATCH 291/293] Add summary helpers to mobile wrapper --- contrib/mobile/mobile.go | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/contrib/mobile/mobile.go b/contrib/mobile/mobile.go index abc89f1c..72ea7d68 100644 --- a/contrib/mobile/mobile.go +++ b/contrib/mobile/mobile.go @@ -1,6 +1,7 @@ package mobile import ( + "crypto/ed25519" "encoding/hex" "encoding/json" "net" @@ -273,3 +274,28 @@ func (m *Yggdrasil) GetMTU() int { func GetVersion() string { return version.BuildVersion() } + +type ConfigSummary struct { + PublicKey string + IPv6Address string + IPv6Subnet string +} + +func SummaryForConfig(b []byte) *ConfigSummary { + cfg := config.GenerateConfig() + if err := cfg.UnmarshalHJSON(b); err != nil { + return nil + } + pub := ed25519.PrivateKey(cfg.PrivateKey).Public().(ed25519.PublicKey) + hpub := hex.EncodeToString(pub) + addr := net.IP(address.AddrForKey(pub)[:]) + snet := net.IPNet{ + IP: append(address.SubnetForKey(pub)[:], 0, 0, 0, 0, 0, 0, 0, 0), + Mask: net.CIDRMask(64, 128), + } + return &ConfigSummary{ + PublicKey: hpub, + IPv6Address: addr.String(), + IPv6Subnet: snet.String(), + } +} From 6377d7f0717296e2c94561fcbd1c3b4df5af63bb Mon Sep 17 00:00:00 2001 From: Sergey Alirzaev Date: Tue, 15 Apr 2025 18:15:09 +0200 Subject: [PATCH 292/293] contrib/openrc: remove SIGHUP logic (#1236) as it is long gone from the daemon code and unexpectedly kills the daemon --- contrib/openrc/yggdrasil | 7 ------- 1 file changed, 7 deletions(-) diff --git a/contrib/openrc/yggdrasil b/contrib/openrc/yggdrasil index 4a2e0a13..aece8ecb 100755 --- a/contrib/openrc/yggdrasil +++ b/contrib/openrc/yggdrasil @@ -6,7 +6,6 @@ CONFFILE="/etc/yggdrasil.conf" pidfile="/run/${RC_SVCNAME}.pid" command="/usr/bin/yggdrasil" -extra_started_commands="reload" depend() { use net dns logger @@ -42,12 +41,6 @@ start() { eend $? } -reload() { - ebegin "Reloading ${RC_SVCNAME}" - start-stop-daemon --signal HUP --pidfile "${pidfile}" - eend $? -} - stop() { ebegin "Stopping ${RC_SVCNAME}" start-stop-daemon --stop --pidfile "${pidfile}" --exec "${command}" From 47818a1a7c475c3c214e3b25d961b77d55069086 Mon Sep 17 00:00:00 2001 From: Sergey Alirzaev Date: Tue, 15 Apr 2025 18:17:52 +0200 Subject: [PATCH 293/293] apparmor: add yggdrasilctl policy (#1235) --- contrib/apparmor/usr.bin.yggdrasilctl | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 contrib/apparmor/usr.bin.yggdrasilctl diff --git a/contrib/apparmor/usr.bin.yggdrasilctl b/contrib/apparmor/usr.bin.yggdrasilctl new file mode 100644 index 00000000..2f2c8366 --- /dev/null +++ b/contrib/apparmor/usr.bin.yggdrasilctl @@ -0,0 +1,11 @@ +# Last Modified: Mon Feb 3 22:19:45 2025 +include + +/usr/bin/yggdrasilctl { + include + + /etc/yggdrasil.conf rw, + /run/yggdrasil.sock rw, + owner /sys/kernel/mm/transparent_hugepage/hpage_pmd_size r, + +}