Compare commits

..

1 Commits

Author SHA1 Message Date
Neil Alexander
ed8991571a Add support for Multipath TCP 2024-05-31 23:22:34 +01:00
44 changed files with 400 additions and 944 deletions

View File

@@ -17,10 +17,10 @@ jobs:
steps:
- uses: actions/setup-go@v5
with:
go-version: stable
go-version: 1.21
- uses: actions/checkout@v4
- name: golangci-lint
uses: golangci/golangci-lint-action@v6
uses: golangci/golangci-lint-action@v3
with:
args: --issues-exit-code=1
@@ -51,7 +51,7 @@ jobs:
strategy:
fail-fast: false
matrix:
goversion: ["1.21", "1.22", "1.23"]
goversion: ["1.21", "1.22"]
name: Build & Test (Linux, Go ${{ matrix.goversion }})
needs: [lint]
@@ -75,7 +75,7 @@ jobs:
strategy:
fail-fast: false
matrix:
goversion: ["1.21", "1.22", "1.23"]
goversion: ["1.21", "1.22"]
name: Build & Test (Windows, Go ${{ matrix.goversion }})
needs: [lint]
@@ -99,7 +99,7 @@ jobs:
strategy:
fail-fast: false
matrix:
goversion: ["1.21", "1.22", "1.23"]
goversion: ["1.21", "1.22"]
name: Build & Test (macOS, Go ${{ matrix.goversion }})
needs: [lint]
@@ -123,7 +123,7 @@ jobs:
strategy:
fail-fast: false
matrix:
goversion: ["1.21", "1.22", "1.23"]
goversion: ["1.21", "1.22"]
goos:
- freebsd
- openbsd

View File

@@ -2,10 +2,9 @@ run:
build-tags:
- lint
issues-exit-code: 0 # TODO: change this to 1 when we want it to fail builds
issues:
exclude-dirs:
skip-dirs:
- contrib/
- misc/
linters:
disable:
- gocyclo
- gocyclo

View File

@@ -26,57 +26,6 @@ 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
* A bug which caused startup problems on Windows and FreeBSD should be fixed
* Resolved some minor link state and listener management bugs during shutdown
## [0.5.7] - 2024-08-05
### Added
* WebSocket support for peerings, by using the new `ws://` scheme in `Listen` and `Peers`
* Additionally, the `wss://` scheme can be used to connect to a WebSocket peer behind a HTTPS reverse proxy
### Changed
* On Linux, the TUN adapter now uses vectorised reads/writes where possible, which should reduce the amount of CPU time spent on syscalls and potentially improve throughput
* Link error handling has been improved and various link error messages have been rewritten to be clearer
* Upgrade dependencies
### Fixed
* Multiple multicast connections to the same remote machine should now work correctly
* You may get two connections in some cases, one inbound and one outbound, this is known and will not cause problems
* Running as a Windows service should be more reliable with service startup and shutdown bugs fixed
## [0.5.6] - 2024-05-30
* Go 1.21 is now required to build Yggdrasil

View File

@@ -1,10 +0,0 @@
//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")
}

View File

@@ -1,87 +0,0 @@
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
package main
import (
"errors"
"fmt"
"math"
osuser "os/user"
"strconv"
"strings"
"syscall"
)
func chuser(user string) error {
group := ""
if i := strings.IndexByte(user, ':'); i >= 0 {
user, group = user[:i], user[i+1:]
}
u := (*osuser.User)(nil)
g := (*osuser.Group)(nil)
if user != "" {
if _, err := strconv.ParseUint(user, 10, 32); err == nil {
u, err = osuser.LookupId(user)
if err != nil {
return fmt.Errorf("failed to lookup user by id %q: %v", user, err)
}
} else {
u, err = osuser.Lookup(user)
if err != nil {
return fmt.Errorf("failed to lookup user by name %q: %v", user, err)
}
}
}
if group != "" {
if _, err := strconv.ParseUint(group, 10, 32); err == nil {
g, err = osuser.LookupGroupId(group)
if err != nil {
return fmt.Errorf("failed to lookup group by id %q: %v", user, err)
}
} else {
g, err = osuser.LookupGroup(group)
if err != nil {
return fmt.Errorf("failed to lookup group by name %q: %v", user, err)
}
}
}
if g != nil {
gid, _ := strconv.ParseUint(g.Gid, 10, 32)
var err error
if gid < math.MaxInt {
err = syscall.Setgid(int(gid))
} else {
err = errors.New("gid too big")
}
if err != nil {
return fmt.Errorf("failed to setgid %d: %v", gid, err)
}
} else if u != nil {
gid, _ := strconv.ParseUint(u.Gid, 10, 32)
err := syscall.Setgid(int(uint32(gid)))
if err != nil {
return fmt.Errorf("failed to setgid %d: %v", gid, err)
}
}
if u != nil {
uid, _ := strconv.ParseUint(u.Uid, 10, 32)
var err error
if uid < math.MaxInt {
err = syscall.Setuid(int(uid))
} else {
err = errors.New("uid too big")
}
if err != nil {
return fmt.Errorf("failed to setuid %d: %v", uid, err)
}
}
return nil
}

View File

@@ -52,15 +52,14 @@ 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)
// Capture the service being stopped on Windows.
minwinsvc.SetOnExit(cancel)
// Create a new logger that logs output to stdout.
var logger *log.Logger
switch *logto {
@@ -273,22 +272,6 @@ func main() {
}
}
//Windows service shutdown
minwinsvc.SetOnExit(func() {
logger.Infof("Shutting down service ...")
cancel()
// Wait for all parts to shutdown properly
<-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()

View File

@@ -38,6 +38,7 @@ 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")
}

View File

@@ -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", "Cost", "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, rtt := "Up", "-", "Out", "-"
if !peer.Up {
@@ -200,7 +200,6 @@ func run() int {
peer.RXBytes.String(),
peer.TXBytes.String(),
fmt.Sprintf("%d", peer.Priority),
fmt.Sprintf("%d", peer.Cost),
lasterr,
})
}

View File

@@ -7,7 +7,6 @@ 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"
@@ -34,15 +33,6 @@ 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

View File

@@ -15,6 +15,8 @@ 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

View File

