mirror of
https://github.com/yggdrasil-network/yggdrasil-go.git
synced 2025-08-26 23:17:52 +00:00
Compare commits
65 Commits
v0.5.0rc2
...
neil/mptcp
Author | SHA1 | Date | |
---|---|---|---|
![]() |
ed8991571a | ||
![]() |
fec96a38a4 | ||
![]() |
f788a18bef | ||
![]() |
fcefb20993 | ||
![]() |
2831d73f73 | ||
![]() |
c2811c0cdc | ||
![]() |
5d9c5b3c9b | ||
![]() |
f56f9c124c | ||
![]() |
5da1fbe397 | ||
![]() |
6f3a0a71d4 | ||
![]() |
6cbe56adfe | ||
![]() |
2d644eabc3 | ||
![]() |
2c20a04369 | ||
![]() |
81f2c711b4 | ||
![]() |
180d7bf499 | ||
![]() |
9f4c89acad | ||
![]() |
5da4c1131e | ||
![]() |
768278a8e6 | ||
![]() |
1e9a59edf9 | ||
![]() |
3dfa6d0cc9 | ||
![]() |
6b6cd0bed5 | ||
![]() |
3d15da34ad | ||
![]() |
741f825b8e | ||
![]() |
676ae52503 | ||
![]() |
fef553ed18 | ||
![]() |
f6f669617f | ||
![]() |
39c4b24395 | ||
![]() |
0d676c6a3b | ||
![]() |
a0b3897278 | ||
![]() |
abec2256ae | ||
![]() |
7aca869170 | ||
![]() |
b759683b76 | ||
![]() |
6677d70648 | ||
![]() |
7ac38e3e58 | ||
![]() |
49c424ef21 | ||
![]() |
0346af46da | ||
![]() |
93a5adfd18 | ||
![]() |
ddb75700a0 | ||
![]() |
ae997a5acb | ||
![]() |
6a9c90d3eb | ||
![]() |
41e045fe5b | ||
![]() |
e5e8c84d7c | ||
![]() |
e41b838d8f | ||
![]() |
7f9d4f3f6d | ||
![]() |
a6b316ef08 | ||
![]() |
d781fef760 | ||
![]() |
b332664acb | ||
![]() |
01c1498bd5 | ||
![]() |
0b578a637a | ||
![]() |
82c54f87ea | ||
![]() |
d17ac39789 | ||
![]() |
ea6ccf552f | ||
![]() |
1ac3d540e7 | ||
![]() |
6873fd44ff | ||
![]() |
8afa737a8d | ||
![]() |
7934158f5f | ||
![]() |
a60771344a | ||
![]() |
90c6288f7c | ||
![]() |
094f80f39c | ||
![]() |
955aa4af79 | ||
![]() |
73c6c25bd9 | ||
![]() |
80e56eafcd | ||
![]() |
6a9493757d | ||
![]() |
8ea20cd205 | ||
![]() |
a2dffeff33 |
50
.github/workflows/ci.yml
vendored
50
.github/workflows/ci.yml
vendored
@@ -15,10 +15,10 @@ 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
|
||||
- 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
|
||||
@@ -51,17 +51,17 @@ 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]
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.goversion }}
|
||||
|
||||
@@ -75,17 +75,17 @@ 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]
|
||||
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.goversion }}
|
||||
|
||||
@@ -99,17 +99,17 @@ 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]
|
||||
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.goversion }}
|
||||
|
||||
@@ -119,6 +119,32 @@ jobs:
|
||||
- name: Unit tests
|
||||
run: go test -v ./...
|
||||
|
||||
build-freebsd:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
goversion: ["1.21", "1.22"]
|
||||
goos:
|
||||
- freebsd
|
||||
- openbsd
|
||||
|
||||
name: Build (Cross ${{ matrix.goos }}, Go ${{ matrix.goversion }})
|
||||
needs: [lint]
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
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]
|
||||
|
41
.github/workflows/pkg.yml
vendored
41
.github/workflows/pkg.yml
vendored
@@ -16,16 +16,16 @@ jobs:
|
||||
|
||||
name: Package (Debian, ${{ matrix.pkgarch }})
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
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:
|
||||
@@ -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,14 +49,14 @@ jobs:
|
||||
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
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:
|
||||
@@ -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,20 +80,23 @@ jobs:
|
||||
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
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: Setup .NET Core SDK
|
||||
uses: actions/setup-dotnet@v4
|
||||
|
||||
- name: Build package
|
||||
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"
|
||||
@@ -107,22 +110,22 @@ jobs:
|
||||
|
||||
name: Package (Router, ${{ matrix.pkgarch }})
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
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
|
||||
|
||||
- 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:
|
||||
@@ -130,7 +133,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"
|
||||
|
87
CHANGELOG.md
87
CHANGELOG.md
@@ -26,7 +26,90 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||
- in case of vulnerabilities.
|
||||
-->
|
||||
|
||||
## [0.5.0] - Release Candidate 1
|
||||
## [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
|
||||
|
||||
* 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
|
||||
|
||||
* Fixed a crash that could happen when calculating the size of bloom filters during encoding
|
||||
|
||||
## [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
|
||||
|
||||
* 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
|
||||
|
||||
* Fix the Debian package so that upgrades are handled more smoothly
|
||||
|
||||
## [0.5.0] - 2023-10-28
|
||||
|
||||
### Added
|
||||
|
||||
@@ -126,7 +209,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
|
||||
|
||||
|
@@ -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.21 or later)
|
||||
2. Clone this repository
|
||||
2. Run `./build`
|
||||
|
||||
|
@@ -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)
|
||||
|
@@ -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()
|
||||
|
||||
@@ -67,7 +68,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:
|
||||
@@ -113,6 +114,7 @@ func main() {
|
||||
_ = f.Close()
|
||||
|
||||
case *genconf:
|
||||
cfg.AdminListen = ""
|
||||
var bs []byte
|
||||
if *confjson {
|
||||
bs, err = json.MarshalIndent(cfg, "", " ")
|
||||
@@ -154,7 +156,15 @@ func main() {
|
||||
fmt.Println(ipnet.String())
|
||||
return
|
||||
|
||||
case *getpkey:
|
||||
fmt.Println(hex.EncodeToString(publicKey))
|
||||
return
|
||||
|
||||
case *normaliseconf:
|
||||
cfg.AdminListen = ""
|
||||
if cfg.PrivateKeyPath != "" {
|
||||
cfg.PrivateKey = nil
|
||||
}
|
||||
var bs []byte
|
||||
if *confjson {
|
||||
bs, err = json.MarshalIndent(cfg, "", " ")
|
||||
@@ -178,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),
|
||||
@@ -205,13 +215,20 @@ 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.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.
|
||||
// Set up the admin socket.
|
||||
{
|
||||
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)
|
||||
}
|
||||
@@ -220,7 +237,7 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
// Setup the multicast module.
|
||||
// Set up the multicast module.
|
||||
{
|
||||
options := []multicast.SetupOption{}
|
||||
for _, intf := range cfg.MulticastInterfaces {
|
||||
@@ -241,7 +258,7 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
// Setup the TUN module.
|
||||
// Set up the TUN module.
|
||||
{
|
||||
options := []tun.SetupOption{
|
||||
tun.InterfaceName(cfg.IfName),
|
||||
|
@@ -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")
|
||||
|
@@ -174,26 +174,29 @@ 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"
|
||||
}
|
||||
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,
|
||||
(time.Duration(peer.Uptime) * time.Second).String(),
|
||||
rtt,
|
||||
peer.RXBytes.String(),
|
||||
peer.TXBytes.String(),
|
||||
fmt.Sprintf("%d", peer.Priority),
|
||||
|
BIN
contrib/.DS_Store
vendored
Normal file
BIN
contrib/.DS_Store
vendored
Normal file
Binary file not shown.
@@ -21,13 +21,16 @@ if [ $PKGBRANCH = "master" ]; then
|
||||
PKGREPLACES=yggdrasil-develop
|
||||
fi
|
||||
|
||||
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
|
||||
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 -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
|
||||
@@ -38,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/etc/systemd/system/
|
||||
mkdir -p /tmp/$PKGNAME/lib/systemd/system/
|
||||
|
||||
cat > /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
|
||||
lib/systemd/system/*.service 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/lib/systemd/system/yggdrasil-default-config.service
|
||||
cp contrib/systemd/yggdrasil.service.debian /tmp/$PKGNAME/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 .
|
||||
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
|
||||
|
||||
ar -r $PKGFILE \
|
||||
|
@@ -48,11 +48,12 @@ 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
|
||||
}
|
||||
// Setup the Yggdrasil node itself.
|
||||
// Set up the Yggdrasil node itself.
|
||||
{
|
||||
options := []core.SetupOption{}
|
||||
for _, peer := range m.config.Peers {
|
||||
@@ -70,16 +71,24 @@ 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 {
|
||||
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.
|
||||
// Set up 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{
|
||||
@@ -91,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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,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
|
||||
}
|
||||
|
@@ -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.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).
|
||||
#
|
||||
@@ -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
|
||||
|
13
contrib/systemd/yggdrasil-default-config.service.debian
Normal file
13
contrib/systemd/yggdrasil-default-config.service.debian
Normal file
@@ -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
|
25
contrib/systemd/yggdrasil.service.debian
Normal file
25
contrib/systemd/yggdrasil.service.debian
Normal file
@@ -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
|
32
go.mod
32
go.mod
@@ -1,39 +1,39 @@
|
||||
module github.com/yggdrasil-network/yggdrasil-go
|
||||
|
||||
go 1.20
|
||||
go 1.21
|
||||
|
||||
require (
|
||||
github.com/Arceliar/ironwood v0.0.0-20230805085300-86206813435f
|
||||
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
|
||||
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.0
|
||||
github.com/quic-go/quic-go v0.44.0
|
||||
github.com/vishvananda/netlink v1.1.0
|
||||
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.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.zx2c4.com/wireguard v0.0.0-20230223181233-21636207a675
|
||||
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/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
|
||||
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/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
|
||||
)
|
||||
|
||||
|
86
go.sum
86
go.sum
@@ -1,14 +1,14 @@
|
||||
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-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=
|
||||
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/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=
|
||||
github.com/cheggaaa/pb/v3 v3.1.4/go.mod h1:6wVjILNBaXMs8c21qRiaUM8BR82erfgau1DQ4iUXmSA=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
@@ -20,18 +20,21 @@ 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=
|
||||
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=
|
||||
@@ -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.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.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.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-20231006135142-2b44d11868fe h1:lrXv4yHeD9FA8PSJATWowP1QvexpyAPWmPia+Kbzql8=
|
||||
golang.org/x/mobile v0.0.0-20231006135142-2b44d11868fe/go.mod h1:BrnXpEObnFxpaT75Jo9hsCazwOWcp7nVIa8NNuH5cuA=
|
||||
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/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.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.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/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=
|
||||
@@ -104,26 +108,32 @@ 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.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||
golang.org/x/sys v0.13.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/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.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/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=
|
||||
@@ -132,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=
|
||||
|
@@ -24,11 +24,13 @@ type PeerEntry struct {
|
||||
PublicKey string `json:"key"`
|
||||
Port uint64 `json:"port"`
|
||||
Priority uint64 `json:"priority"`
|
||||
Multipath bool `json:"multipath,omitempty"`
|
||||
RXBytes DataUnit `json:"bytes_recvd,omitempty"`
|
||||
TXBytes DataUnit `json:"bytes_sent,omitempty"`
|
||||
Uptime float64 `json:"uptime,omitempty"`
|
||||
LastError string `json:"last_error,omitempty"`
|
||||
Latency time.Duration `json:"latency_ms,omitempty"`
|
||||
LastErrorTime time.Duration `json:"last_error_time,omitempty"`
|
||||
LastError string `json:"last_error,omitempty"`
|
||||
}
|
||||
|
||||
func (a *AdminSocket) getPeersHandler(req *GetPeersRequest, res *GetPeersResponse) error {
|
||||
@@ -36,14 +38,18 @@ func (a *AdminSocket) getPeersHandler(req *GetPeersRequest, res *GetPeersRespons
|
||||
res.Peers = make([]PeerEntry, 0, len(peers))
|
||||
for _, p := range peers {
|
||||
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(),
|
||||
Port: p.Port,
|
||||
Up: p.Up,
|
||||
Inbound: p.Inbound,
|
||||
Priority: uint64(p.Priority), // can't be uint8 thanks to gobind
|
||||
Multipath: p.Multipath,
|
||||
URI: p.URI,
|
||||
RXBytes: DataUnit(p.RXBytes),
|
||||
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)
|
||||
@@ -56,10 +62,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
|
||||
}
|
||||
|
@@ -1,9 +1,24 @@
|
||||
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) {
|
||||
switch v := opt.(type) {
|
||||
case ListenAddress:
|
||||
c.config.listenaddr = v
|
||||
case LogLookups:
|
||||
c.logLookups()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,3 +29,51 @@ type SetupOption interface {
|
||||
type ListenAddress string
|
||||
|
||||
func (a ListenAddress) isSetupOption() {}
|
||||
|
||||
type LogLookups struct{}
|
||||
|
||||
func (l LogLookups) isSetupOption() {}
|
||||
|
||||
func (a *AdminSocket) logLookups() {
|
||||
type resi struct {
|
||||
Address string `json:"addr"`
|
||||
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
|
||||
}
|
||||
type edk [ed25519.PublicKeySize]byte
|
||||
infos := make(map[edk]info)
|
||||
var m sync.Mutex
|
||||
a.core.PacketConn.PacketConn.Debug.SetDebugLookupLogger(func(l network.DebugLookupInfo) {
|
||||
var k edk
|
||||
copy(k[:], l.Key[:])
|
||||
m.Lock()
|
||||
infos[k] = 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)
|
||||
}
|
||||
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
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -40,17 +40,18 @@ 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."`
|
||||
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."`
|
||||
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."`
|
||||
}
|
||||
|
@@ -3,7 +3,6 @@ package core
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"sync/atomic"
|
||||
@@ -31,9 +30,11 @@ type PeerInfo struct {
|
||||
Coords []uint64
|
||||
Port uint64
|
||||
Priority uint8
|
||||
Multipath bool
|
||||
RXBytes uint64
|
||||
TXBytes uint64
|
||||
Uptime time.Duration
|
||||
Latency time.Duration
|
||||
}
|
||||
|
||||
type TreeEntryInfo struct {
|
||||
@@ -87,12 +88,14 @@ func (c *Core) GetPeers() []PeerInfo {
|
||||
peerinfo.RXBytes = atomic.LoadUint64(&c.rx)
|
||||
peerinfo.TXBytes = atomic.LoadUint64(&c.tx)
|
||||
peerinfo.Uptime = time.Since(c.up)
|
||||
peerinfo.Multipath = isMPTCP(c)
|
||||
}
|
||||
if p, ok := conns[conn]; ok {
|
||||
peerinfo.Key = p.Key
|
||||
peerinfo.Root = p.Root
|
||||
peerinfo.Port = p.Port
|
||||
peerinfo.Priority = p.Priority
|
||||
peerinfo.Latency = p.Latency
|
||||
}
|
||||
peers = append(peers, peerinfo)
|
||||
}
|
||||
@@ -192,28 +195,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,
|
||||
|
@@ -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.
|
||||
@@ -210,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)
|
||||
|
@@ -1,6 +1,3 @@
|
||||
//go:build debug
|
||||
// +build debug
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
@@ -8,28 +5,15 @@ 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)
|
||||
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 func() { fmt.Println(http.ListenAndServe(hostPort, nil)) }()
|
||||
go func() {
|
||||
fmt.Fprintf(os.Stderr, "DEBUG: %s", 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
|
||||
}
|
||||
|
227
src/core/link.go
227
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
|
||||
@@ -42,7 +44,7 @@ type links struct {
|
||||
|
||||
type linkProtocol interface {
|
||||
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)
|
||||
listen(ctx context.Context, url *url.URL, sintf string, options linkOptions) (net.Listener, error)
|
||||
}
|
||||
|
||||
// linkInfo is used as a map key
|
||||
@@ -53,13 +55,15 @@ 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
|
||||
_errtime time.Time // Last time an error occured
|
||||
_errtime time.Time // Last time an error occurred
|
||||
}
|
||||
|
||||
type linkOptions struct {
|
||||
@@ -67,6 +71,8 @@ type linkOptions struct {
|
||||
priority uint8
|
||||
tlsSNI string
|
||||
password []byte
|
||||
maxBackoff time.Duration
|
||||
multipath bool
|
||||
}
|
||||
|
||||
type Listener struct {
|
||||
@@ -129,10 +135,13 @@ 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")
|
||||
const ErrLinkUnrecognisedSchema = linkError("link schema unknown")
|
||||
const ErrLinkMaxBackoffInvalid = linkError("max backoff duration invalid")
|
||||
const ErrLinkMultipathInvalid = linkError("multipath invalid")
|
||||
|
||||
func (l *links) add(u *url.URL, sintf string, linkType linkType) error {
|
||||
var retErr error
|
||||
@@ -147,7 +156,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 {
|
||||
@@ -176,6 +187,41 @@ 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
|
||||
}
|
||||
if p := u.Query().Get("multipath"); p != "" {
|
||||
switch p {
|
||||
case "true", "1":
|
||||
options.multipath = true
|
||||
case "false", "0":
|
||||
options.multipath = false
|
||||
default:
|
||||
retErr = ErrLinkMultipathInvalid
|
||||
return
|
||||
}
|
||||
}
|
||||
// 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,
|
||||
@@ -199,6 +245,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
|
||||
@@ -215,37 +262,59 @@ 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++
|
||||
duration := time.Second * time.Duration(math.Exp2(float64(backoff)))
|
||||
if backoff < 32 {
|
||||
backoff++
|
||||
}
|
||||
duration := time.Second << backoff
|
||||
if duration > options.maxBackoff {
|
||||
duration = options.maxBackoff
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
// 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)
|
||||
if err != nil {
|
||||
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 || 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
|
||||
@@ -294,10 +363,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,
|
||||
@@ -319,13 +386,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) 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
|
||||
@@ -342,7 +435,36 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) {
|
||||
cancel()
|
||||
return nil, ErrLinkUnrecognisedSchema
|
||||
}
|
||||
listener, err := protocol.listen(ctx, u, sintf)
|
||||
|
||||
var options linkOptions
|
||||
if p := u.Query().Get("priority"); p != "" {
|
||||
pi, err := strconv.ParseUint(p, 10, 8)
|
||||
if err != nil {
|
||||
cancel()
|
||||
return nil, ErrLinkPriorityInvalid
|
||||
}
|
||||
options.priority = uint8(pi)
|
||||
}
|
||||
if p := u.Query().Get("password"); p != "" {
|
||||
if len(p) > blake2b.Size {
|
||||
cancel()
|
||||
return nil, ErrLinkPasswordInvalid
|
||||
}
|
||||
options.password = []byte(p)
|
||||
}
|
||||
if p := u.Query().Get("multipath"); p != "" {
|
||||
switch p {
|
||||
case "true", "1":
|
||||
options.multipath = true
|
||||
case "false", "0":
|
||||
options.multipath = false
|
||||
default:
|
||||
cancel()
|
||||
return nil, ErrLinkMultipathInvalid
|
||||
}
|
||||
}
|
||||
|
||||
listener, err := protocol.listen(ctx, u, sintf, options)
|
||||
if err != nil {
|
||||
cancel()
|
||||
return nil, err
|
||||
@@ -353,24 +475,9 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) {
|
||||
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)
|
||||
}
|
||||
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())
|
||||
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 {
|
||||
@@ -434,7 +541,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)
|
||||
}
|
||||
|
||||
@@ -453,30 +560,14 @@ 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":
|
||||
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
|
||||
@@ -485,10 +576,10 @@ 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 {
|
||||
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
|
||||
@@ -503,13 +594,14 @@ func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn) e
|
||||
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{}
|
||||
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)",
|
||||
@@ -558,6 +650,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 {
|
||||
|
@@ -3,6 +3,7 @@ package core
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"time"
|
||||
@@ -24,7 +25,7 @@ type linkQUICStream struct {
|
||||
}
|
||||
|
||||
type linkQUICListener struct {
|
||||
*quic.EarlyListener
|
||||
*quic.Listener
|
||||
ch <-chan *linkQUICStream
|
||||
}
|
||||
|
||||
@@ -50,11 +51,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
|
||||
}
|
||||
@@ -64,31 +65,35 @@ func (l *linkQUIC) dial(ctx context.Context, url *url.URL, info linkInfo, option
|
||||
}, 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)
|
||||
func (l *linkQUIC) listen(ctx context.Context, url *url.URL, _ string, _ linkOptions) (net.Listener, error) {
|
||||
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 {
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
@@ -2,6 +2,7 @@ package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
@@ -34,9 +35,18 @@ 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) {
|
||||
func (l *linkSOCKS) listen(ctx context.Context, url *url.URL, _ string, _ linkOptions) (net.Listener, error) {
|
||||
return nil, fmt.Errorf("SOCKS listener not supported")
|
||||
}
|
||||
|
@@ -36,7 +36,7 @@ type tcpDialer struct {
|
||||
addr *net.TCPAddr
|
||||
}
|
||||
|
||||
func (l *linkTCP) dialersFor(url *url.URL, info linkInfo) ([]*tcpDialer, error) {
|
||||
func (l *linkTCP) dialersFor(url *url.URL, info linkInfo, options linkOptions) ([]*tcpDialer, error) {
|
||||
host, p, err := net.SplitHostPort(url.Host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -55,7 +55,7 @@ func (l *linkTCP) dialersFor(url *url.URL, info linkInfo) ([]*tcpDialer, error)
|
||||
IP: ip,
|
||||
Port: port,
|
||||
}
|
||||
dialer, err := l.dialerFor(addr, info.sintf)
|
||||
dialer, err := l.dialerFor(addr, info.sintf, options.multipath)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
@@ -69,7 +69,7 @@ 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) {
|
||||
dialers, err := l.dialersFor(url, info)
|
||||
dialers, err := l.dialersFor(url, info, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -88,17 +88,21 @@ func (l *linkTCP) dial(ctx context.Context, url *url.URL, info linkInfo, options
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (l *linkTCP) listen(ctx context.Context, url *url.URL, sintf string) (net.Listener, error) {
|
||||
func (l *linkTCP) listen(ctx context.Context, url *url.URL, sintf string, options linkOptions) (net.Listener, error) {
|
||||
hostport := url.Host
|
||||
if sintf != "" {
|
||||
if host, port, err := net.SplitHostPort(hostport); err == nil {
|
||||
hostport = fmt.Sprintf("[%s%%%s]:%s", host, sintf, port)
|
||||
}
|
||||
}
|
||||
return l.listenconfig.Listen(ctx, "tcp", hostport)
|
||||
lc := *l.listenconfig
|
||||
if options.multipath {
|
||||
setMPTCPForListener(&lc)
|
||||
}
|
||||
return lc.Listen(ctx, "tcp", hostport)
|
||||
}
|
||||
|
||||
func (l *linkTCP) dialerFor(dst *net.TCPAddr, sintf string) (*net.Dialer, error) {
|
||||
func (l *linkTCP) dialerFor(dst *net.TCPAddr, sintf string, mptcp bool) (*net.Dialer, error) {
|
||||
if dst.IP.IsLinkLocalUnicast() {
|
||||
if sintf != "" {
|
||||
dst.Zone = sintf
|
||||
@@ -112,6 +116,9 @@ func (l *linkTCP) dialerFor(dst *net.TCPAddr, sintf string) (*net.Dialer, error)
|
||||
KeepAlive: -1,
|
||||
Control: l.tcpContext,
|
||||
}
|
||||
if mptcp {
|
||||
setMPTCPForDialer(dialer)
|
||||
}
|
||||
if sintf != "" {
|
||||
dialer.Control = l.getControl(sintf)
|
||||
ief, err := net.InterfaceByName(sintf)
|
||||
|
30
src/core/link_tcp_mptcp.go
Normal file
30
src/core/link_tcp_mptcp.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
)
|
||||
|
||||
func setMPTCPForDialer(d *net.Dialer) {
|
||||
d.SetMultipathTCP(true)
|
||||
}
|
||||
|
||||
func setMPTCPForListener(lc *net.ListenConfig) {
|
||||
lc.SetMultipathTCP(true)
|
||||
}
|
||||
|
||||
func isMPTCP(c net.Conn) bool {
|
||||
switch tc := c.(type) {
|
||||
case *net.TCPConn:
|
||||
mp, _ := tc.MultipathTCP()
|
||||
return mp
|
||||
case *tls.Conn:
|
||||
if tc, ok := tc.NetConn().(*net.TCPConn); ok {
|
||||
mp, _ := tc.MultipathTCP()
|
||||
return mp
|
||||
}
|
||||
return false
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
@@ -3,7 +3,6 @@ package core
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
|
||||
@@ -34,7 +33,7 @@ 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)
|
||||
dialers, err := l.tcp.dialersFor(url, info, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -58,17 +57,10 @@ func (l *linkTLS) dial(ctx context.Context, url *url.URL, info linkInfo, options
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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 {
|
||||
hostport = fmt.Sprintf("[%s%%%s]:%s", host, sintf, port)
|
||||
}
|
||||
}
|
||||
listener, err := l.listener.Listen(ctx, "tcp", hostport)
|
||||
func (l *linkTLS) listen(ctx context.Context, url *url.URL, sintf string, options linkOptions) (net.Listener, error) {
|
||||
listener, err := l.tcp.listen(ctx, url, sintf, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlslistener := tls.NewListener(listener, l.config)
|
||||
return tlslistener, nil
|
||||
return tls.NewListener(listener, l.config), nil
|
||||
}
|
||||
|
@@ -40,6 +40,6 @@ func (l *linkUNIX) dial(ctx context.Context, url *url.URL, info linkInfo, option
|
||||
return l.dialer.DialContext(ctx, "unix", addr.String())
|
||||
}
|
||||
|
||||
func (l *linkUNIX) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) {
|
||||
func (l *linkUNIX) listen(ctx context.Context, url *url.URL, _ string, _ linkOptions) (net.Listener, error) {
|
||||
return l.listener.Listen(ctx, "unix", url.Path)
|
||||
}
|
||||
|
@@ -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:
|
||||
|
41
src/core/options_test.go
Normal file
41
src/core/options_test.go
Normal file
@@ -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")
|
||||
}
|
||||
}
|
@@ -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)
|
||||
|
@@ -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.
|
||||
|
@@ -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)
|
||||
|
@@ -358,6 +358,7 @@ func (m *Multicast) _announce() {
|
||||
if linfo.interval.Seconds() < 15 {
|
||||
linfo.interval += time.Second
|
||||
}
|
||||
linfo.time = time.Now()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@@ -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 {
|
||||
|
@@ -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
|
||||
|
@@ -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 {
|
||||
@@ -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.
|
||||
|
@@ -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 {
|
||||
@@ -32,24 +33,27 @@ 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.
|
||||
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 {
|
||||
@@ -107,8 +111,8 @@ 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)
|
||||
return err
|
||||
tun.log.Errorf("Create AF_SYSTEM socket failed: %v.", err)
|
||||
return fmt.Errorf("failed to open AF_SYSTEM: %w", err)
|
||||
}
|
||||
|
||||
var ar in6_aliasreq
|
||||
@@ -146,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
|
||||
|
@@ -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 {
|
||||
@@ -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.
|
||||
@@ -42,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())
|
||||
|
@@ -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 {
|
||||
@@ -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.
|
||||
|
@@ -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)
|
||||
|
Reference in New Issue
Block a user