mirror of
https://github.com/yggdrasil-network/yggdrasil-go.git
synced 2025-08-26 21:17:36 +00:00
Compare commits
31 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
bec40ed9fb | ||
![]() |
75d2080e53 | ||
![]() |
834680045a | ||
![]() |
eef613993f | ||
![]() |
ff0ef7ff56 | ||
![]() |
ef110b0181 | ||
![]() |
b20ad846a1 | ||
![]() |
0b9c8bd020 | ||
![]() |
0b9469100c | ||
![]() |
a6429390da | ||
![]() |
1ee61dcefa | ||
![]() |
81e345c1ae | ||
![]() |
a038a6a8ef | ||
![]() |
01e73792fe | ||
![]() |
d22dc9ecc9 | ||
![]() |
874083da79 | ||
![]() |
ccda1075c0 | ||
![]() |
6d5243bd9a | ||
![]() |
377bc664c9 | ||
![]() |
d1b849588f | ||
![]() |
d6fd305f12 | ||
![]() |
98a6fdb4f2 | ||
![]() |
c00779c7d3 | ||
![]() |
43a1a3de64 | ||
![]() |
b8ab843a98 | ||
![]() |
e138fa679c | ||
![]() |
361b9fd6fc | ||
![]() |
5461bb380e | ||
![]() |
34f087de1c | ||
![]() |
c4b29b735c | ||
![]() |
947b6ad7aa |
12
.github/workflows/ci.yml
vendored
12
.github/workflows/ci.yml
vendored
@@ -17,10 +17,10 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.21
|
||||
go-version: stable
|
||||
- uses: actions/checkout@v4
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
uses: golangci/golangci-lint-action@v6
|
||||
with:
|
||||
args: --issues-exit-code=1
|
||||
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
goversion: ["1.21", "1.22"]
|
||||
goversion: ["1.21", "1.22", "1.23"]
|
||||
|
||||
name: Build & Test (Linux, Go ${{ matrix.goversion }})
|
||||
needs: [lint]
|
||||
@@ -75,7 +75,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
goversion: ["1.21", "1.22"]
|
||||
goversion: ["1.21", "1.22", "1.23"]
|
||||
|
||||
name: Build & Test (Windows, Go ${{ matrix.goversion }})
|
||||
needs: [lint]
|
||||
@@ -99,7 +99,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
goversion: ["1.21", "1.22"]
|
||||
goversion: ["1.21", "1.22", "1.23"]
|
||||
|
||||
name: Build & Test (macOS, Go ${{ matrix.goversion }})
|
||||
needs: [lint]
|
||||
@@ -123,7 +123,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
goversion: ["1.21", "1.22"]
|
||||
goversion: ["1.21", "1.22", "1.23"]
|
||||
goos:
|
||||
- freebsd
|
||||
- openbsd
|
||||
|
@@ -2,9 +2,10 @@ run:
|
||||
build-tags:
|
||||
- lint
|
||||
issues-exit-code: 0 # TODO: change this to 1 when we want it to fail builds
|
||||
skip-dirs:
|
||||
issues:
|
||||
exclude-dirs:
|
||||
- contrib/
|
||||
- misc/
|
||||
linters:
|
||||
disable:
|
||||
- gocyclo
|
||||
- gocyclo
|
||||
|
25
CHANGELOG.md
25
CHANGELOG.md
@@ -26,6 +26,31 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||
- in case of vulnerabilities.
|
||||
-->
|
||||
|
||||
## [0.5.9] - 2024-10-19
|
||||
|
||||
### Added
|
||||
|
||||
* New command line option `-user` for changing the process UID/GID
|
||||
|
||||
### Changed
|
||||
|
||||
* The routing algorithm has been updated with RTT-aware link costing, which should prefer lower latency links over higher latency links where possible
|
||||
* The calculated cost is an average of the link RTT, but newly established links are costed higher to begin with, such that unstable peerings can be avoided
|
||||
* Link costs are only used where multiple next-hops are available and will be ignored if there is only one loop-free path to the destination
|
||||
* This is protocol-compatible with existing v0.5.x nodes but will have the best results when peering with nodes that are also running the latest version
|
||||
* The `getPeers` endpoint will now report the calculated link cost for each given peer
|
||||
* Upgrade dependencies
|
||||
|
||||
### Fixed
|
||||
|
||||
* Multicast discovery should now work again when building Yggdrasil as an Android framework
|
||||
* Multicast discovery will now correctly ignore interfaces that are not marked as running
|
||||
* Ephemeral links, such as those added by multicast, will no longer try to reconnect in a fast loop, fixing a high CPU issue
|
||||
* The TUN interface will no longer stop working when hitting a segment read error from vectorised reads
|
||||
* The `AllowedPublicKeys` option will once again no longer apply to multicast peerings, as was originally intended
|
||||
* A potential panic when shutting down peering links has been fixed
|
||||
* A redundant system call for setting MTU on OpenBSD has been removed
|
||||
|
||||
## [0.5.8] - 2024-08-12
|
||||
|
||||
### Fixed
|
||||
|
10
cmd/yggdrasil/chuser_other.go
Normal file
10
cmd/yggdrasil/chuser_other.go
Normal file
@@ -0,0 +1,10 @@
|
||||
//go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris
|
||||
// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris
|
||||
|
||||
package main
|
||||
|
||||
import "errors"
|
||||
|
||||
func chuser(user string) error {
|
||||
return errors.New("setting uid/gid is not supported on this platform")
|
||||
}
|
93
cmd/yggdrasil/chuser_unix.go
Normal file
93
cmd/yggdrasil/chuser_unix.go
Normal file
@@ -0,0 +1,93 @@
|
||||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
|
||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
osuser "os/user"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func chuser(user string) error {
|
||||
group := ""
|
||||
if i := strings.IndexByte(user, ':'); i >= 0 {
|
||||
user, group = user[:i], user[i+1:]
|
||||
}
|
||||
|
||||
u := (*osuser.User)(nil)
|
||||
g := (*osuser.Group)(nil)
|
||||
|
||||
if user != "" {
|
||||
if _, err := strconv.ParseUint(user, 10, 32); err == nil {
|
||||
u, err = osuser.LookupId(user)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to lookup user by id %q: %v", user, err)
|
||||
}
|
||||
} else {
|
||||
u, err = osuser.Lookup(user)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to lookup user by name %q: %v", user, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if group != "" {
|
||||
if _, err := strconv.ParseUint(group, 10, 32); err == nil {
|
||||
g, err = osuser.LookupGroupId(group)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to lookup group by id %q: %v", user, err)
|
||||
}
|
||||
} else {
|
||||
g, err = osuser.LookupGroup(group)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to lookup group by name %q: %v", user, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if g != nil {
|
||||
gid, _ := strconv.ParseUint(g.Gid, 10, 32)
|
||||
var err error
|
||||
if gid < math.MaxInt {
|
||||
if err := syscall.Setgroups([]int{int(gid)}); err != nil {
|
||||
return fmt.Errorf("failed to setgroups %d: %v", gid, err)
|
||||
}
|
||||
err = syscall.Setgid(int(gid))
|
||||
} else {
|
||||
err = errors.New("gid too big")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to setgid %d: %v", gid, err)
|
||||
}
|
||||
} else if u != nil {
|
||||
gid, _ := strconv.ParseUint(u.Gid, 10, 32)
|
||||
if err := syscall.Setgroups([]int{int(uint32(gid))}); err != nil {
|
||||
return fmt.Errorf("failed to setgroups %d: %v", gid, err)
|
||||
}
|
||||
err := syscall.Setgid(int(uint32(gid)))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to setgid %d: %v", gid, err)
|
||||
}
|
||||
}
|
||||
|
||||
if u != nil {
|
||||
uid, _ := strconv.ParseUint(u.Uid, 10, 32)
|
||||
var err error
|
||||
if uid < math.MaxInt {
|
||||
err = syscall.Setuid(int(uid))
|
||||
} else {
|
||||
err = errors.New("uid too big")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to setuid %d: %v", uid, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@@ -52,11 +52,12 @@ func main() {
|
||||
getsnet := flag.Bool("subnet", false, "use in combination with either -useconf or -useconffile, outputs your IPv6 subnet")
|
||||
getpkey := flag.Bool("publickey", false, "use in combination with either -useconf or -useconffile, outputs your public key")
|
||||
loglevel := flag.String("loglevel", "info", "loglevel to enable")
|
||||
chuserto := flag.String("user", "", "user (and, optionally, group) to set UID/GID to")
|
||||
flag.Parse()
|
||||
|
||||
|
||||
done := make(chan struct{})
|
||||
defer close(done)
|
||||
|
||||
|
||||
// Catch interrupts from the operating system to exit gracefully.
|
||||
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
||||
|
||||
@@ -271,7 +272,7 @@ func main() {
|
||||
n.tun.SetupAdminHandlers(n.admin)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Windows service shutdown
|
||||
minwinsvc.SetOnExit(func() {
|
||||
logger.Infof("Shutting down service ...")
|
||||
@@ -280,6 +281,14 @@ func main() {
|
||||
<-done
|
||||
})
|
||||
|
||||
// Change user if requested
|
||||
if *chuserto != "" {
|
||||
err = chuser(*chuserto)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Block until we are told to shut down.
|
||||
<-ctx.Done()
|
||||
|
||||
|
@@ -38,7 +38,6 @@ func (cmdLineEnv *CmdLineEnv) parseFlagsAndArgs() {
|
||||
fmt.Println("Examples:")
|
||||
fmt.Println(" - ", os.Args[0], "list")
|
||||
fmt.Println(" - ", os.Args[0], "getPeers")
|
||||
fmt.Println(" - ", os.Args[0], "setTunTap name=auto mtu=1500 tap_mode=false")
|
||||
fmt.Println(" - ", os.Args[0], "-endpoint=tcp://localhost:9001 getPeers")
|
||||
fmt.Println(" - ", os.Args[0], "-endpoint=unix:///var/run/ygg.sock getPeers")
|
||||
}
|
||||
|
@@ -174,7 +174,7 @@ func run() int {
|
||||
if err := json.Unmarshal(recv.Response, &resp); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
table.SetHeader([]string{"URI", "State", "Dir", "IP Address", "Uptime", "RTT", "RX", "TX", "Pr", "Last Error"})
|
||||
table.SetHeader([]string{"URI", "State", "Dir", "IP Address", "Uptime", "RTT", "RX", "TX", "Pr", "Cost", "Last Error"})
|
||||
for _, peer := range resp.Peers {
|
||||
state, lasterr, dir, rtt := "Up", "-", "Out", "-"
|
||||
if !peer.Up {
|
||||
@@ -200,6 +200,7 @@ func run() int {
|
||||
peer.RXBytes.String(),
|
||||
peer.TXBytes.String(),
|
||||
fmt.Sprintf("%d", peer.Priority),
|
||||
fmt.Sprintf("%d", peer.Cost),
|
||||
lasterr,
|
||||
})
|
||||
}
|
||||
|
@@ -50,11 +50,12 @@ echo 9 > /tmp/$PKGNAME/debian/compat
|
||||
cat > /tmp/$PKGNAME/debian/control << EOF
|
||||
Package: $PKGNAME
|
||||
Version: $PKGVERSION
|
||||
Section: contrib/net
|
||||
Priority: extra
|
||||
Section: golang
|
||||
Priority: optional
|
||||
Architecture: $PKGARCH
|
||||
Replaces: $PKGREPLACES
|
||||
Conflicts: $PKGREPLACES
|
||||
Depends: systemd
|
||||
Maintainer: Neil Alexander <neilalexander@users.noreply.github.com>
|
||||
Description: Yggdrasil Network
|
||||
Yggdrasil is an early-stage implementation of a fully end-to-end encrypted IPv6
|
||||
@@ -102,7 +103,7 @@ then
|
||||
|
||||
echo "Normalising and updating /etc/yggdrasil/yggdrasil.conf"
|
||||
/usr/bin/yggdrasil -useconf -normaliseconf < /var/backups/yggdrasil.conf.`date +%Y%m%d` > /etc/yggdrasil/yggdrasil.conf
|
||||
|
||||
|
||||
chown root:yggdrasil /etc/yggdrasil/yggdrasil.conf
|
||||
chmod 640 /etc/yggdrasil/yggdrasil.conf
|
||||
else
|
||||
|
@@ -7,6 +7,7 @@ set -ef
|
||||
PKGSRC=${PKGSRC:-github.com/yggdrasil-network/yggdrasil-go/src/version}
|
||||
PKGNAME=${PKGNAME:-$(sh contrib/semver/name.sh)}
|
||||
PKGVER=${PKGVER:-$(sh contrib/semver/version.sh --bare)}
|
||||
GOVER=$(go version | { read _ _ version _; echo ${version#go}; })
|
||||
|
||||
LDFLAGS="-X $PKGSRC.buildName=$PKGNAME -X $PKGSRC.buildVersion=$PKGVER"
|
||||
ARGS="-v"
|
||||
@@ -33,6 +34,15 @@ if [ ! $IOS ] && [ ! $ANDROID ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ver_le() {
|
||||
printf "$1\n$2\n" | sort -VC
|
||||
}
|
||||
|
||||
if [ $ANDROID ] && ver_le 1.23.0 $GOVER ; then
|
||||
# github.com/wlynxg/anet library relies on //go:linkname
|
||||
LDFLAGS="$LDFLAGS -checklinkname=0"
|
||||
fi
|
||||
|
||||
if [ $IOS ]; then
|
||||
echo "Building framework for iOS"
|
||||
go get golang.org/x/mobile/bind
|
||||
|
@@ -15,8 +15,6 @@ import (
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/multicast"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/tun"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/version"
|
||||
|
||||
_ "golang.org/x/mobile/bind"
|
||||
)
|
||||
|
||||
// Yggdrasil mobile package is meant to "plug the gap" for mobile support, as
|
||||
|
24
go.mod
24
go.mod
@@ -3,23 +3,24 @@ module github.com/yggdrasil-network/yggdrasil-go
|
||||
go 1.21
|
||||
|
||||
require (
|
||||
github.com/Arceliar/ironwood v0.0.0-20240529054413-b8e59574e2b2
|
||||
github.com/Arceliar/ironwood v0.0.0-20241016082300-f6fb9da97a17
|
||||
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d
|
||||
github.com/cheggaaa/pb/v3 v3.1.5
|
||||
github.com/coder/websocket v1.8.12
|
||||
github.com/gologme/log v1.3.0
|
||||
github.com/hashicorp/go-syslog v1.0.0
|
||||
github.com/hjson/hjson-go/v4 v4.4.0
|
||||
github.com/kardianos/minwinsvc v1.0.2
|
||||
github.com/quic-go/quic-go v0.45.1
|
||||
github.com/vishvananda/netlink v1.1.0
|
||||
golang.org/x/crypto v0.25.0
|
||||
golang.org/x/mobile v0.0.0-20240716161057-1ad2df20a8b6
|
||||
golang.org/x/net v0.27.0
|
||||
golang.org/x/sys v0.22.0
|
||||
golang.org/x/text v0.16.0
|
||||
github.com/quic-go/quic-go v0.46.0
|
||||
github.com/vishvananda/netlink v1.3.0
|
||||
github.com/wlynxg/anet v0.0.5
|
||||
golang.org/x/crypto v0.28.0
|
||||
golang.org/x/net v0.30.0
|
||||
golang.org/x/sys v0.26.0
|
||||
golang.org/x/text v0.19.0
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
|
||||
golang.zx2c4.com/wireguard/windows v0.5.3
|
||||
nhooyr.io/websocket v1.8.11
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -33,9 +34,8 @@ require (
|
||||
go.uber.org/mock v0.4.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
|
||||
golang.org/x/mod v0.19.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/tools v0.23.0 // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -44,5 +44,5 @@ require (
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f // indirect
|
||||
github.com/vishvananda/netns v0.0.4 // indirect
|
||||
)
|
||||
|
49
go.sum
49
go.sum
@@ -1,5 +1,5 @@
|
||||
github.com/Arceliar/ironwood v0.0.0-20240529054413-b8e59574e2b2 h1:SBdYBKeXYUUFef5wi2CMhYmXFVGiYaRpTvbki0Bu+JQ=
|
||||
github.com/Arceliar/ironwood v0.0.0-20240529054413-b8e59574e2b2/go.mod h1:6WP4799FX0OuWdENGQAh+0RXp9FLh0y7NZ7tM9cJyXk=
|
||||
github.com/Arceliar/ironwood v0.0.0-20241016082300-f6fb9da97a17 h1:uOvHqPwu09ndYZQDUL6QvyDcz0M9kwooKYa/PEfLwIU=
|
||||
github.com/Arceliar/ironwood v0.0.0-20241016082300-f6fb9da97a17/go.mod h1:6WP4799FX0OuWdENGQAh+0RXp9FLh0y7NZ7tM9cJyXk=
|
||||
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM=
|
||||
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q=
|
||||
github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
|
||||
@@ -14,6 +14,8 @@ github.com/cheggaaa/pb/v3 v3.1.5/go.mod h1:CrxkeghYTXi1lQBEI7jSn+3svI3cuc19haAj6
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo=
|
||||
github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -56,8 +58,8 @@ github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
|
||||
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/quic-go/quic-go v0.45.1 h1:tPfeYCk+uZHjmDRwHHQmvHRYL2t44ROTujLeFVBmjCA=
|
||||
github.com/quic-go/quic-go v0.45.1/go.mod h1:1dLehS7TIR64+vxGR70GDcatWTOtMX2PUtnKsjbTurI=
|
||||
github.com/quic-go/quic-go v0.46.0 h1:uuwLClEEyk1DNvchH8uCByQVjo3yKL9opKulExNDs7Y=
|
||||
github.com/quic-go/quic-go v0.46.0/go.mod h1:1dLehS7TIR64+vxGR70GDcatWTOtMX2PUtnKsjbTurI=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@@ -65,11 +67,12 @@ github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg=
|
||||
github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=
|
||||
github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0=
|
||||
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
|
||||
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
|
||||
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f h1:p4VB7kIXpOQvVn1ZaTIVp+3vuYAXFe3OJEvjbUYJLaA=
|
||||
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||
github.com/vishvananda/netlink v1.3.0 h1:X7l42GfcV4S6E4vHTsw48qbrV+9PVojNfIhZcwQdrZk=
|
||||
github.com/vishvananda/netlink v1.3.0/go.mod h1:i6NetklAujEcC6fK0JPjT8qSwWyO0HLn4UKG+hGqeJs=
|
||||
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
|
||||
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||
github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU=
|
||||
github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
|
||||
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
||||
@@ -77,12 +80,10 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
||||
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
||||
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
||||
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||
golang.org/x/mobile v0.0.0-20240716161057-1ad2df20a8b6 h1:/VlmIrkuLf2wzPjkZ8imSpckHoW7Y71h66dxbLHSpi8=
|
||||
golang.org/x/mobile v0.0.0-20240716161057-1ad2df20a8b6/go.mod h1:TCsc78+c4cqb8IKEosz2LwJ6YRNkIjMuAYeHYjchGDE=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
|
||||
@@ -93,29 +94,29 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
||||
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
||||
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
@@ -129,8 +130,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -154,5 +155,3 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 h1:TbRPT0HtzFP3Cno1zZo7yPzEEnfu8EjLfl6IU9VfqkQ=
|
||||
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259/go.mod h1:AVgIgHMwK63XvmAzWG9vLQ41YnVHN0du0tEC46fI7yY=
|
||||
nhooyr.io/websocket v1.8.11 h1:f/qXNc2/3DpoSZkHt1DQu6rj4zGC8JmkkLkWss0MgN0=
|
||||
nhooyr.io/websocket v1.8.11/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c=
|
||||
|
@@ -83,6 +83,52 @@ func New(c *core.Core, log core.Logger, opts ...SetupOption) (*AdminSocket, erro
|
||||
if a.config.listenaddr == "none" || a.config.listenaddr == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
listenaddr := string(a.config.listenaddr)
|
||||
u, err := url.Parse(listenaddr)
|
||||
if err == nil {
|
||||
switch strings.ToLower(u.Scheme) {
|
||||
case "unix":
|
||||
if _, err := os.Stat(u.Path); err == nil {
|
||||
a.log.Debugln("Admin socket", u.Path, "already exists, trying to clean up")
|
||||
if _, err := net.DialTimeout("unix", u.Path, time.Second*2); err == nil || err.(net.Error).Timeout() {
|
||||
a.log.Errorln("Admin socket", u.Path, "already exists and is in use by another process")
|
||||
os.Exit(1)
|
||||
} else {
|
||||
if err := os.Remove(u.Path); err == nil {
|
||||
a.log.Debugln(u.Path, "was cleaned up")
|
||||
} else {
|
||||
a.log.Errorln(u.Path, "already exists and was not cleaned up:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
a.listener, err = net.Listen("unix", u.Path)
|
||||
if err == nil {
|
||||
switch u.Path[:1] {
|
||||
case "@": // maybe abstract namespace
|
||||
default:
|
||||
if err := os.Chmod(u.Path, 0660); err != nil {
|
||||
a.log.Warnln("WARNING:", u.Path, "may have unsafe permissions!")
|
||||
}
|
||||
}
|
||||
}
|
||||
case "tcp":
|
||||
a.listener, err = net.Listen("tcp", u.Host)
|
||||
default:
|
||||
a.listener, err = net.Listen("tcp", listenaddr)
|
||||
}
|
||||
} else {
|
||||
a.listener, err = net.Listen("tcp", listenaddr)
|
||||
}
|
||||
if err != nil {
|
||||
a.log.Errorf("Admin socket failed to listen: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
a.log.Infof("%s admin socket listening on %s",
|
||||
strings.ToUpper(a.listener.Addr().Network()),
|
||||
a.listener.Addr().String())
|
||||
|
||||
_ = a.AddHandler("list", "List available commands", []string{}, func(_ json.RawMessage) (interface{}, error) {
|
||||
res := &ListResponse{}
|
||||
for name, handler := range a.handlers {
|
||||
@@ -233,50 +279,6 @@ func (a *AdminSocket) Stop() error {
|
||||
|
||||
// listen is run by start and manages API connections.
|
||||
func (a *AdminSocket) listen() {
|
||||
listenaddr := string(a.config.listenaddr)
|
||||
u, err := url.Parse(listenaddr)
|
||||
if err == nil {
|
||||
switch strings.ToLower(u.Scheme) {
|
||||
case "unix":
|
||||
if _, err := os.Stat(listenaddr[7:]); err == nil {
|
||||
a.log.Debugln("Admin socket", listenaddr[7:], "already exists, trying to clean up")
|
||||
if _, err := net.DialTimeout("unix", listenaddr[7:], time.Second*2); err == nil || err.(net.Error).Timeout() {
|
||||
a.log.Errorln("Admin socket", listenaddr[7:], "already exists and is in use by another process")
|
||||
os.Exit(1)
|
||||
} else {
|
||||
if err := os.Remove(listenaddr[7:]); err == nil {
|
||||
a.log.Debugln(listenaddr[7:], "was cleaned up")
|
||||
} else {
|
||||
a.log.Errorln(listenaddr[7:], "already exists and was not cleaned up:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
a.listener, err = net.Listen("unix", listenaddr[7:])
|
||||
if err == nil {
|
||||
switch listenaddr[7:8] {
|
||||
case "@": // maybe abstract namespace
|
||||
default:
|
||||
if err := os.Chmod(listenaddr[7:], 0660); err != nil {
|
||||
a.log.Warnln("WARNING:", listenaddr[:7], "may have unsafe permissions!")
|
||||
}
|
||||
}
|
||||
}
|
||||
case "tcp":
|
||||
a.listener, err = net.Listen("tcp", u.Host)
|
||||
default:
|
||||
a.listener, err = net.Listen("tcp", listenaddr)
|
||||
}
|
||||
} else {
|
||||
a.listener, err = net.Listen("tcp", listenaddr)
|
||||
}
|
||||
if err != nil {
|
||||
a.log.Errorf("Admin socket failed to listen: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
a.log.Infof("%s admin socket listening on %s",
|
||||
strings.ToUpper(a.listener.Addr().Network()),
|
||||
a.listener.Addr().String())
|
||||
defer a.listener.Close()
|
||||
for {
|
||||
conn, err := a.listener.Accept()
|
||||
|
@@ -3,7 +3,7 @@ package admin
|
||||
import (
|
||||
"encoding/hex"
|
||||
"net"
|
||||
"sort"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||
@@ -35,8 +35,8 @@ func (a *AdminSocket) getPathsHandler(_ *GetPathsRequest, res *GetPathsResponse)
|
||||
Sequence: p.Sequence,
|
||||
})
|
||||
}
|
||||
sort.SliceStable(res.Paths, func(i, j int) bool {
|
||||
return strings.Compare(res.Paths[i].PublicKey, res.Paths[j].PublicKey) < 0
|
||||
slices.SortStableFunc(res.Paths, func(a, b PathEntry) int {
|
||||
return strings.Compare(a.PublicKey, b.PublicKey)
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
@@ -3,7 +3,8 @@ package admin
|
||||
import (
|
||||
"encoding/hex"
|
||||
"net"
|
||||
"sort"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||
@@ -24,6 +25,7 @@ type PeerEntry struct {
|
||||
PublicKey string `json:"key"`
|
||||
Port uint64 `json:"port"`
|
||||
Priority uint64 `json:"priority"`
|
||||
Cost uint64 `json:"cost"`
|
||||
RXBytes DataUnit `json:"bytes_recvd,omitempty"`
|
||||
TXBytes DataUnit `json:"bytes_sent,omitempty"`
|
||||
Uptime float64 `json:"uptime,omitempty"`
|
||||
@@ -41,6 +43,7 @@ func (a *AdminSocket) getPeersHandler(_ *GetPeersRequest, res *GetPeersResponse)
|
||||
Up: p.Up,
|
||||
Inbound: p.Inbound,
|
||||
Priority: uint64(p.Priority), // can't be uint8 thanks to gobind
|
||||
Cost: p.Cost,
|
||||
URI: p.URI,
|
||||
RXBytes: DataUnit(p.RXBytes),
|
||||
TXBytes: DataUnit(p.TXBytes),
|
||||
@@ -59,17 +62,26 @@ func (a *AdminSocket) getPeersHandler(_ *GetPeersRequest, res *GetPeersResponse)
|
||||
}
|
||||
res.Peers = append(res.Peers, peer)
|
||||
}
|
||||
sort.Slice(res.Peers, func(i, j int) bool {
|
||||
if res.Peers[i].Inbound == res.Peers[j].Inbound {
|
||||
if res.Peers[i].PublicKey == res.Peers[j].PublicKey {
|
||||
if res.Peers[i].Priority == res.Peers[j].Priority {
|
||||
return res.Peers[i].Uptime > res.Peers[j].Uptime
|
||||
}
|
||||
return res.Peers[i].Priority < res.Peers[j].Priority
|
||||
}
|
||||
return res.Peers[i].PublicKey < res.Peers[j].PublicKey
|
||||
slices.SortStableFunc(res.Peers, func(a, b PeerEntry) int {
|
||||
if !a.Inbound && b.Inbound {
|
||||
return -1
|
||||
}
|
||||
return !res.Peers[i].Inbound && res.Peers[j].Inbound
|
||||
if a.Inbound && !b.Inbound {
|
||||
return 1
|
||||
}
|
||||
if d := strings.Compare(a.PublicKey, b.PublicKey); d != 0 {
|
||||
return d
|
||||
}
|
||||
if d := a.Priority - b.Priority; d != 0 {
|
||||
return int(d)
|
||||
}
|
||||
if d := a.Cost - b.Cost; d != 0 {
|
||||
return int(d)
|
||||
}
|
||||
if d := a.Uptime - b.Uptime; d != 0 {
|
||||
return int(d)
|
||||
}
|
||||
return 0
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@ package admin
|
||||
import (
|
||||
"encoding/hex"
|
||||
"net"
|
||||
"sort"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||
@@ -36,8 +36,8 @@ func (a *AdminSocket) getSessionsHandler(_ *GetSessionsRequest, res *GetSessions
|
||||
Uptime: s.Uptime.Seconds(),
|
||||
})
|
||||
}
|
||||
sort.SliceStable(res.Sessions, func(i, j int) bool {
|
||||
return strings.Compare(res.Sessions[i].PublicKey, res.Sessions[j].PublicKey) < 0
|
||||
slices.SortStableFunc(res.Sessions, func(a, b SessionEntry) int {
|
||||
return strings.Compare(a.PublicKey, b.PublicKey)
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@ package admin
|
||||
import (
|
||||
"encoding/hex"
|
||||
"net"
|
||||
"sort"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||
@@ -34,8 +34,8 @@ func (a *AdminSocket) getTreeHandler(_ *GetTreeRequest, res *GetTreeResponse) er
|
||||
Sequence: d.Sequence,
|
||||
})
|
||||
}
|
||||
sort.SliceStable(res.Tree, func(i, j int) bool {
|
||||
return strings.Compare(res.Tree[i].PublicKey, res.Tree[j].PublicKey) < 0
|
||||
slices.SortStableFunc(res.Tree, func(a, b TreeEntry) int {
|
||||
return strings.Compare(a.PublicKey, b.PublicKey)
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
@@ -43,17 +43,17 @@ type NodeConfig struct {
|
||||
PrivateKey KeyBytes `json:",omitempty" comment:"Your private key. DO NOT share this with anyone!"`
|
||||
PrivateKeyPath string `json:",omitempty" comment:"The path to your private key file in PEM format."`
|
||||
Certificate *tls.Certificate `json:"-"`
|
||||
Peers []string `comment:"List of connection strings for outbound peer connections in URI format,\ne.g. tls://a.b.c.d:e or socks://a.b.c.d:e/f.g.h.i:j. These connections\nwill obey the operating system routing table, therefore you should\nuse this section when you may connect via different interfaces."`
|
||||
InterfacePeers map[string][]string `comment:"List of connection strings for outbound peer connections in URI format,\narranged by source interface, e.g. { \"eth0\": [ \"tls://a.b.c.d:e\" ] }.\nNote that SOCKS peerings will NOT be affected by this option and should\ngo in the \"Peers\" section instead."`
|
||||
Listen []string `comment:"Listen addresses for incoming connections. You will need to add\nlisteners in order to accept incoming peerings from non-local nodes.\nMulticast peer discovery will work regardless of any listeners set\nhere. Each listener should be specified in URI format as above, e.g.\ntls://0.0.0.0:0 or tls://[::]:0 to listen on all interfaces."`
|
||||
Peers []string `comment:"List of outbound peer connection strings (e.g. tls://a.b.c.d:e or\nsocks://a.b.c.d:e/f.g.h.i:j). Connection strings can contain options,\nsee https://yggdrasil-network.github.io/configurationref.html#peers.\nYggdrasil has no concept of bootstrap nodes - all network traffic\nwill transit peer connections. Therefore make sure to only peer with\nnearby nodes that have good connectivity and low latency. Avoid adding\npeers to this list from distant countries as this will worsen your\nnode's connectivity and performance considerably."`
|
||||
InterfacePeers map[string][]string `comment:"List of connection strings for outbound peer connections in URI format,\narranged by source interface, e.g. { \"eth0\": [ \"tls://a.b.c.d:e\" ] }.\nYou should only use this option if your machine is multi-homed and you\nwant to establish outbound peer connections on different interfaces.\nOtherwise you should use \"Peers\"."`
|
||||
Listen []string `comment:"Listen addresses for incoming connections. You will need to add\nlisteners in order to accept incoming peerings from non-local nodes.\nThis is not required if you wish to establish outbound peerings only.\nMulticast peer discovery will work regardless of any listeners set\nhere. Each listener should be specified in URI format as above, e.g.\ntls://0.0.0.0:0 or tls://[::]:0 to listen on all interfaces."`
|
||||
AdminListen string `json:",omitempty" comment:"Listen address for admin connections. Default is to listen for local\nconnections either on TCP/9001 or a UNIX socket depending on your\nplatform. Use this value for yggdrasilctl -endpoint=X. To disable\nthe admin socket, use the value \"none\" instead."`
|
||||
MulticastInterfaces []MulticastInterfaceConfig `comment:"Configuration for which interfaces multicast peer discovery should be\nenabled on. Each entry in the list should be a json object which may\ncontain Regex, Beacon, Listen, and Port. Regex is a regular expression\nwhich is matched against an interface name, and interfaces use the\nfirst configuration that they match gainst. Beacon configures whether\nor not the node should send link-local multicast beacons to advertise\ntheir presence, while listening for incoming connections on Port.\nListen controls whether or not the node listens for multicast beacons\nand opens outgoing connections."`
|
||||
AllowedPublicKeys []string `comment:"List of peer public keys to allow incoming peering connections\nfrom. If left empty/undefined then all connections will be allowed\nby default. This does not affect outgoing peerings, nor does it\naffect link-local peers discovered via multicast."`
|
||||
AllowedPublicKeys []string `comment:"List of peer public keys to allow incoming peering connections\nfrom. If left empty/undefined then all connections will be allowed\nby default. This does not affect outgoing peerings, nor does it\naffect link-local peers discovered via multicast.\nWARNING: THIS IS NOT A FIREWALL and DOES NOT limit who can reach\nopen ports or services running on your machine!"`
|
||||
IfName string `comment:"Local network interface name for TUN adapter, or \"auto\" to select\nan interface automatically, or \"none\" to run without TUN."`
|
||||
IfMTU uint64 `comment:"Maximum Transmission Unit (MTU) size for your local TUN interface.\nDefault is the largest supported size for your platform. The lowest\npossible value is 1280."`
|
||||
LogLookups bool `json:",omitempty"`
|
||||
NodeInfoPrivacy bool `comment:"By default, nodeinfo contains some defaults including the platform,\narchitecture and Yggdrasil version. These can help when surveying\nthe network and diagnosing network routing problems. Enabling\nnodeinfo privacy prevents this, so that only items specified in\n\"NodeInfo\" are sent back if specified."`
|
||||
NodeInfo map[string]interface{} `comment:"Optional node info. This must be a { \"key\": \"value\", ... } map\nor set as null. This is entirely optional but, if set, is visible\nto the whole network on request."`
|
||||
NodeInfo map[string]interface{} `comment:"Optional nodeinfo. This must be a { \"key\": \"value\", ... } map\nor set as null. This is entirely optional but, if set, is visible\nto the whole network on request."`
|
||||
}
|
||||
|
||||
type MulticastInterfaceConfig struct {
|
||||
|
@@ -30,6 +30,7 @@ type PeerInfo struct {
|
||||
Coords []uint64
|
||||
Port uint64
|
||||
Priority uint8
|
||||
Cost uint64
|
||||
RXBytes uint64
|
||||
TXBytes uint64
|
||||
Uptime time.Duration
|
||||
@@ -94,6 +95,7 @@ func (c *Core) GetPeers() []PeerInfo {
|
||||
peerinfo.Port = p.Port
|
||||
peerinfo.Priority = p.Priority
|
||||
peerinfo.Latency = p.Latency
|
||||
peerinfo.Cost = p.Cost
|
||||
}
|
||||
peers = append(peers, peerinfo)
|
||||
}
|
||||
@@ -148,7 +150,14 @@ func (c *Core) GetSessions() []SessionInfo {
|
||||
// parsed from a string of the form e.g. "tcp://a.b.c.d:e". In the case of a
|
||||
// link-local address, the interface should be provided as the second argument.
|
||||
func (c *Core) Listen(u *url.URL, sintf string) (*Listener, error) {
|
||||
return c.links.listen(u, sintf)
|
||||
return c.links.listen(u, sintf, false)
|
||||
}
|
||||
|
||||
// ListenLocal starts a listener, like the Listen function, but is used for
|
||||
// more trustworthy situations where you want to ignore AllowedPublicKeys, i.e.
|
||||
// with multicast listeners.
|
||||
func (c *Core) ListenLocal(u *url.URL, sintf string) (*Listener, error) {
|
||||
return c.links.listen(u, sintf, true)
|
||||
}
|
||||
|
||||
// Address gets the IPv6 address of the Yggdrasil node. This is always a /128
|
||||
|
@@ -127,7 +127,7 @@ func New(cert *tls.Certificate, logger Logger, opts ...SetupOption) (*Core, erro
|
||||
c.log.Errorf("Invalid listener URI %q specified, ignoring\n", listenaddr)
|
||||
continue
|
||||
}
|
||||
if _, err = c.links.listen(u, ""); err != nil {
|
||||
if _, err = c.links.listen(u, "", false); err != nil {
|
||||
c.log.Errorf("Failed to start listener %q: %s\n", listenaddr, err)
|
||||
}
|
||||
}
|
||||
|
@@ -25,6 +25,27 @@ func GetLoggerWithPrefix(prefix string, verbose bool) *log.Logger {
|
||||
return l
|
||||
}
|
||||
|
||||
func require_NoError(t *testing.T, err error) {
|
||||
t.Helper()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func require_Equal[T comparable](t *testing.T, a, b T) {
|
||||
t.Helper()
|
||||
if a != b {
|
||||
t.Fatalf("%v != %v", a, b)
|
||||
}
|
||||
}
|
||||
|
||||
func require_True(t *testing.T, a bool) {
|
||||
t.Helper()
|
||||
if !a {
|
||||
t.Fatal("expected true")
|
||||
}
|
||||
}
|
||||
|
||||
// CreateAndConnectTwo creates two nodes. nodeB connects to nodeA.
|
||||
// Verbosity flag is passed to logger.
|
||||
func CreateAndConnectTwo(t testing.TB, verbose bool) (nodeA *Core, nodeB *Core) {
|
||||
@@ -201,3 +222,69 @@ func BenchmarkCore_Start_Transfer(b *testing.B) {
|
||||
}
|
||||
<-done
|
||||
}
|
||||
|
||||
func TestAllowedPublicKeys(t *testing.T) {
|
||||
logger := GetLoggerWithPrefix("", false)
|
||||
cfgA, cfgB := config.GenerateConfig(), config.GenerateConfig()
|
||||
require_NoError(t, cfgA.GenerateSelfSignedCertificate())
|
||||
require_NoError(t, cfgB.GenerateSelfSignedCertificate())
|
||||
|
||||
nodeA, err := New(cfgA.Certificate, logger, AllowedPublicKey("abcdef"))
|
||||
require_NoError(t, err)
|
||||
defer nodeA.Stop()
|
||||
|
||||
nodeB, err := New(cfgB.Certificate, logger)
|
||||
require_NoError(t, err)
|
||||
defer nodeB.Stop()
|
||||
|
||||
u, err := url.Parse("tcp://localhost:0")
|
||||
require_NoError(t, err)
|
||||
|
||||
l, err := nodeA.Listen(u, "")
|
||||
require_NoError(t, err)
|
||||
|
||||
u, err = url.Parse("tcp://" + l.Addr().String())
|
||||
require_NoError(t, err)
|
||||
|
||||
require_NoError(t, nodeB.AddPeer(u, ""))
|
||||
|
||||
time.Sleep(time.Second)
|
||||
|
||||
peers := nodeB.GetPeers()
|
||||
require_Equal(t, len(peers), 1)
|
||||
require_True(t, !peers[0].Up)
|
||||
require_True(t, peers[0].LastError != nil)
|
||||
}
|
||||
|
||||
func TestAllowedPublicKeysLocal(t *testing.T) {
|
||||
logger := GetLoggerWithPrefix("", false)
|
||||
cfgA, cfgB := config.GenerateConfig(), config.GenerateConfig()
|
||||
require_NoError(t, cfgA.GenerateSelfSignedCertificate())
|
||||
require_NoError(t, cfgB.GenerateSelfSignedCertificate())
|
||||
|
||||
nodeA, err := New(cfgA.Certificate, logger, AllowedPublicKey("abcdef"))
|
||||
require_NoError(t, err)
|
||||
defer nodeA.Stop()
|
||||
|
||||
nodeB, err := New(cfgB.Certificate, logger)
|
||||
require_NoError(t, err)
|
||||
defer nodeB.Stop()
|
||||
|
||||
u, err := url.Parse("tcp://localhost:0")
|
||||
require_NoError(t, err)
|
||||
|
||||
l, err := nodeA.ListenLocal(u, "")
|
||||
require_NoError(t, err)
|
||||
|
||||
u, err = url.Parse("tcp://" + l.Addr().String())
|
||||
require_NoError(t, err)
|
||||
|
||||
require_NoError(t, nodeB.AddPeer(u, ""))
|
||||
|
||||
time.Sleep(time.Second)
|
||||
|
||||
peers := nodeB.GetPeers()
|
||||
require_Equal(t, len(peers), 1)
|
||||
require_True(t, peers[0].Up)
|
||||
require_True(t, peers[0].LastError == nil)
|
||||
}
|
||||
|
@@ -108,7 +108,9 @@ func (l *links) shutdown() {
|
||||
_ = listener.listener.Close()
|
||||
}
|
||||
for _, link := range l._links {
|
||||
_ = link._conn.Close()
|
||||
if link._conn != nil {
|
||||
_ = link._conn.Close()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -124,6 +126,7 @@ const ErrLinkPinnedKeyInvalid = linkError("pinned public key is invalid")
|
||||
const ErrLinkPasswordInvalid = linkError("invalid password supplied")
|
||||
const ErrLinkUnrecognisedSchema = linkError("link schema unknown")
|
||||
const ErrLinkMaxBackoffInvalid = linkError("max backoff duration invalid")
|
||||
const ErrLinkSNINotSupported = linkError("SNI not supported on this link type")
|
||||
|
||||
func (l *links) add(u *url.URL, sintf string, linkType linkType) error {
|
||||
var retErr error
|
||||
@@ -334,7 +337,7 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error {
|
||||
|
||||
// Give the connection to the handler. The handler will block
|
||||
// for the lifetime of the connection.
|
||||
if err = l.handler(linkType, options, lc, resetBackoff); err != nil && err != io.EOF {
|
||||
if err = l.handler(linkType, options, lc, resetBackoff, false); err != nil && err != io.EOF {
|
||||
l.core.log.Debugf("Link %s error: %s\n", info.uri, err)
|
||||
}
|
||||
|
||||
@@ -357,8 +360,9 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error {
|
||||
if backoffNow() {
|
||||
continue
|
||||
}
|
||||
return
|
||||
}
|
||||
// Ephemeral or incoming connections don't reconnect.
|
||||
return
|
||||
}
|
||||
}()
|
||||
})
|
||||
@@ -392,7 +396,7 @@ func (l *links) remove(u *url.URL, sintf string, _ linkType) error {
|
||||
return retErr
|
||||
}
|
||||
|
||||
func (l *links) listen(u *url.URL, sintf string) (*Listener, error) {
|
||||
func (l *links) listen(u *url.URL, sintf string, local bool) (*Listener, error) {
|
||||
ctx, cancel := context.WithCancel(l.core.ctx)
|
||||
var protocol linkProtocol
|
||||
switch strings.ToLower(u.Scheme) {
|
||||
@@ -420,7 +424,10 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) {
|
||||
li := &Listener{
|
||||
listener: listener,
|
||||
ctx: ctx,
|
||||
Cancel: cancel,
|
||||
Cancel: func() {
|
||||
cancel()
|
||||
_ = listener.Close()
|
||||
},
|
||||
}
|
||||
|
||||
var options linkOptions
|
||||
@@ -516,7 +523,7 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) {
|
||||
|
||||
// Give the connection to the handler. The handler will block
|
||||
// for the lifetime of the connection.
|
||||
switch err = l.handler(linkTypeIncoming, options, lc, nil); {
|
||||
switch err = l.handler(linkTypeIncoming, options, lc, nil, local); {
|
||||
case err == nil:
|
||||
case errors.Is(err, io.EOF):
|
||||
case errors.Is(err, net.ErrClosed):
|
||||
@@ -557,7 +564,7 @@ func (l *links) connect(ctx context.Context, u *url.URL, info linkInfo, options
|
||||
return dialer.dial(ctx, u, info, options)
|
||||
}
|
||||
|
||||
func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn, success func()) error {
|
||||
func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn, success func(), local bool) error {
|
||||
meta := version_getBaseMetadata()
|
||||
meta.publicKey = l.core.public
|
||||
meta.priority = options.priority
|
||||
@@ -600,19 +607,21 @@ func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn, s
|
||||
}
|
||||
}
|
||||
// Check if we're authorized to connect to this key / IP
|
||||
var allowed map[[32]byte]struct{}
|
||||
phony.Block(l.core, func() {
|
||||
allowed = l.core.config._allowedPublicKeys
|
||||
})
|
||||
isallowed := len(allowed) == 0
|
||||
for k := range allowed {
|
||||
if bytes.Equal(k[:], meta.publicKey) {
|
||||
isallowed = true
|
||||
break
|
||||
if !local {
|
||||
var allowed map[[32]byte]struct{}
|
||||
phony.Block(l.core, func() {
|
||||
allowed = l.core.config._allowedPublicKeys
|
||||
})
|
||||
isallowed := len(allowed) == 0
|
||||
for k := range allowed {
|
||||
if bytes.Equal(k[:], meta.publicKey) {
|
||||
isallowed = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if linkType == linkTypeIncoming && !isallowed {
|
||||
return fmt.Errorf("node public key %q is not in AllowedPublicKeys", hex.EncodeToString(meta.publicKey))
|
||||
}
|
||||
}
|
||||
if linkType == linkTypeIncoming && !isallowed {
|
||||
return fmt.Errorf("node public key %q is not in AllowedPublicKeys", hex.EncodeToString(meta.publicKey))
|
||||
}
|
||||
|
||||
dir := "outbound"
|
||||
|
@@ -23,6 +23,9 @@ func (l *links) newLinkSOCKS() *linkSOCKS {
|
||||
}
|
||||
|
||||
func (l *linkSOCKS) dial(_ context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
|
||||
if url.Scheme != "sockstls" && options.tlsSNI != "" {
|
||||
return nil, ErrLinkSNINotSupported
|
||||
}
|
||||
var proxyAuth *proxy.Auth
|
||||
if url.User != nil && url.User.Username() != "" {
|
||||
proxyAuth = &proxy.Auth{
|
||||
|
@@ -67,6 +67,9 @@ func (l *linkTCP) dialersFor(url *url.URL, info linkInfo) ([]*tcpDialer, error)
|
||||
}
|
||||
|
||||
func (l *linkTCP) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
|
||||
if options.tlsSNI != "" {
|
||||
return nil, ErrLinkSNINotSupported
|
||||
}
|
||||
dialers, err := l.dialersFor(url, info)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@@ -31,6 +31,9 @@ func (l *links) newLinkUNIX() *linkUNIX {
|
||||
}
|
||||
|
||||
func (l *linkUNIX) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
|
||||
if options.tlsSNI != "" {
|
||||
return nil, ErrLinkSNINotSupported
|
||||
}
|
||||
addr, err := net.ResolveUnixAddr("unix", url.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@@ -8,7 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/Arceliar/phony"
|
||||
"nhooyr.io/websocket"
|
||||
"github.com/coder/websocket"
|
||||
)
|
||||
|
||||
type linkWS struct {
|
||||
@@ -87,6 +87,9 @@ func (l *links) newLinkWS() *linkWS {
|
||||
}
|
||||
|
||||
func (l *linkWS) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
|
||||
if options.tlsSNI != "" {
|
||||
return nil, ErrLinkSNINotSupported
|
||||
}
|
||||
wsconn, _, err := websocket.Dial(ctx, url.String(), &websocket.DialOptions{
|
||||
Subprotocols: []string{"ygg-ws"},
|
||||
})
|
||||
|
@@ -7,7 +7,7 @@ import (
|
||||
"net/url"
|
||||
|
||||
"github.com/Arceliar/phony"
|
||||
"nhooyr.io/websocket"
|
||||
"github.com/coder/websocket"
|
||||
)
|
||||
|
||||
type linkWSS struct {
|
||||
@@ -27,6 +27,9 @@ func (l *links) newLinkWSS() *linkWSS {
|
||||
}
|
||||
|
||||
func (l *linkWSS) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
|
||||
if options.tlsSNI != "" {
|
||||
return nil, ErrLinkSNINotSupported
|
||||
}
|
||||
wsconn, _, err := websocket.Dial(ctx, url.String(), &websocket.DialOptions{
|
||||
Subprotocols: []string{"ygg-ws"},
|
||||
})
|
||||
|
@@ -2,9 +2,7 @@ package ipv6rwc
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -19,15 +17,6 @@ import (
|
||||
|
||||
const keyStoreTimeout = 2 * time.Minute
|
||||
|
||||
/*
|
||||
// Out-of-band packet types
|
||||
const (
|
||||
typeKeyDummy = iota // nolint:deadcode,varcheck
|
||||
typeKeyLookup
|
||||
typeKeyResponse
|
||||
)
|
||||
*/
|
||||
|
||||
type keyArray [ed25519.PublicKeySize]byte
|
||||
|
||||
type keyStore struct {
|
||||
@@ -40,6 +29,7 @@ type keyStore struct {
|
||||
addrBuffer map[address.Address]*buffer
|
||||
subnetToInfo map[address.Subnet]*keyInfo
|
||||
subnetBuffer map[address.Subnet]*buffer
|
||||
tunnelHelper TunnelHelper
|
||||
mtu uint64
|
||||
}
|
||||
|
||||
@@ -59,10 +49,6 @@ func (k *keyStore) init(c *core.Core) {
|
||||
k.core = c
|
||||
k.address = *address.AddrForKey(k.core.PublicKey())
|
||||
k.subnet = *address.SubnetForKey(k.core.PublicKey())
|
||||
/*if err := k.core.SetOutOfBandHandler(k.oobHandler); err != nil {
|
||||
err = fmt.Errorf("tun.core.SetOutOfBandHander: %w", err)
|
||||
panic(err)
|
||||
}*/
|
||||
k.core.SetPathNotify(func(key ed25519.PublicKey) {
|
||||
k.update(key)
|
||||
})
|
||||
@@ -182,49 +168,10 @@ func (k *keyStore) resetTimeout(info *keyInfo) {
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
func (k *keyStore) oobHandler(fromKey, toKey ed25519.PublicKey, data []byte) { // nolint:unused
|
||||
if len(data) != 1+ed25519.SignatureSize {
|
||||
return
|
||||
}
|
||||
sig := data[1:]
|
||||
switch data[0] {
|
||||
case typeKeyLookup:
|
||||
snet := *address.SubnetForKey(toKey)
|
||||
if snet == k.subnet && ed25519.Verify(fromKey, toKey[:], sig) {
|
||||
// This is looking for at least our subnet (possibly our address)
|
||||
// Send a response
|
||||
k.sendKeyResponse(fromKey)
|
||||
}
|
||||
case typeKeyResponse:
|
||||
// TODO keep a list of something to match against...
|
||||
// Ignore the response if it doesn't match anything of interest...
|
||||
if ed25519.Verify(fromKey, toKey[:], sig) {
|
||||
k.update(fromKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
func (k *keyStore) sendKeyLookup(partial ed25519.PublicKey) {
|
||||
/*
|
||||
sig := ed25519.Sign(k.core.PrivateKey(), partial[:])
|
||||
bs := append([]byte{typeKeyLookup}, sig...)
|
||||
//_ = k.core.SendOutOfBand(partial, bs)
|
||||
_ = bs
|
||||
*/
|
||||
k.core.SendLookup(partial)
|
||||
}
|
||||
|
||||
/*
|
||||
func (k *keyStore) sendKeyResponse(dest ed25519.PublicKey) { // nolint:unused
|
||||
sig := ed25519.Sign(k.core.PrivateKey(), dest[:])
|
||||
bs := append([]byte{typeKeyResponse}, sig...)
|
||||
//_ = k.core.SendOutOfBand(dest, bs)
|
||||
_ = bs
|
||||
}
|
||||
*/
|
||||
|
||||
func (k *keyStore) readPC(p []byte) (int, error) {
|
||||
buf := make([]byte, k.core.MTU(), 65535)
|
||||
for {
|
||||
@@ -240,16 +187,22 @@ func (k *keyStore) readPC(p []byte) (int, error) {
|
||||
if len(bs) == 0 {
|
||||
continue
|
||||
}
|
||||
if bs[0]&0xf0 != 0x60 {
|
||||
continue // not IPv6
|
||||
}
|
||||
if len(bs) < 40 {
|
||||
ip4 := bs[0]&0xf0 == 0x40
|
||||
ip6 := bs[0]&0xf0 == 0x60
|
||||
switch {
|
||||
case !ip4 && !ip6:
|
||||
continue
|
||||
case ip6 && len(bs) < 40:
|
||||
continue
|
||||
case ip4 && len(bs) < 20:
|
||||
continue
|
||||
}
|
||||
k.mutex.Lock()
|
||||
mtu := int(k.mtu)
|
||||
th := k.tunnelHelper
|
||||
k.mutex.Unlock()
|
||||
if len(bs) > mtu {
|
||||
switch {
|
||||
case ip6 && len(bs) > mtu:
|
||||
// Using bs would make it leak off the stack, so copy to buf
|
||||
buf := make([]byte, 512)
|
||||
cn := copy(buf, bs)
|
||||
@@ -261,51 +214,87 @@ func (k *keyStore) readPC(p []byte) (int, error) {
|
||||
_, _ = k.writePC(packet)
|
||||
}
|
||||
continue
|
||||
case len(bs) > mtu:
|
||||
continue
|
||||
}
|
||||
var srcAddr, dstAddr address.Address
|
||||
var srcSubnet, dstSubnet address.Subnet
|
||||
copy(srcAddr[:], bs[8:])
|
||||
copy(dstAddr[:], bs[24:])
|
||||
copy(srcSubnet[:], bs[8:])
|
||||
copy(dstSubnet[:], bs[24:])
|
||||
if dstAddr != k.address && dstSubnet != k.subnet {
|
||||
continue // bad local address/subnet
|
||||
var addrlen int
|
||||
switch {
|
||||
case ip4:
|
||||
copy(srcAddr[:], bs[12:16])
|
||||
addrlen = 4
|
||||
case ip6:
|
||||
copy(srcAddr[:], bs[8:])
|
||||
copy(srcSubnet[:], bs[8:])
|
||||
copy(dstAddr[:], bs[24:])
|
||||
copy(dstSubnet[:], bs[24:])
|
||||
addrlen = 16
|
||||
}
|
||||
info := k.update(ed25519.PublicKey(from.(iwt.Addr)))
|
||||
if srcAddr != info.address && srcSubnet != info.subnet {
|
||||
continue // bad remote address/subnet
|
||||
srcKey := ed25519.PublicKey(from.(iwt.Addr))
|
||||
info := k.update(srcKey)
|
||||
switch {
|
||||
case ip6 && (srcAddr == info.address || srcSubnet == info.subnet):
|
||||
return copy(p, bs), nil
|
||||
case ip4, ip6:
|
||||
if th == nil {
|
||||
continue
|
||||
}
|
||||
addr, ok := netip.AddrFromSlice(srcAddr[:addrlen])
|
||||
if !ok || !th.InboundAllowed(addr, srcKey) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
n = copy(p, bs)
|
||||
return n, nil
|
||||
return copy(p, bs), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (k *keyStore) writePC(bs []byte) (int, error) {
|
||||
if bs[0]&0xf0 != 0x60 {
|
||||
return 0, errors.New("not an IPv6 packet") // not IPv6
|
||||
if len(bs) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
if len(bs) < 40 {
|
||||
strErr := fmt.Sprint("undersized IPv6 packet, length: ", len(bs))
|
||||
return 0, errors.New(strErr)
|
||||
ip4 := bs[0]&0xf0 == 0x40
|
||||
ip6 := bs[0]&0xf0 == 0x60
|
||||
switch {
|
||||
case !ip4 && !ip6:
|
||||
return len(bs), nil
|
||||
case ip6 && len(bs) < 40:
|
||||
return len(bs), nil
|
||||
case ip4 && len(bs) < 20:
|
||||
return len(bs), nil
|
||||
}
|
||||
var srcAddr, dstAddr address.Address
|
||||
var srcSubnet, dstSubnet address.Subnet
|
||||
copy(srcAddr[:], bs[8:])
|
||||
copy(dstAddr[:], bs[24:])
|
||||
copy(srcSubnet[:], bs[8:])
|
||||
copy(dstSubnet[:], bs[24:])
|
||||
if srcAddr != k.address && srcSubnet != k.subnet {
|
||||
// This happens all the time due to link-local traffic
|
||||
// Don't send back an error, just drop it
|
||||
strErr := fmt.Sprint("incorrect source address: ", net.IP(srcAddr[:]).String())
|
||||
return 0, errors.New(strErr)
|
||||
var dstAddr address.Address
|
||||
var dstSubnet address.Subnet
|
||||
var addrlen int
|
||||
switch {
|
||||
case ip4:
|
||||
copy(dstAddr[:], bs[16:20])
|
||||
addrlen = 4
|
||||
case ip6:
|
||||
copy(dstAddr[:], bs[24:40])
|
||||
copy(dstSubnet[:], bs[24:40])
|
||||
addrlen = 16
|
||||
}
|
||||
if dstAddr.IsValid() {
|
||||
switch {
|
||||
case dstAddr.IsValid():
|
||||
k.sendToAddress(dstAddr, bs)
|
||||
} else if dstSubnet.IsValid() {
|
||||
case dstSubnet.IsValid():
|
||||
k.sendToSubnet(dstSubnet, bs)
|
||||
} else {
|
||||
return 0, errors.New("invalid destination address")
|
||||
default:
|
||||
k.mutex.Lock()
|
||||
th := k.tunnelHelper
|
||||
k.mutex.Unlock()
|
||||
if th == nil {
|
||||
return len(bs), nil
|
||||
}
|
||||
addr, ok := netip.AddrFromSlice(dstAddr[:addrlen])
|
||||
if !ok {
|
||||
return len(bs), nil
|
||||
}
|
||||
if key := th.OutboundAllowed(addr); key != nil && len(key) == ed25519.PublicKeySize {
|
||||
return k.core.WriteTo(bs, iwt.Addr(key))
|
||||
}
|
||||
return len(bs), nil
|
||||
}
|
||||
return len(bs), nil
|
||||
}
|
||||
@@ -366,3 +355,14 @@ func (rwc *ReadWriteCloser) Close() error {
|
||||
rwc.core.Stop()
|
||||
return err
|
||||
}
|
||||
|
||||
func (rwc *ReadWriteCloser) SetTunnelHelper(h TunnelHelper) {
|
||||
rwc.mutex.Lock()
|
||||
defer rwc.mutex.Unlock()
|
||||
rwc.tunnelHelper = h
|
||||
}
|
||||
|
||||
type TunnelHelper interface {
|
||||
InboundAllowed(srcip netip.Addr, src ed25519.PublicKey) bool
|
||||
OutboundAllowed(dstip netip.Addr) ed25519.PublicKey
|
||||
}
|
||||
|
@@ -14,6 +14,7 @@ import (
|
||||
|
||||
"github.com/Arceliar/phony"
|
||||
"github.com/gologme/log"
|
||||
"github.com/wlynxg/anet"
|
||||
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/core"
|
||||
"golang.org/x/crypto/blake2b"
|
||||
@@ -148,7 +149,8 @@ func (m *Multicast) _stop() error {
|
||||
func (m *Multicast) _updateInterfaces() {
|
||||
interfaces := m._getAllowedInterfaces()
|
||||
for name, info := range interfaces {
|
||||
addrs, err := info.iface.Addrs()
|
||||
// 'anet' package is used here to avoid https://github.com/golang/go/issues/40569
|
||||
addrs, err := anet.InterfaceAddrsByInterface(&info.iface)
|
||||
if err != nil {
|
||||
m.log.Warnf("Failed up get addresses for interface %s: %s", name, err)
|
||||
delete(interfaces, name)
|
||||
@@ -156,6 +158,7 @@ func (m *Multicast) _updateInterfaces() {
|
||||
}
|
||||
info.addrs = addrs
|
||||
interfaces[name] = info
|
||||
m.log.Debugf("Discovered addresses for interface %s: %s", name, addrs)
|
||||
}
|
||||
m._interfaces = interfaces
|
||||
}
|
||||
@@ -174,10 +177,11 @@ func (m *Multicast) Interfaces() map[string]net.Interface {
|
||||
func (m *Multicast) _getAllowedInterfaces() map[string]*interfaceInfo {
|
||||
interfaces := make(map[string]*interfaceInfo)
|
||||
// Ask the system for network interfaces
|
||||
allifaces, err := net.Interfaces()
|
||||
// 'anet' package is used here to avoid https://github.com/golang/go/issues/40569
|
||||
allifaces, err := anet.Interfaces()
|
||||
if err != nil {
|
||||
// Don't panic, since this may be from e.g. too many open files (from too much connection spam)
|
||||
// TODO? log something
|
||||
m.log.Debugf("Failed to get interfaces: %s", err)
|
||||
return nil
|
||||
}
|
||||
// Work out which interfaces to announce on
|
||||
@@ -186,6 +190,8 @@ func (m *Multicast) _getAllowedInterfaces() map[string]*interfaceInfo {
|
||||
switch {
|
||||
case iface.Flags&net.FlagUp == 0:
|
||||
continue // Ignore interfaces that are down
|
||||
case iface.Flags&net.FlagRunning == 0:
|
||||
continue // Ignore interfaces that are not running
|
||||
case iface.Flags&net.FlagMulticast == 0:
|
||||
continue // Ignore non-multicast interfaces
|
||||
case iface.Flags&net.FlagPointToPoint != 0:
|
||||
@@ -321,7 +327,7 @@ func (m *Multicast) _announce() {
|
||||
Host: net.JoinHostPort(addrIP.String(), fmt.Sprintf("%d", info.port)),
|
||||
RawQuery: v.Encode(),
|
||||
}
|
||||
if li, err := m.core.Listen(u, iface.Name); err == nil {
|
||||
if li, err := m.core.ListenLocal(u, iface.Name); err == nil {
|
||||
m.log.Debugln("Started multicasting on", iface.Name)
|
||||
// Store the listener so that we can stop it later if needed
|
||||
linfo = &listenerInfo{listener: li, time: time.Now(), port: info.port}
|
||||
|
@@ -1,5 +1,11 @@
|
||||
package tun
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
wgtun "golang.zx2c4.com/wireguard/tun"
|
||||
)
|
||||
|
||||
const TUN_OFFSET_BYTES = 80 // sizeof(virtio_net_hdr)
|
||||
|
||||
func (tun *TunAdapter) read() {
|
||||
@@ -12,6 +18,10 @@ func (tun *TunAdapter) read() {
|
||||
for {
|
||||
n, err := tun.iface.Read(bufs, sizes, TUN_OFFSET_BYTES)
|
||||
if err != nil {
|
||||
if errors.Is(err, wgtun.ErrTooManySegments) {
|
||||
tun.log.Debugln("TUN segments dropped: %v", err)
|
||||
continue
|
||||
}
|
||||
tun.log.Errorln("Error reading TUN:", err)
|
||||
return
|
||||
}
|
||||
|
@@ -125,6 +125,7 @@ func (tun *TunAdapter) _start() error {
|
||||
if tun.config.name == "none" || tun.config.name == "dummy" {
|
||||
tun.log.Debugln("Not starting TUN as ifname is none or dummy")
|
||||
tun.isEnabled = false
|
||||
go tun.queue()
|
||||
go tun.write()
|
||||
return nil
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
//go:build openbsd || freebsd
|
||||
// +build openbsd freebsd
|
||||
//go:build freebsd
|
||||
// +build freebsd
|
||||
|
||||
package tun
|
||||
|
||||
@@ -54,11 +54,6 @@ struct in6_ifreq {
|
||||
290 };
|
||||
*/
|
||||
|
||||
type in6_ifreq_mtu struct {
|
||||
ifr_name [syscall.IFNAMSIZ]byte
|
||||
ifru_mtu int
|
||||
}
|
||||
|
||||
type in6_ifreq_addr struct {
|
||||
ifr_name [syscall.IFNAMSIZ]byte
|
||||
ifru_addr sockaddr_in6
|
||||
@@ -112,26 +107,6 @@ func (tun *TunAdapter) setupAddress(addr string) error {
|
||||
tun.log.Infof("Interface IPv6: %s", addr)
|
||||
tun.log.Infof("Interface MTU: %d", tun.mtu)
|
||||
|
||||
// Create the MTU request
|
||||
var ir in6_ifreq_mtu
|
||||
copy(ir.ifr_name[:], tun.Name())
|
||||
ir.ifru_mtu = int(tun.mtu)
|
||||
|
||||
// Set the MTU
|
||||
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(sfd), uintptr(syscall.SIOCSIFMTU), uintptr(unsafe.Pointer(&ir))); errno != 0 {
|
||||
err = errno
|
||||
tun.log.Errorf("Error in SIOCSIFMTU: %v", errno)
|
||||
|
||||
// Fall back to ifconfig to set the MTU
|
||||
cmd := exec.Command("ifconfig", tun.Name(), "mtu", string(tun.mtu))
|
||||
tun.log.Warnf("Using ifconfig as fallback: %v", strings.Join(cmd.Args, " "))
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
tun.log.Errorf("SIOCSIFMTU fallback failed: %v.", err)
|
||||
tun.log.Traceln(string(output))
|
||||
}
|
||||
}
|
||||
|
||||
// Create the address request
|
||||
// FIXME: I don't work!
|
||||
var ar in6_ifreq_addr
|
122
src/tun/tun_openbsd.go
Normal file
122
src/tun/tun_openbsd.go
Normal file
@@ -0,0 +1,122 @@
|
||||
//go:build openbsd
|
||||
// +build openbsd
|
||||
|
||||
package tun
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
wgtun "golang.zx2c4.com/wireguard/tun"
|
||||
)
|
||||
|
||||
const (
|
||||
SIOCAIFADDR_IN6 = 0x8080691a
|
||||
ND6_INFINITE_LIFETIME = 0xffffffff
|
||||
)
|
||||
|
||||
type in6_addrlifetime struct {
|
||||
ia6t_expire int64
|
||||
ia6t_preferred int64
|
||||
ia6t_vltime uint32
|
||||
ia6t_pltime uint32
|
||||
}
|
||||
|
||||
// Match types from the net package, effectively being [16]byte for IPv6 addresses.
|
||||
type in6_addr [16]uint8
|
||||
|
||||
type sockaddr_in6 struct {
|
||||
sin6_len uint8
|
||||
sin6_family uint8
|
||||
sin6_port uint16
|
||||
sin6_flowinfo uint32
|
||||
sin6_addr in6_addr
|
||||
sin6_scope_id uint32
|
||||
}
|
||||
|
||||
func (sa6 *sockaddr_in6) setSockaddr(addr [/*16*/]byte /* net.IP or net.IPMask */) {
|
||||
sa6.sin6_len = uint8(unsafe.Sizeof(*sa6))
|
||||
sa6.sin6_family = unix.AF_INET6
|
||||
|
||||
for i := range sa6.sin6_addr {
|
||||
sa6.sin6_addr[i] = addr[i]
|
||||
}
|
||||
}
|
||||
|
||||
type in6_aliasreq struct {
|
||||
ifra_name [syscall.IFNAMSIZ]byte
|
||||
ifra_addr sockaddr_in6
|
||||
ifra_dstaddr sockaddr_in6
|
||||
ifra_prefixmask sockaddr_in6
|
||||
ifra_flags int32
|
||||
ifra_lifetime in6_addrlifetime
|
||||
}
|
||||
|
||||
// Configures the TUN adapter with the correct IPv6 address and MTU.
|
||||
func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error {
|
||||
iface, err := wgtun.CreateTUN(ifname, int(mtu))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create TUN: %w", err)
|
||||
}
|
||||
tun.iface = iface
|
||||
if mtu, err := iface.MTU(); err == nil {
|
||||
tun.mtu = getSupportedMTU(uint64(mtu))
|
||||
} else {
|
||||
tun.mtu = 0
|
||||
}
|
||||
if addr != "" {
|
||||
return tun.setupAddress(addr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Configures the "utun" adapter from an existing file descriptor.
|
||||
func (tun *TunAdapter) setupFD(fd int32, addr string, mtu uint64) error {
|
||||
return fmt.Errorf("setup via FD not supported on this platform")
|
||||
}
|
||||
|
||||
func (tun *TunAdapter) setupAddress(addr string) error {
|
||||
var sfd int
|
||||
var err error
|
||||
|
||||
ip, prefix, err := net.ParseCIDR(addr)
|
||||
if err != nil {
|
||||
tun.log.Errorf("Error in ParseCIDR: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Create system socket
|
||||
if sfd, err = unix.Socket(unix.AF_INET6, unix.SOCK_DGRAM, 0); err != nil {
|
||||
tun.log.Printf("Create AF_INET6 socket failed: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Friendly output
|
||||
tun.log.Infof("Interface name: %s", tun.Name())
|
||||
tun.log.Infof("Interface IPv6: %s", addr)
|
||||
tun.log.Infof("Interface MTU: %d", tun.mtu)
|
||||
|
||||
// Create the address request
|
||||
var ar in6_aliasreq
|
||||
copy(ar.ifra_name[:], tun.Name())
|
||||
|
||||
ar.ifra_addr.setSockaddr(ip)
|
||||
|
||||
prefixmask := net.CIDRMask(prefix.Mask.Size())
|
||||
ar.ifra_prefixmask.setSockaddr(prefixmask)
|
||||
|
||||
ar.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME
|
||||
ar.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME
|
||||
|
||||
// Set the interface address
|
||||
if err = unix.IoctlSetInt(sfd, SIOCAIFADDR_IN6, int(uintptr(unsafe.Pointer(&ar)))); err != nil {
|
||||
tun.log.Errorf("Error in SIOCAIFADDR_IN6: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
Reference in New Issue
Block a user