@@ -1,5 +1,5 @@
//go:build ios || darwin
// +build ios darwin
//go:build ios
// +build ios
package mobile

View File

@@ -1,5 +1,5 @@
//go:build !android && !ios && !darwin
// +build !android,!ios,!darwin
//go:build !android && !ios
// +build !android,!ios
package mobile

35
go.mod
View File

@@ -3,23 +3,21 @@ module github.com/yggdrasil-network/yggdrasil-go
go 1.21
require (
github.com/Arceliar/ironwood v0.0.0-20241016082300-f6fb9da97a17
github.com/Arceliar/ironwood v0.0.0-20240529054413-b8e59574e2b2
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/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.4.0
github.com/kardianos/minwinsvc v1.0.2
github.com/quic-go/quic-go v0.46.0
github.com/vishvananda/netlink v1.3.0
github.com/wlynxg/anet v0.0.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
github.com/quic-go/quic-go v0.44.0
github.com/vishvananda/netlink v1.1.0
golang.org/x/crypto v0.23.0
golang.org/x/mobile v0.0.0-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
)
@@ -32,17 +30,18 @@ require (
github.com/onsi/ginkgo/v2 v2.9.5 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
go.uber.org/mock v0.4.0 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
golang.org/x/mod v0.19.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/tools v0.23.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
)
require (
github.com/VividCortex/ewma v1.2.0 // indirect
github.com/fatih/color v1.15.0 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/olekukonko/tablewriter v0.0.5
github.com/vishvananda/netns v0.0.4 // indirect
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f // indirect
)

76
go.sum
View File

@@ -1,5 +1,5 @@
github.com/Arceliar/ironwood v0.0.0-20241016082300-f6fb9da97a17 h1:uOvHqPwu09ndYZQDUL6QvyDcz0M9kwooKYa/PEfLwIU=
github.com/Arceliar/ironwood v0.0.0-20241016082300-f6fb9da97a17/go.mod h1:6WP4799FX0OuWdENGQAh+0RXp9FLh0y7NZ7tM9cJyXk=
github.com/Arceliar/ironwood v0.0.0-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=
@@ -9,13 +9,11 @@ github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJR
github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/bits-and-blooms/bloom/v3 v3.7.0 h1:VfknkqV4xI+PsaDIsoHueyxVDZrfvMn56jeWUzvzdls=
github.com/bits-and-blooms/bloom/v3 v3.7.0/go.mod h1:VKlUSvp0lFIYqxJjzdnSsZEw4iHb1kOL2tfHTgyJBHg=
github.com/cheggaaa/pb/v3 v3.1.5 h1:QuuUzeM2WsAqG2gMqtzaWithDJv0i+i6UlnwSCI4QLk=
github.com/cheggaaa/pb/v3 v3.1.5/go.mod h1:CrxkeghYTXi1lQBEI7jSn+3svI3cuc19haAj6jM60XI=
github.com/cheggaaa/pb/v3 v3.1.4 h1:DN8j4TVVdKu3WxVwcRKu0sG00IIU6FewoABZzXbRQeo=
github.com/cheggaaa/pb/v3 v3.1.4/go.mod h1:6wVjILNBaXMs8c21qRiaUM8BR82erfgau1DQ4iUXmSA=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/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=
@@ -29,8 +27,6 @@ github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/gologme/log v1.3.0 h1:l781G4dE+pbigClDSDzSaaYKtiueHCILUa/qSDsmHAo=
github.com/gologme/log v1.3.0/go.mod h1:yKT+DvIPdDdDoPtqFrFxheooyVmoqi0BAsw+erN3wA4=
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
@@ -48,8 +44,8 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
@@ -58,8 +54,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.46.0 h1:uuwLClEEyk1DNvchH8uCByQVjo3yKL9opKulExNDs7Y=
github.com/quic-go/quic-go v0.46.0/go.mod h1:1dLehS7TIR64+vxGR70GDcatWTOtMX2PUtnKsjbTurI=
github.com/quic-go/quic-go v0.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,56 +63,55 @@ 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.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/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f h1:p4VB7kIXpOQvVn1ZaTIVp+3vuYAXFe3OJEvjbUYJLaA=
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/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.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/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.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
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=
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 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@@ -129,29 +124,26 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg=
golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI=
golang.org/x/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=
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4=
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
golang.zx2c4.com/wireguard v0.0.0-20230223181233-21636207a675 h1:/J/RVnr7ng4fWPRH3xa4WtBJ1Jp+Auu4YNLmGiPv5QU=
golang.zx2c4.com/wireguard v0.0.0-20230223181233-21636207a675/go.mod h1:whfbyDBt09xhCYQWtO2+3UVjlaq6/9hDZrjg2ZE6SyA=
golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE=
golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
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=
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 h1:TbRPT0HtzFP3Cno1zZo7yPzEEnfu8EjLfl6IU9VfqkQ=
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259/go.mod h1:AVgIgHMwK63XvmAzWG9vLQ41YnVHN0du0tEC46fI7yY=

View File

@@ -12,7 +12,7 @@ type AddPeerRequest struct {
type AddPeerResponse struct{}
func (a *AdminSocket) addPeerHandler(req *AddPeerRequest, _ *AddPeerResponse) error {
func (a *AdminSocket) addPeerHandler(req *AddPeerRequest, res *AddPeerResponse) error {
u, err := url.Parse(req.Uri)
if err != nil {
return fmt.Errorf("unable to parse peering URI: %w", err)

View File

@@ -201,6 +201,10 @@ func (a *AdminSocket) SetupAdminHandlers() {
return res, nil
},
)
//_ = a.AddHandler("getNodeInfo", []string{"key"}, t.proto.nodeinfo.nodeInfoAdminHandler)
//_ = a.AddHandler("debug_remoteGetSelf", []string{"key"}, t.proto.getSelfHandler)
//_ = a.AddHandler("debug_remoteGetPeers", []string{"key"}, t.proto.getPeersHandler)
//_ = a.AddHandler("debug_remoteGetDHT", []string{"key"}, t.proto.getDHTHandler)
}
// IsStarted returns true if the module has been started.
@@ -238,33 +242,34 @@ func (a *AdminSocket) listen() {
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")
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(u.Path); err == nil {
a.log.Debugln(u.Path, "was cleaned up")
if err := os.Remove(listenaddr[7:]); err == nil {
a.log.Debugln(listenaddr[7:], "was cleaned up")
} else {
a.log.Errorln(u.Path, "already exists and was not cleaned up:", err)
a.log.Errorln(listenaddr[7:], "already exists and was not cleaned up:", err)
os.Exit(1)
}
}
}
a.listener, err = net.Listen("unix", u.Path)
a.listener, err = net.Listen("unix", listenaddr[7:])
if err == nil {
switch u.Path[:1] {
switch listenaddr[7:8] {
case "@": // maybe abstract namespace
default:
if err := os.Chmod(u.Path, 0660); err != nil {
a.log.Warnln("WARNING:", u.Path, "may have unsafe permissions!")
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:
// err = errors.New(fmt.Sprint("protocol not supported: ", u.Scheme))
a.listener, err = net.Listen("tcp", listenaddr)
}
} else {
@@ -304,6 +309,23 @@ func (a *AdminSocket) handleRequest(conn net.Conn) {
defer conn.Close()
/*
defer func() {
r := recover()
if r != nil {
fmt.Println("ERROR:", r)
a.log.Debugln("Admin socket error:", r)
if err := encoder.Encode(&ErrorResponse{
Error: "Check your syntax and input types",
}); err != nil {
fmt.Println("ERROR 2:", err)
a.log.Debugln("Admin socket JSON encode error:", err)
}
conn.Close()
}
}()
*/
for {
var err error
var buf json.RawMessage

View File

@@ -3,7 +3,7 @@ package admin
import (
"encoding/hex"
"net"
"slices"
"sort"
"strings"
"github.com/yggdrasil-network/yggdrasil-go/src/address"
@@ -23,7 +23,7 @@ type PathEntry struct {
Sequence uint64 `json:"sequence"`
}
func (a *AdminSocket) getPathsHandler(_ *GetPathsRequest, res *GetPathsResponse) error {
func (a *AdminSocket) getPathsHandler(req *GetPathsRequest, res *GetPathsResponse) error {
paths := a.core.GetPaths()
res.Paths = make([]PathEntry, 0, len(paths))
for _, p := range paths {
@@ -35,8 +35,8 @@ func (a *AdminSocket) getPathsHandler(_ *GetPathsRequest, res *GetPathsResponse)
Sequence: p.Sequence,
})
}
slices.SortStableFunc(res.Paths, func(a, b PathEntry) int {
return strings.Compare(a.PublicKey, b.PublicKey)
sort.SliceStable(res.Paths, func(i, j int) bool {
return strings.Compare(res.Paths[i].PublicKey, res.Paths[j].PublicKey) < 0
})
return nil
}

View File

@@ -3,8 +3,7 @@ package admin
import (
"encoding/hex"
"net"
"slices"
"strings"
"sort"
"time"
"github.com/yggdrasil-network/yggdrasil-go/src/address"
@@ -25,7 +24,7 @@ type PeerEntry struct {
PublicKey string `json:"key"`
Port uint64 `json:"port"`
Priority uint64 `json:"priority"`
Cost uint64 `json:"cost"`
Multipath bool `json:"multipath,omitempty"`
RXBytes DataUnit `json:"bytes_recvd,omitempty"`
TXBytes DataUnit `json:"bytes_sent,omitempty"`
Uptime float64 `json:"uptime,omitempty"`
@@ -34,20 +33,20 @@ type PeerEntry struct {
LastError string `json:"last_error,omitempty"`
}
func (a *AdminSocket) getPeersHandler(_ *GetPeersRequest, res *GetPeersResponse) error {
func (a *AdminSocket) getPeersHandler(req *GetPeersRequest, res *GetPeersResponse) error {
peers := a.core.GetPeers()
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
Cost: p.Cost,
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
@@ -62,26 +61,17 @@ func (a *AdminSocket) getPeersHandler(_ *GetPeersRequest, res *GetPeersResponse)
}
res.Peers = append(res.Peers, peer)
}
slices.SortStableFunc(res.Peers, func(a, b PeerEntry) int {
if !a.Inbound && b.Inbound {
return -1
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
}
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 !res.Peers[i].Inbound && res.Peers[j].Inbound
})
return nil
}

View File

@@ -17,7 +17,7 @@ type GetSelfResponse struct {
Subnet string `json:"subnet"`
}
func (a *AdminSocket) getSelfHandler(_ *GetSelfRequest, res *GetSelfResponse) error {
func (a *AdminSocket) getSelfHandler(req *GetSelfRequest, res *GetSelfResponse) error {
self := a.core.GetSelf()
snet := a.core.Subnet()
res.BuildName = version.BuildName()

View File

@@ -3,7 +3,7 @@ package admin
import (
"encoding/hex"
"net"
"slices"
"sort"
"strings"
"github.com/yggdrasil-network/yggdrasil-go/src/address"
@@ -23,7 +23,7 @@ type SessionEntry struct {
Uptime float64 `json:"uptime"`
}
func (a *AdminSocket) getSessionsHandler(_ *GetSessionsRequest, res *GetSessionsResponse) error {
func (a *AdminSocket) getSessionsHandler(req *GetSessionsRequest, res *GetSessionsResponse) error {
sessions := a.core.GetSessions()
res.Sessions = make([]SessionEntry, 0, len(sessions))
for _, s := range sessions {
@@ -36,8 +36,8 @@ func (a *AdminSocket) getSessionsHandler(_ *GetSessionsRequest, res *GetSessions
Uptime: s.Uptime.Seconds(),
})
}
slices.SortStableFunc(res.Sessions, func(a, b SessionEntry) int {
return strings.Compare(a.PublicKey, b.PublicKey)
sort.SliceStable(res.Sessions, func(i, j int) bool {
return strings.Compare(res.Sessions[i].PublicKey, res.Sessions[j].PublicKey) < 0
})
return nil
}

View File

@@ -3,7 +3,7 @@ package admin
import (
"encoding/hex"
"net"
"slices"
"sort"
"strings"
"github.com/yggdrasil-network/yggdrasil-go/src/address"
@@ -20,9 +20,11 @@ type TreeEntry struct {
PublicKey string `json:"key"`
Parent string `json:"parent"`
Sequence uint64 `json:"sequence"`
//Port uint64 `json:"port"`
//Rest uint64 `json:"rest"`
}
func (a *AdminSocket) getTreeHandler(_ *GetTreeRequest, res *GetTreeResponse) error {
func (a *AdminSocket) getTreeHandler(req *GetTreeRequest, res *GetTreeResponse) error {
tree := a.core.GetTree()
res.Tree = make([]TreeEntry, 0, len(tree))
for _, d := range tree {
@@ -32,10 +34,12 @@ func (a *AdminSocket) getTreeHandler(_ *GetTreeRequest, res *GetTreeResponse) er
PublicKey: hex.EncodeToString(d.Key[:]),
Parent: hex.EncodeToString(d.Parent[:]),
Sequence: d.Sequence,
//Port: d.Port,
//Rest: d.Rest,
})
}
slices.SortStableFunc(res.Tree, func(a, b TreeEntry) int {
return strings.Compare(a.PublicKey, b.PublicKey)
sort.SliceStable(res.Tree, func(i, j int) bool {
return strings.Compare(res.Tree[i].PublicKey, res.Tree[j].PublicKey) < 0
})
return nil
}

View File

@@ -12,7 +12,7 @@ type RemovePeerRequest struct {
type RemovePeerResponse struct{}
func (a *AdminSocket) removePeerHandler(req *RemovePeerRequest, _ *RemovePeerResponse) error {
func (a *AdminSocket) removePeerHandler(req *RemovePeerRequest, res *RemovePeerResponse) error {
u, err := url.Parse(req.Uri)
if err != nil {
return fmt.Errorf("unable to parse peering URI: %w", err)

View File

@@ -30,7 +30,7 @@ type PeerInfo struct {
Coords []uint64
Port uint64
Priority uint8
Cost uint64
Multipath bool
RXBytes uint64
TXBytes uint64
Uptime time.Duration
@@ -88,6 +88,7 @@ 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
@@ -95,7 +96,6 @@ 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)
}
@@ -150,14 +150,7 @@ func (c *Core) GetSessions() []SessionInfo {
// parsed from a string of the form e.g. "tcp://a.b.c.d:e". In the case of a
// link-local address, the interface should be provided as the second argument.
func (c *Core) Listen(u *url.URL, sintf string) (*Listener, error) {
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)
return c.links.listen(u, sintf)
}
// Address gets the IPv6 address of the Yggdrasil node. This is always a /128

View File

@@ -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, "", false); err != nil {
if _, err = c.links.listen(u, ""); err != nil {
c.log.Errorf("Failed to start listener %q: %s\n", listenaddr, err)
}
}

View File

@@ -25,27 +25,6 @@ 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) {
@@ -222,69 +201,3 @@ 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)
}

View File

@@ -4,10 +4,10 @@ import (
"bytes"
"context"
"encoding/hex"
"errors"
"fmt"
"io"
"net"
"net/netip"
"net/url"
"strconv"
"strings"
@@ -38,16 +38,13 @@ type links struct {
unix *linkUNIX // UNIX interface support
socks *linkSOCKS // SOCKS interface support
quic *linkQUIC // QUIC interface support
ws *linkWS // WS interface support
wss *linkWSS // WSS interface support
// _links can only be modified safely from within the links actor
_links map[linkInfo]*link // *link is nil if connection in progress
_listeners map[*Listener]context.CancelFunc
_links map[linkInfo]*link // *link is nil if connection in progress
}
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
@@ -75,6 +72,7 @@ type linkOptions struct {
tlsSNI string
password []byte
maxBackoff time.Duration
multipath bool
}
type Listener struct {
@@ -87,6 +85,13 @@ func (l *Listener) Addr() net.Addr {
return l.listener.Addr()
}
func (l *Listener) Close() error {
l.Cancel()
err := l.listener.Close()
<-l.ctx.Done()
return err
}
func (l *links) init(c *Core) error {
l.core = c
l.tcp = l.newLinkTCP()
@@ -94,23 +99,33 @@ func (l *links) init(c *Core) error {
l.unix = l.newLinkUNIX()
l.socks = l.newLinkSOCKS()
l.quic = l.newLinkQUIC()
l.ws = l.newLinkWS()
l.wss = l.newLinkWSS()
l._links = make(map[linkInfo]*link)
l._listeners = make(map[*Listener]context.CancelFunc)
var listeners []ListenAddress
phony.Block(c, func() {
listeners = make([]ListenAddress, 0, len(c.config._listeners))
for listener := range c.config._listeners {
listeners = append(listeners, listener)
}
})
return nil
}
func (l *links) shutdown() {
phony.Block(l, func() {
for listener := range l._listeners {
_ = listener.listener.Close()
phony.Block(l.tcp, func() {
for l := range l.tcp._listeners {
_ = l.Close()
}
for _, link := range l._links {
if link._conn != nil {
_ = link._conn.Close()
}
})
phony.Block(l.tls, func() {
for l := range l.tls._listeners {
_ = l.Close()
}
})
phony.Block(l.unix, func() {
for l := range l.unix._listeners {
_ = l.Close()
}
})
}
@@ -123,9 +138,10 @@ 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("invalid password supplied")
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
@@ -179,6 +195,17 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error {
}
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
@@ -336,7 +363,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, false); 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)
}
@@ -346,11 +373,9 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error {
_ = lc.Close()
phony.Block(l, func() {
state._conn = nil
if err == nil {
err = fmt.Errorf("remote side closed the connection")
if state._err = err; state._err != nil {
state._errtime = time.Now()
}
state._err = err
state._errtime = time.Now()
})
// If the link is persistently configured, back off if needed
@@ -359,9 +384,8 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error {
if backoffNow() {
continue
}
return
}
// Ephemeral or incoming connections don't reconnect.
return
}
}()
})
@@ -395,7 +419,7 @@ func (l *links) remove(u *url.URL, sintf string, _ linkType) error {
return retErr
}
func (l *links) listen(u *url.URL, sintf string, local bool) (*Listener, error) {
func (l *links) listen(u *url.URL, sintf string) (*Listener, error) {
ctx, cancel := context.WithCancel(l.core.ctx)
var protocol linkProtocol
switch strings.ToLower(u.Scheme) {
@@ -407,15 +431,40 @@ func (l *links) listen(u *url.URL, sintf string, local bool) (*Listener, error)
protocol = l.unix
case "quic":
protocol = l.quic
case "ws":
protocol = l.ws
case "wss":
protocol = l.wss
default:
cancel()
return nil, ErrLinkUnrecognisedSchema
}
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
@@ -423,39 +472,14 @@ func (l *links) listen(u *url.URL, sintf string, local bool) (*Listener, error)
li := &Listener{
listener: listener,
ctx: ctx,
Cancel: func() {
cancel()
_ = listener.Close()
},
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)
}
phony.Block(l, func() {
l._listeners[li] = cancel
})
go func() {
l.core.log.Infof("%s listener started on %s", strings.ToUpper(u.Scheme), li.listener.Addr())
defer l.core.log.Infof("%s listener stopped on %s", strings.ToUpper(u.Scheme), li.listener.Addr())
defer phony.Block(l, func() {
delete(l._listeners, li)
})
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 := li.listener.Accept()
conn, err := listener.Accept()
if err != nil {
return
}
@@ -511,22 +535,13 @@ func (l *links) listen(u *url.URL, sintf string, local bool) (*Listener, error)
// Store the state of the link so that it can be queried later.
l._links[info] = state
})
defer phony.Block(l, func() {
if l._links[info] == state {
delete(l._links, info)
}
})
if lc == nil {
return
}
// Give the connection to the handler. The handler will block
// for the lifetime of the connection.
switch err = l.handler(linkTypeIncoming, options, lc, nil, local); {
case err == nil:
case errors.Is(err, io.EOF):
case errors.Is(err, net.ErrClosed):
default:
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)
}
@@ -534,6 +549,11 @@ func (l *links) listen(u *url.URL, sintf string, local bool) (*Listener, error)
// try to close the underlying socket just in case and then
// drop the link state.
_ = lc.Close()
phony.Block(l, func() {
if l._links[info] == state {
delete(l._links, info)
}
})
}(conn)
}
}()
@@ -553,17 +573,13 @@ func (l *links) connect(ctx context.Context, u *url.URL, info linkInfo, options
dialer = l.unix
case "quic":
dialer = l.quic
case "ws":
dialer = l.ws
case "wss":
dialer = l.wss
default:
return nil, ErrLinkUnrecognisedSchema
}
return dialer.dial(ctx, u, info, options)
}
func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn, success func(), local bool) 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
@@ -606,22 +622,20 @@ func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn, s
}
}
// Check if we're authorized to connect to this key / IP
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))
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))
}
dir := "outbound"
if linkType == linkTypeIncoming {
@@ -649,11 +663,21 @@ func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn, s
l.core.log.Infof("Disconnected %s: %s, source %s; error: %s",
dir, remoteStr, localStr, err)
}
return err
return nil
}
func urlForLinkInfo(u url.URL) url.URL {
u.RawQuery = ""
if host, _, err := net.SplitHostPort(u.Host); err == nil {
if addr, err := netip.ParseAddr(host); err == nil {
// For peers that look like multicast peers (i.e.
// link-local addresses), we will ignore the port number,
// otherwise we might open multiple connections to them.
if addr.IsLinkLocalUnicast() {
u.Host = fmt.Sprintf("[%s]", addr.String())
}
}
}
return u
}

View File

@@ -65,7 +65,7 @@ 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) {
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

View File

@@ -47,6 +47,6 @@ func (l *linkSOCKS) dial(_ context.Context, url *url.URL, info linkInfo, options
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")
}

View File

@@ -15,6 +15,7 @@ type linkTCP struct {
phony.Inbox
*links
listenconfig *net.ListenConfig
_listeners map[*Listener]context.CancelFunc
}
func (l *links) newLinkTCP() *linkTCP {
@@ -23,6 +24,7 @@ func (l *links) newLinkTCP() *linkTCP {
listenconfig: &net.ListenConfig{
KeepAlive: -1,
},
_listeners: map[*Listener]context.CancelFunc{},
}
lt.listenconfig.Control = lt.tcpContext
return lt
@@ -34,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
@@ -53,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
}
@@ -67,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
}
@@ -86,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
@@ -110,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)

View File

@@ -28,6 +28,6 @@ func (t *linkTCP) tcpContext(network, address string, c syscall.RawConn) error {
}
}
func (t *linkTCP) getControl(_ string) func(string, string, syscall.RawConn) error {
func (t *linkTCP) getControl(sintf string) func(string, string, syscall.RawConn) error {
return t.tcpContext
}

View 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
}
}

View File

@@ -3,7 +3,6 @@ package core
import (
"context"
"crypto/tls"
"fmt"
"net"
"net/url"
@@ -13,9 +12,10 @@ import (
type linkTLS struct {
phony.Inbox
*links
tcp *linkTCP
listener *net.ListenConfig
config *tls.Config
tcp *linkTCP
listener *net.ListenConfig
config *tls.Config
_listeners map[*Listener]context.CancelFunc
}
func (l *links) newLinkTLS(tcp *linkTCP) *linkTLS {
@@ -26,13 +26,14 @@ func (l *links) newLinkTLS(tcp *linkTCP) *linkTLS {
Control: tcp.tcpContext,
KeepAlive: -1,
},
config: l.core.config.tls.Clone(),
config: l.core.config.tls.Clone(),
_listeners: map[*Listener]context.CancelFunc{},
}
return lt
}
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
}
@@ -56,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
}

View File

@@ -12,8 +12,9 @@ import (
type linkUNIX struct {
phony.Inbox
*links
dialer *net.Dialer
listener *net.ListenConfig
dialer *net.Dialer
listener *net.ListenConfig
_listeners map[*Listener]context.CancelFunc
}
func (l *links) newLinkUNIX() *linkUNIX {
@@ -26,6 +27,7 @@ func (l *links) newLinkUNIX() *linkUNIX {
listener: &net.ListenConfig{
KeepAlive: -1,
},
_listeners: map[*Listener]context.CancelFunc{},
}
return lt
}
@@ -38,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)
}

View File

@@ -1,127 +0,0 @@
package core
import (
"context"
"net"
"net/http"
"net/url"
"time"
"github.com/Arceliar/phony"
"github.com/coder/websocket"
)
type linkWS struct {
phony.Inbox
*links
listenconfig *net.ListenConfig
}
type linkWSConn struct {
net.Conn
}
type linkWSListener struct {
ch chan *linkWSConn
ctx context.Context
httpServer *http.Server
listener net.Listener
}
type wsServer struct {
ch chan *linkWSConn
ctx context.Context
}
func (l *linkWSListener) Accept() (net.Conn, error) {
qs := <-l.ch
if qs == nil {
return nil, context.Canceled
}
return qs, nil
}
func (l *linkWSListener) Addr() net.Addr {
return l.listener.Addr()
}
func (l *linkWSListener) Close() error {
if err := l.httpServer.Shutdown(l.ctx); err != nil {
return err
}
return l.listener.Close()
}
func (s *wsServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/health" || r.URL.Path == "/healthz" {
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("OK"))
return
}
c, err := websocket.Accept(w, r, &websocket.AcceptOptions{
Subprotocols: []string{"ygg-ws"},
})
if err != nil {
return
}
if c.Subprotocol() != "ygg-ws" {
c.Close(websocket.StatusPolicyViolation, "client must speak the ygg-ws subprotocol")
return
}
s.ch <- &linkWSConn{
Conn: websocket.NetConn(s.ctx, c, websocket.MessageBinary),
}
}
func (l *links) newLinkWS() *linkWS {
lt := &linkWS{
links: l,
listenconfig: &net.ListenConfig{
KeepAlive: -1,
},
}
return lt
}
func (l *linkWS) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
wsconn, _, err := websocket.Dial(ctx, url.String(), &websocket.DialOptions{
Subprotocols: []string{"ygg-ws"},
})
if err != nil {
return nil, err
}
return &linkWSConn{
Conn: websocket.NetConn(ctx, wsconn, websocket.MessageBinary),
}, nil
}
func (l *linkWS) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) {
nl, err := l.listenconfig.Listen(ctx, "tcp", url.Host)
if err != nil {
return nil, err
}
ch := make(chan *linkWSConn)
httpServer := &http.Server{
Handler: &wsServer{
ch: ch,
ctx: ctx,
},
BaseContext: func(_ net.Listener) context.Context { return ctx },
ReadTimeout: time.Second * 10,
WriteTimeout: time.Second * 10,
}
lwl := &linkWSListener{
ch: ch,
ctx: ctx,
httpServer: httpServer,
listener: nl,
}
go lwl.httpServer.Serve(nl) // nolint:errcheck
return lwl, nil
}

View File

@@ -1,43 +0,0 @@
package core
import (
"context"
"fmt"
"net"
"net/url"
"github.com/Arceliar/phony"
"github.com/coder/websocket"
)
type linkWSS struct {
phony.Inbox
*links
}
type linkWSSConn struct {
net.Conn
}
func (l *links) newLinkWSS() *linkWSS {
lwss := &linkWSS{
links: l,
}
return lwss
}
func (l *linkWSS) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
wsconn, _, err := websocket.Dial(ctx, url.String(), &websocket.DialOptions{
Subprotocols: []string{"ygg-ws"},
})
if err != nil {
return nil, err
}
return &linkWSSConn{
Conn: websocket.NetConn(ctx, wsconn, websocket.MessageBinary),
}, nil
}
func (l *linkWSS) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) {
return nil, fmt.Errorf("WSS listener not supported, use WS listener behind reverse proxy instead")
}

View File

@@ -8,6 +8,7 @@ import (
"bytes"
"crypto/ed25519"
"encoding/binary"
"fmt"
"io"
"golang.org/x/crypto/blake2b"
@@ -37,16 +38,6 @@ const (
metaPriority // uint8
)
type handshakeError string
func (e handshakeError) Error() string { return string(e) }
const ErrHandshakeInvalidPreamble = handshakeError("invalid handshake, remote side is not Yggdrasil")
const ErrHandshakeInvalidLength = handshakeError("invalid handshake length, possible version mismatch")
const ErrHandshakeInvalidPassword = handshakeError("invalid password supplied, check your config")
const ErrHandshakeHashFailure = handshakeError("invalid hash length")
const ErrHandshakeIncorrectPassword = handshakeError("password does not match remote side")
// Gets a base metadata with no keys set, but with the correct version numbers.
func version_getBaseMetadata() version_metadata {
return version_metadata{
@@ -86,7 +77,7 @@ func (m *version_metadata) encode(privateKey ed25519.PrivateKey, password []byte
return nil, err
}
if n != ed25519.PublicKeySize {
return nil, ErrHandshakeHashFailure
return nil, fmt.Errorf("hash writer only wrote %d bytes", n)
}
hash := hasher.Sum(nil)
bs = append(bs, ed25519.Sign(privateKey, hash)...)
@@ -103,11 +94,11 @@ func (m *version_metadata) decode(r io.Reader, password []byte) error {
}
meta := [4]byte{'m', 'e', 't', 'a'}
if !bytes.Equal(bh[:4], meta[:]) {
return ErrHandshakeInvalidPreamble
return fmt.Errorf("invalid handshake preamble")
}
hl := binary.BigEndian.Uint16(bh[4:6])
if hl < ed25519.SignatureSize {
return ErrHandshakeInvalidLength
return fmt.Errorf("invalid handshake length")
}
bs := make([]byte, hl)
if _, err := io.ReadFull(r, bs); err != nil {
@@ -141,15 +132,15 @@ func (m *version_metadata) decode(r io.Reader, password []byte) error {
hasher, err := blake2b.New512(password)
if err != nil {
return ErrHandshakeInvalidPassword
return fmt.Errorf("invalid password supplied")
}
n, err := hasher.Write(m.publicKey)
if err != nil || n != ed25519.PublicKeySize {
return ErrHandshakeHashFailure
return fmt.Errorf("failed to generate hash")
}
hash := hasher.Sum(nil)
if !ed25519.Verify(m.publicKey, hash, sig) {
return ErrHandshakeIncorrectPassword
return fmt.Errorf("password is incorrect")
}
return nil
}

View File

@@ -11,7 +11,7 @@ type GetMulticastInterfacesResponse struct {
Interfaces []string `json:"multicast_interfaces"`
}
func (m *Multicast) getMulticastInterfacesHandler(_ *GetMulticastInterfacesRequest, res *GetMulticastInterfacesResponse) error {
func (m *Multicast) getMulticastInterfacesHandler(req *GetMulticastInterfacesRequest, res *GetMulticastInterfacesResponse) error {
res.Interfaces = []string{}
for _, v := range m.Interfaces() {
res.Interfaces = append(res.Interfaces, v.Name)

View File

@@ -9,12 +9,10 @@ import (
"math/rand"
"net"
"net/url"
"sync/atomic"
"time"
"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"
@@ -30,7 +28,7 @@ type Multicast struct {
core *core.Core
log *log.Logger
sock *ipv6.PacketConn
running atomic.Bool
_isOpen bool
_listeners map[string]*listenerInfo
_interfaces map[string]*interfaceInfo
_timer *time.Timer
@@ -81,7 +79,7 @@ func New(core *core.Core, log *log.Logger, opts ...SetupOption) (*Multicast, err
}
func (m *Multicast) _start() error {
if !m.running.CompareAndSwap(false, true) {
if m._isOpen {
return fmt.Errorf("multicast module is already started")
}
var anyEnabled bool
@@ -89,14 +87,12 @@ func (m *Multicast) _start() error {
anyEnabled = anyEnabled || intf.Beacon || intf.Listen
}
if !anyEnabled {
m.running.Store(false)
return nil
}
m.log.Debugln("Starting multicast module")
defer m.log.Debugln("Started multicast module")
addr, err := net.ResolveUDPAddr("udp", string(m.config._groupAddr))
if err != nil {
m.running.Store(false)
return err
}
listenString := fmt.Sprintf("[::]:%v", addr.Port)
@@ -105,7 +101,6 @@ func (m *Multicast) _start() error {
}
conn, err := lc.ListenPacket(context.Background(), "udp6", listenString)
if err != nil {
m.running.Store(false)
return err
}
m.sock = ipv6.NewPacketConn(conn)
@@ -113,6 +108,7 @@ func (m *Multicast) _start() error {
// Windows can't set this flag, so we need to handle it in other ways
}
m._isOpen = true
go m.listen()
m.Act(nil, m._multicastStarted)
m.Act(nil, m._announce)
@@ -122,7 +118,11 @@ func (m *Multicast) _start() error {
// IsStarted returns true if the module has been started.
func (m *Multicast) IsStarted() bool {
return m.running.Load()
var isOpen bool
phony.Block(m, func() {
isOpen = m._isOpen
})
return isOpen
}
// Stop stops the multicast module.
@@ -136,10 +136,8 @@ func (m *Multicast) Stop() error {
}
func (m *Multicast) _stop() error {
if !m.running.CompareAndSwap(true, false) {
return nil
}
m.log.Infoln("Stopping multicast module")
m._isOpen = false
if m.sock != nil {
m.sock.Close()
}
@@ -149,8 +147,7 @@ func (m *Multicast) _stop() error {
func (m *Multicast) _updateInterfaces() {
interfaces := m._getAllowedInterfaces()
for name, info := range interfaces {
// 'anet' package is used here to avoid https://github.com/golang/go/issues/40569
addrs, err := anet.InterfaceAddrsByInterface(&info.iface)
addrs, err := info.iface.Addrs()
if err != nil {
m.log.Warnf("Failed up get addresses for interface %s: %s", name, err)
delete(interfaces, name)
@@ -158,7 +155,6 @@ func (m *Multicast) _updateInterfaces() {
}
info.addrs = addrs
interfaces[name] = info
m.log.Debugf("Discovered addresses for interface %s: %s", name, addrs)
}
m._interfaces = interfaces
}
@@ -177,11 +173,10 @@ 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
// 'anet' package is used here to avoid https://github.com/golang/go/issues/40569
allifaces, err := anet.Interfaces()
allifaces, err := net.Interfaces()
if err != nil {
// Don't panic, since this may be from e.g. too many open files (from too much connection spam)
m.log.Debugf("Failed to get interfaces: %s", err)
// TODO? log something
return nil
}
// Work out which interfaces to announce on
@@ -190,8 +185,6 @@ 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:
@@ -240,7 +233,7 @@ func (m *Multicast) AnnounceNow() {
}
func (m *Multicast) _announce() {
if !m.running.Load() {
if !m._isOpen {
return
}
m._updateInterfaces()
@@ -257,7 +250,7 @@ func (m *Multicast) _announce() {
for name, info := range m._listeners {
// Prepare our stop function!
stop := func() {
info.listener.Cancel()
info.listener.Close()
delete(m._listeners, name)
m.log.Debugln("No longer multicasting on", name)
}
@@ -327,7 +320,7 @@ func (m *Multicast) _announce() {
Host: net.JoinHostPort(addrIP.String(), fmt.Sprintf("%d", info.port)),
RawQuery: v.Encode(),
}
if li, err := m.core.ListenLocal(u, iface.Name); err == nil {
if li, err := m.core.Listen(u, iface.Name); err == nil {
m.log.Debugln("Started multicasting on", iface.Name)
// Store the listener so that we can stop it later if needed
linfo = &listenerInfo{listener: li, time: time.Now(), port: info.port}
@@ -383,9 +376,6 @@ func (m *Multicast) listen() {
bs := make([]byte, 2048)
hb := make([]byte, 0, blake2b.Size) // Reused to reduce hash allocations
for {
if !m.running.Load() {
return
}
n, rcm, fromAddr, err := m.sock.ReadFrom(bs)
if err != nil {
if !m.IsStarted() {

View File

@@ -31,7 +31,7 @@ import (
)
func (m *Multicast) _multicastStarted() {
if !m.running.Load() {
if !m._isOpen {
return
}
C.StopAWDLBrowsing()

View File

@@ -1,70 +1,42 @@
package tun
import (
"errors"
wgtun "golang.zx2c4.com/wireguard/tun"
)
const TUN_OFFSET_BYTES = 80 // sizeof(virtio_net_hdr)
const TUN_OFFSET_BYTES = 4
func (tun *TunAdapter) read() {
vs := tun.iface.BatchSize()
bufs := make([][]byte, vs)
sizes := make([]int, vs)
for i := range bufs {
bufs[i] = make([]byte, TUN_OFFSET_BYTES+65535)
}
var buf [TUN_OFFSET_BYTES + 65535]byte
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
}
n, err := tun.iface.Read(buf[:], TUN_OFFSET_BYTES)
if n <= TUN_OFFSET_BYTES || err != nil {
tun.log.Errorln("Error reading TUN:", err)
return
}
for i, b := range bufs[:n] {
if _, err := tun.rwc.Write(b[TUN_OFFSET_BYTES : TUN_OFFSET_BYTES+sizes[i]]); err != nil {
tun.log.Debugln("Unable to send packet:", err)
ferr := tun.iface.Flush()
if ferr != nil {
tun.log.Errorln("Unable to flush packets:", ferr)
}
}
}
}
func (tun *TunAdapter) queue() {
for {
p := bufPool.Get().([]byte)[:bufPoolSize]
n, err := tun.rwc.Read(p)
if err != nil {
tun.log.Errorln("Exiting TUN writer due to core read error:", err)
return
}
tun.ch <- p[:n]
begin := TUN_OFFSET_BYTES
end := begin + n
bs := buf[begin:end]
if _, err := tun.rwc.Write(bs); err != nil {
tun.log.Debugln("Unable to send packet:", err)
}
}
}
func (tun *TunAdapter) write() {
vs := cap(tun.ch)
bufs := make([][]byte, vs)
for i := range bufs {
bufs[i] = make([]byte, TUN_OFFSET_BYTES+65535)
}
var buf [TUN_OFFSET_BYTES + 65535]byte
for {
n := len(tun.ch)
if n == 0 {
n = 1 // Nothing queued up yet, wait for it instead
}
for i := 0; i < n; i++ {
msg := <-tun.ch
bufs[i] = append(bufs[i][:TUN_OFFSET_BYTES], msg...)
bufPool.Put(msg) // nolint:staticcheck
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)
return
}
if !tun.isEnabled {
continue // Nothing to do, the tun isn't enabled
}
if _, err := tun.iface.Write(bufs[:n], TUN_OFFSET_BYTES); err != nil {
bs = buf[:TUN_OFFSET_BYTES+n]
if _, err = tun.iface.Write(bs, TUN_OFFSET_BYTES); err != nil {
tun.Act(nil, func() {
if !tun.isOpen {
tun.log.Errorln("TUN iface write error:", err)

View File

@@ -10,10 +10,9 @@ import (
"fmt"
"io"
"net"
"sync"
"github.com/Arceliar/phony"
wgtun "golang.zx2c4.com/wireguard/tun"
"golang.zx2c4.com/wireguard/tun"
"github.com/yggdrasil-network/yggdrasil-go/src/address"
"github.com/yggdrasil-network/yggdrasil-go/src/config"
@@ -40,7 +39,7 @@ type TunAdapter struct {
addr address.Address
subnet address.Subnet
mtu uint64
iface wgtun.Device
iface tun.Device
phony.Inbox // Currently only used for _handlePacket from the reader, TODO: all the stuff that currently needs a mutex below
isOpen bool
isEnabled bool // Used by the writer to drop sessionTraffic if not enabled
@@ -49,7 +48,6 @@ type TunAdapter struct {
name InterfaceName
mtu InterfaceMTU
}
ch chan []byte
}
// Gets the maximum supported MTU for the platform based on the defaults in
@@ -147,8 +145,6 @@ func (tun *TunAdapter) _start() error {
tun.rwc.SetMTU(tun.MTU())
tun.isOpen = true
tun.isEnabled = true
tun.ch = make(chan []byte, tun.iface.BatchSize())
go tun.queue()
go tun.read()
go tun.write()
return nil
@@ -182,12 +178,3 @@ func (tun *TunAdapter) _stop() error {
}
return nil
}
const bufPoolSize = TUN_OFFSET_BYTES + 65535
var bufPool = sync.Pool{
New: func() any {
b := [bufPoolSize]byte{}
return b[:]
},
}

View File

@@ -1,5 +1,5 @@
//go:build freebsd
// +build freebsd
//go:build openbsd || freebsd
// +build openbsd freebsd
package tun
@@ -54,6 +54,11 @@ 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
@@ -107,6 +112,26 @@ 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

View File

@@ -1,122 +0,0 @@
//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
}

View File

@@ -8,12 +8,10 @@ import (
"fmt"
"log"
"net/netip"
"time"
"github.com/yggdrasil-network/yggdrasil-go/src/config"
"golang.org/x/sys/windows"
"golang.zx2c4.com/wintun"
wgtun "golang.zx2c4.com/wireguard/tun"
"golang.zx2c4.com/wireguard/windows/elevate"
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
@@ -33,23 +31,11 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error {
if guid, err = windows.GUIDFromString("{8f59971a-7872-4aa6-b2eb-061fc4e9d0a7}"); err != nil {
return err
}
iface, err = wgtun.CreateTUNWithRequestedGUID(ifname, &guid, int(mtu))
if err != nil {
// Very rare condition, it will purge the old device and create new
tun.log.Printf("Error creating TUN: '%s'", err)
wintun.Uninstall()
time.Sleep(3 * time.Second)
tun.log.Printf("Trying again")
iface, err = wgtun.CreateTUNWithRequestedGUID(ifname, &guid, int(mtu))
if err != nil {
return err
}
if iface, err = wgtun.CreateTUNWithRequestedGUID(ifname, &guid, int(mtu)); err != nil {
return err
}
tun.log.Printf("Waiting for TUN to come up")
time.Sleep(1 * time.Second)
tun.iface = iface
if addr != "" {
tun.log.Printf("Setting up address")
if err = tun.setupAddress(addr); err != nil {
tun.log.Errorln("Failed to set up TUN address:", err)
return err
@@ -62,7 +48,6 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error {
if mtu, err := iface.MTU(); err == nil {
tun.mtu = uint64(mtu)
}
tun.log.Printf("TUN is set up successfully")
return nil
})
}