mirror of
https://github.com/yggdrasil-network/yggdrasil-go.git
synced 2025-08-27 02:57:02 +00:00
Compare commits
54 Commits
arc/linkfi
...
v0.5.0rc3
Author | SHA1 | Date | |
---|---|---|---|
![]() |
90c6288f7c | ||
![]() |
094f80f39c | ||
![]() |
955aa4af79 | ||
![]() |
73c6c25bd9 | ||
![]() |
80e56eafcd | ||
![]() |
6a9493757d | ||
![]() |
8ea20cd205 | ||
![]() |
a2dffeff33 | ||
![]() |
a2053b51fe | ||
![]() |
aceb037c57 | ||
![]() |
bcd80b043f | ||
![]() |
74ca02edfd | ||
![]() |
e110dd46fd | ||
![]() |
88b773cd0a | ||
![]() |
efb4b4635d | ||
![]() |
117e4b88f8 | ||
![]() |
4b48fd0b5f | ||
![]() |
854cd75f04 | ||
![]() |
4f656685ef | ||
![]() |
ed8ba584e2 | ||
![]() |
2a21241738 | ||
![]() |
45b773eade | ||
![]() |
6dc847de31 | ||
![]() |
bd7e699130 | ||
![]() |
268ffbfd14 | ||
![]() |
490c11c29e | ||
![]() |
991ea8b876 | ||
![]() |
68d1036de8 | ||
![]() |
fa3d943ba9 | ||
![]() |
9defa35c66 | ||
![]() |
c8b9aaeb67 | ||
![]() |
8f3ab1d83c | ||
![]() |
12a3a8c73b | ||
![]() |
6ab0639b82 | ||
![]() |
fbc5f62add | ||
![]() |
5b203ad8c5 | ||
![]() |
fe14981dda | ||
![]() |
63b214f6b7 | ||
![]() |
ff96740ac7 | ||
![]() |
7f94463332 | ||
![]() |
bcbabff80f | ||
![]() |
99dd8f85d3 | ||
![]() |
57d9a2399f | ||
![]() |
423fc248d2 | ||
![]() |
516fcce6b3 | ||
![]() |
d8dc6b2670 | ||
![]() |
109f59c7dc | ||
![]() |
002b984c04 | ||
![]() |
5e684550a8 | ||
![]() |
80724438c9 | ||
![]() |
b0f8d8af13 | ||
![]() |
31177f5a73 | ||
![]() |
f6c0d8406d | ||
![]() |
db9b57c052 |
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.19
|
||||
go-version: 1.21
|
||||
- uses: actions/checkout@v3
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
goversion: ["1.19", "1.20"]
|
||||
goversion: ["1.20", "1.21"]
|
||||
|
||||
name: Build & Test (Linux, Go ${{ matrix.goversion }})
|
||||
needs: [lint]
|
||||
@@ -75,7 +75,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
goversion: ["1.19", "1.20"]
|
||||
goversion: ["1.20", "1.21"]
|
||||
|
||||
name: Build & Test (Windows, Go ${{ matrix.goversion }})
|
||||
needs: [lint]
|
||||
@@ -99,7 +99,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
goversion: ["1.19", "1.20"]
|
||||
goversion: ["1.20", "1.21"]
|
||||
|
||||
name: Build & Test (macOS, Go ${{ matrix.goversion }})
|
||||
needs: [lint]
|
||||
|
768
CHANGELOG.md
768
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@@ -16,6 +16,7 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||
)
|
||||
@@ -27,6 +28,8 @@ type keySet struct {
|
||||
|
||||
func main() {
|
||||
threads := runtime.GOMAXPROCS(0)
|
||||
fmt.Println("Threads:", threads)
|
||||
start := time.Now()
|
||||
var currentBest ed25519.PublicKey
|
||||
newKeys := make(chan keySet, threads)
|
||||
for i := 0; i < threads; i++ {
|
||||
@@ -36,7 +39,7 @@ func main() {
|
||||
newKey := <-newKeys
|
||||
if isBetter(currentBest, newKey.pub) || len(currentBest) == 0 {
|
||||
currentBest = newKey.pub
|
||||
fmt.Println("-----")
|
||||
fmt.Println("-----", time.Since(start))
|
||||
fmt.Println("Priv:", hex.EncodeToString(newKey.priv))
|
||||
fmt.Println("Pub:", hex.EncodeToString(newKey.pub))
|
||||
addr := address.AddrForKey(newKey.pub)
|
||||
|
@@ -44,8 +44,6 @@ func main() {
|
||||
useconffile := flag.String("useconffile", "", "read HJSON/JSON config from specified file path")
|
||||
normaliseconf := flag.Bool("normaliseconf", false, "use in combination with either -useconf or -useconffile, outputs your configuration normalised")
|
||||
exportkey := flag.Bool("exportkey", false, "use in combination with either -useconf or -useconffile, outputs your private key in PEM format")
|
||||
exportcsr := flag.Bool("exportcsr", false, "use in combination with either -useconf or -useconffile, outputs your self-signed certificate request in PEM format")
|
||||
exportcert := flag.Bool("exportcert", false, "use in combination with either -useconf or -useconffile, outputs your self-signed certificate in PEM format")
|
||||
confjson := flag.Bool("json", false, "print configuration from -genconf or -normaliseconf as JSON instead of HJSON")
|
||||
autoconf := flag.Bool("autoconf", false, "automatic mode (dynamic IP, peer with IPv6 neighbors)")
|
||||
ver := flag.Bool("version", false, "prints the version of this build")
|
||||
@@ -69,7 +67,7 @@ func main() {
|
||||
|
||||
case "syslog":
|
||||
if syslogger, err := gsyslog.NewLogger(gsyslog.LOG_NOTICE, "DAEMON", version.BuildName()); err == nil {
|
||||
logger = log.New(syslogger, "", log.Flags())
|
||||
logger = log.New(syslogger, "", log.Flags()&^(log.Ldate|log.Ltime))
|
||||
}
|
||||
|
||||
default:
|
||||
@@ -134,6 +132,7 @@ func main() {
|
||||
if *getaddr || *getsnet {
|
||||
fmt.Println("\nError: You need to specify some config data using -useconf or -useconffile.")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
privateKey := ed25519.PrivateKey(cfg.PrivateKey)
|
||||
@@ -175,22 +174,6 @@ func main() {
|
||||
}
|
||||
fmt.Println(string(pem))
|
||||
return
|
||||
|
||||
case *exportcsr:
|
||||
pem, err := cfg.GenerateCertificateSigningRequest()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(string(pem))
|
||||
return
|
||||
|
||||
case *exportcert:
|
||||
pem, err := cfg.MarshalPEMCertificate()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(string(pem))
|
||||
return
|
||||
}
|
||||
|
||||
n := &node{}
|
||||
@@ -212,9 +195,6 @@ func main() {
|
||||
options = append(options, core.Peer{URI: peer, SourceInterface: intf})
|
||||
}
|
||||
}
|
||||
for _, root := range cfg.RootCertificates {
|
||||
options = append(options, core.RootCertificate(*root))
|
||||
}
|
||||
for _, allowed := range cfg.AllowedPublicKeys {
|
||||
k, err := hex.DecodeString(allowed)
|
||||
if err != nil {
|
||||
@@ -225,6 +205,10 @@ func main() {
|
||||
if n.core, err = core.New(cfg.Certificate, logger, options...); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
address, subnet := n.core.Address(), n.core.Subnet()
|
||||
logger.Infof("Your public key is %s", hex.EncodeToString(n.core.PublicKey()))
|
||||
logger.Infof("Your IPv6 address is %s", address.String())
|
||||
logger.Infof("Your IPv6 subnet is %s", subnet.String())
|
||||
}
|
||||
|
||||
// Setup the admin socket.
|
||||
@@ -250,6 +234,7 @@ func main() {
|
||||
Listen: intf.Listen,
|
||||
Port: intf.Port,
|
||||
Priority: uint8(intf.Priority),
|
||||
Password: intf.Password,
|
||||
})
|
||||
}
|
||||
if n.multicast, err = multicast.New(n.core, logger, options...); err != nil {
|
||||
|
@@ -37,7 +37,7 @@ if [ $IOS ]; then
|
||||
echo "Building framework for iOS"
|
||||
go get golang.org/x/mobile/bind
|
||||
gomobile bind \
|
||||
-target ios -tags mobile -o Yggdrasil.xcframework \
|
||||
-target ios,macos -tags mobile -o Yggdrasil.xcframework \
|
||||
-ldflags="$LDFLAGS $STRIP" -gcflags="$GCFLAGS" \
|
||||
./contrib/mobile ./src/config;
|
||||
fi
|
||||
|
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/core"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/ipv6rwc"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/multicast"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/tun"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/version"
|
||||
|
||||
_ "golang.org/x/mobile/bind"
|
||||
@@ -28,7 +29,9 @@ type Yggdrasil struct {
|
||||
iprwc *ipv6rwc.ReadWriteCloser
|
||||
config *config.NodeConfig
|
||||
multicast *multicast.Multicast
|
||||
tun *tun.TunAdapter // optional
|
||||
log MobileLogger
|
||||
logger *log.Logger
|
||||
}
|
||||
|
||||
// StartAutoconfigure starts a node with a randomly generated config
|
||||
@@ -39,6 +42,8 @@ func (m *Yggdrasil) StartAutoconfigure() error {
|
||||
// StartJSON starts a node with the given JSON config. You can get JSON config
|
||||
// (rather than HJSON) by using the GenerateConfigJSON() function
|
||||
func (m *Yggdrasil) StartJSON(configjson []byte) error {
|
||||
setMemLimitIfPossible()
|
||||
|
||||
logger := log.New(m.log, "", 0)
|
||||
logger.EnableLevel("error")
|
||||
logger.EnableLevel("warn")
|
||||
@@ -65,14 +70,18 @@ func (m *Yggdrasil) StartJSON(configjson []byte) error {
|
||||
}
|
||||
options = append(options, core.AllowedPublicKey(k[:]))
|
||||
}
|
||||
for _, root := range m.config.RootCertificates {
|
||||
options = append(options, core.RootCertificate(*root))
|
||||
for _, lAddr := range m.config.Listen {
|
||||
options = append(options, core.ListenAddress(lAddr))
|
||||
}
|
||||
var err error
|
||||
m.core, err = core.New(m.config.Certificate, logger, options...)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
address, subnet := m.core.Address(), m.core.Subnet()
|
||||
logger.Infof("Your public key is %s", hex.EncodeToString(m.core.PublicKey()))
|
||||
logger.Infof("Your IPv6 address is %s", address.String())
|
||||
logger.Infof("Your IPv6 subnet is %s", subnet.String())
|
||||
}
|
||||
|
||||
// Setup the multicast module.
|
||||
@@ -86,11 +95,12 @@ func (m *Yggdrasil) StartJSON(configjson []byte) error {
|
||||
Listen: intf.Listen,
|
||||
Port: intf.Port,
|
||||
Priority: uint8(intf.Priority),
|
||||
Password: intf.Password,
|
||||
})
|
||||
}
|
||||
m.multicast, err = multicast.New(m.core, logger, options...)
|
||||
m.multicast, err = multicast.New(m.core, m.logger, options...)
|
||||
if err != nil {
|
||||
logger.Errorln("An error occurred starting multicast:", err)
|
||||
m.logger.Errorln("An error occurred starting multicast:", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,6 +163,11 @@ func (m *Yggdrasil) Stop() error {
|
||||
if err := m.multicast.Stop(); err != nil {
|
||||
return err
|
||||
}
|
||||
if m.tun != nil {
|
||||
if err := m.tun.Stop(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
m.core.Stop()
|
||||
return nil
|
||||
}
|
||||
@@ -200,8 +215,11 @@ func (m *Yggdrasil) GetPeersJSON() (result string) {
|
||||
IP string
|
||||
}{}
|
||||
for _, v := range m.core.GetPeers() {
|
||||
a := address.AddrForKey(v.Key)
|
||||
ip := net.IP(a[:]).String()
|
||||
var ip string
|
||||
if v.Key != nil {
|
||||
a := address.AddrForKey(v.Key)
|
||||
ip = net.IP(a[:]).String()
|
||||
}
|
||||
peers = append(peers, struct {
|
||||
core.PeerInfo
|
||||
IP string
|
||||
|
@@ -15,6 +15,8 @@ void Log(const char *text) {
|
||||
import "C"
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/tun"
|
||||
)
|
||||
|
||||
type MobileLogger struct {
|
||||
@@ -26,3 +28,13 @@ func (nsl MobileLogger) Write(p []byte) (n int, err error) {
|
||||
C.Log(cstr)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (m *Yggdrasil) TakeOverTUN(fd int32) error {
|
||||
options := []tun.SetupOption{
|
||||
tun.FileDescriptor(fd),
|
||||
tun.InterfaceMTU(m.iprwc.MTU()),
|
||||
}
|
||||
var err error
|
||||
m.tun, err = tun.New(m.iprwc, m.logger, options...)
|
||||
return err
|
||||
}
|
||||
|
10
contrib/mobile/mobile_mem_go120.go
Normal file
10
contrib/mobile/mobile_mem_go120.go
Normal file
@@ -0,0 +1,10 @@
|
||||
//go:build go1.20
|
||||
// +build go1.20
|
||||
|
||||
package mobile
|
||||
|
||||
import "runtime/debug"
|
||||
|
||||
func setMemLimitIfPossible() {
|
||||
debug.SetMemoryLimit(1024 * 1024 * 40)
|
||||
}
|
8
contrib/mobile/mobile_mem_other.go
Normal file
8
contrib/mobile/mobile_mem_other.go
Normal file
@@ -0,0 +1,8 @@
|
||||
//go:build !go1.20
|
||||
// +build !go1.20
|
||||
|
||||
package mobile
|
||||
|
||||
func setMemLimitIfPossible() {
|
||||
// not supported by this Go version
|
||||
}
|
@@ -1,9 +1,21 @@
|
||||
package mobile
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/gologme/log"
|
||||
)
|
||||
|
||||
func TestStartYggdrasil(t *testing.T) {
|
||||
ygg := &Yggdrasil{}
|
||||
logger := log.New(os.Stdout, "", 0)
|
||||
logger.EnableLevel("error")
|
||||
logger.EnableLevel("warn")
|
||||
logger.EnableLevel("info")
|
||||
|
||||
ygg := &Yggdrasil{
|
||||
logger: logger,
|
||||
}
|
||||
if err := ygg.StartAutoconfigure(); err != nil {
|
||||
t.Fatalf("Failed to start Yggdrasil: %s", err)
|
||||
}
|
||||
|
42
go.mod
42
go.mod
@@ -1,39 +1,47 @@
|
||||
module github.com/yggdrasil-network/yggdrasil-go
|
||||
|
||||
go 1.19
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/Arceliar/ironwood v0.0.0-20230521174855-fdfa6326d125
|
||||
github.com/Arceliar/ironwood v0.0.0-20230805085300-86206813435f
|
||||
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d
|
||||
github.com/cheggaaa/pb/v3 v3.0.8
|
||||
github.com/gologme/log v1.2.0
|
||||
github.com/cheggaaa/pb/v3 v3.1.4
|
||||
github.com/gologme/log v1.3.0
|
||||
github.com/hashicorp/go-syslog v1.0.0
|
||||
github.com/hjson/hjson-go/v4 v4.3.0
|
||||
github.com/kardianos/minwinsvc v1.0.2
|
||||
github.com/quic-go/quic-go v0.39.0
|
||||
github.com/vishvananda/netlink v1.1.0
|
||||
golang.org/x/mobile v0.0.0-20221110043201-43a038452099
|
||||
golang.org/x/net v0.9.0
|
||||
golang.org/x/sys v0.7.0
|
||||
golang.org/x/text v0.9.0
|
||||
golang.zx2c4.com/wireguard v0.0.0-20211017052713-f87e87af0d9a
|
||||
golang.zx2c4.com/wireguard/windows v0.4.12
|
||||
golang.org/x/crypto v0.14.0
|
||||
golang.org/x/mobile v0.0.0-20231006135142-2b44d11868fe
|
||||
golang.org/x/net v0.17.0
|
||||
golang.org/x/sys v0.13.0
|
||||
golang.org/x/text v0.13.0
|
||||
golang.zx2c4.com/wireguard v0.0.0-20230223181233-21636207a675
|
||||
golang.zx2c4.com/wireguard/windows v0.5.3
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/bits-and-blooms/bitset v1.5.0 // indirect
|
||||
github.com/bits-and-blooms/bloom/v3 v3.3.1 // indirect
|
||||
github.com/mattn/go-colorable v0.1.8 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.9.5 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.3.4 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
golang.org/x/crypto v0.8.0 // indirect
|
||||
golang.org/x/mod v0.8.0 // indirect
|
||||
golang.org/x/tools v0.6.0 // indirect
|
||||
go.uber.org/mock v0.3.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect
|
||||
golang.org/x/mod v0.13.0 // indirect
|
||||
golang.org/x/tools v0.14.0 // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/VividCortex/ewma v1.2.0 // indirect
|
||||
github.com/fatih/color v1.12.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.13 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||
github.com/fatih/color v1.15.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f // indirect
|
||||
)
|
||||
|
108
go.sum
108
go.sum
@@ -1,8 +1,7 @@
|
||||
github.com/Arceliar/ironwood v0.0.0-20230521174855-fdfa6326d125 h1:l2elyrosw63mTqZzwR0Nv8vPZWZC/0Hvwl8Iuva5htM=
|
||||
github.com/Arceliar/ironwood v0.0.0-20230521174855-fdfa6326d125/go.mod h1:5x7fWW0mshe9WQ1lvSMmmHBYC3BeHH9gpwW5tz7cbfw=
|
||||
github.com/Arceliar/ironwood v0.0.0-20230805085300-86206813435f h1:Fz0zG7ZyQQqk+ROnmHuGrIZO250Lx/YHmp9o48XE+Vw=
|
||||
github.com/Arceliar/ironwood v0.0.0-20230805085300-86206813435f/go.mod h1:5x7fWW0mshe9WQ1lvSMmmHBYC3BeHH9gpwW5tz7cbfw=
|
||||
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM=
|
||||
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q=
|
||||
github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA=
|
||||
github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
|
||||
github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4=
|
||||
github.com/bits-and-blooms/bitset v1.3.1/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
|
||||
@@ -10,33 +9,56 @@ github.com/bits-and-blooms/bitset v1.5.0 h1:NpE8frKRLGHIcEzkR+gZhiioW1+WbYV6fKwD
|
||||
github.com/bits-and-blooms/bitset v1.5.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
|
||||
github.com/bits-and-blooms/bloom/v3 v3.3.1 h1:K2+A19bXT8gJR5mU7y+1yW6hsKfNCjcP2uNfLFKncjQ=
|
||||
github.com/bits-and-blooms/bloom/v3 v3.3.1/go.mod h1:bhUUknWd5khVbTe4UgMCSiOOVJzr3tMoijSK3WwvW90=
|
||||
github.com/cheggaaa/pb/v3 v3.0.8 h1:bC8oemdChbke2FHIIGy9mn4DPJ2caZYQnfbRqwmdCoA=
|
||||
github.com/cheggaaa/pb/v3 v3.0.8/go.mod h1:UICbiLec/XO6Hw6k+BHEtHeQFzzBH4i2/qk/ow1EJTA=
|
||||
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||
github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc=
|
||||
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||
github.com/gologme/log v1.2.0 h1:Ya5Ip/KD6FX7uH0S31QO87nCCSucKtF44TLbTtO7V4c=
|
||||
github.com/gologme/log v1.2.0/go.mod h1:gq31gQ8wEHkR+WekdWsqDuf8pXTUZA9BnnzTuPz1Y9U=
|
||||
github.com/cheggaaa/pb/v3 v3.1.4 h1:DN8j4TVVdKu3WxVwcRKu0sG00IIU6FewoABZzXbRQeo=
|
||||
github.com/cheggaaa/pb/v3 v3.1.4/go.mod h1:6wVjILNBaXMs8c21qRiaUM8BR82erfgau1DQ4iUXmSA=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/gologme/log v1.3.0 h1:l781G4dE+pbigClDSDzSaaYKtiueHCILUa/qSDsmHAo=
|
||||
github.com/gologme/log v1.3.0/go.mod h1:yKT+DvIPdDdDoPtqFrFxheooyVmoqi0BAsw+erN3wA4=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE=
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
github.com/hjson/hjson-go/v4 v4.3.0 h1:dyrzJdqqFGhHt+FSrs5n9s6b0fPM8oSJdWo+oS3YnJw=
|
||||
github.com/hjson/hjson-go/v4 v4.3.0/go.mod h1:KaYt3bTw3zhBjYqnXkYywcYctk0A2nxeEFTse3rH13E=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/kardianos/minwinsvc v1.0.2 h1:JmZKFJQrmTGa/WiW+vkJXKmfzdjabuEW4Tirj5lLdR0=
|
||||
github.com/kardianos/minwinsvc v1.0.2/go.mod h1:LUZNYhNmxujx2tR7FbdxqYJ9XDDoCd3MQcl1o//FWl4=
|
||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA=
|
||||
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
|
||||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
|
||||
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
|
||||
github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
|
||||
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/quic-go/qtls-go1-20 v0.3.4 h1:MfFAPULvst4yoMgY9QmtpYmfij/em7O8UUi+bNVm7Cg=
|
||||
github.com/quic-go/qtls-go1-20 v0.3.4/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
|
||||
github.com/quic-go/quic-go v0.39.0 h1:AgP40iThFMY0bj8jGxROhw3S0FMGa8ryqsmi9tBH3So=
|
||||
github.com/quic-go/quic-go v0.39.0/go.mod h1:T09QsDQWjLiQ74ZmacDfqZmhY/NLnw5BC40MANNNZ1Q=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg=
|
||||
github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=
|
||||
github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0=
|
||||
@@ -45,38 +67,46 @@ github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17
|
||||
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f h1:p4VB7kIXpOQvVn1ZaTIVp+3vuYAXFe3OJEvjbUYJLaA=
|
||||
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo=
|
||||
go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
|
||||
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
|
||||
golang.org/x/mobile v0.0.0-20221110043201-43a038452099 h1:aIu0lKmfdgtn2uTj7JI2oN4TUrQvgB+wzTPO23bCKt8=
|
||||
golang.org/x/mobile v0.0.0-20221110043201-43a038452099/go.mod h1:aAjjkJNdrh3PMckS4B10TGS2nag27cbKR1y2BpUxsiY=
|
||||
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o=
|
||||
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/mobile v0.0.0-20231006135142-2b44d11868fe h1:lrXv4yHeD9FA8PSJATWowP1QvexpyAPWmPia+Kbzql8=
|
||||
golang.org/x/mobile v0.0.0-20231006135142-2b44d11868fe/go.mod h1:BrnXpEObnFxpaT75Jo9hsCazwOWcp7nVIa8NNuH5cuA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
|
||||
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
|
||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-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-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/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=
|
||||
@@ -85,15 +115,23 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/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 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc=
|
||||
golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20211017052713-f87e87af0d9a h1:tTbyylK9/D3u/wEP26Vx7L700UpY48nhioJWZM1vhZw=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20211017052713-f87e87af0d9a/go.mod h1:id8Oh3eCCmpj9uVGWVjsUAl6UPX5ysMLzu6QxJU2UOU=
|
||||
golang.zx2c4.com/wireguard/windows v0.4.12 h1:CUmbdWKVNzTSsVb4yUAiEwL3KsabdJkEPdDjCHxBlhA=
|
||||
golang.zx2c4.com/wireguard/windows v0.4.12/go.mod h1:PW4y+d9oY83XU9rRwRwrJDwEMuhVjMxu2gfD1cfzS7w=
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20230223181233-21636207a675 h1:/J/RVnr7ng4fWPRH3xa4WtBJ1Jp+Auu4YNLmGiPv5QU=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20230223181233-21636207a675/go.mod h1:whfbyDBt09xhCYQWtO2+3UVjlaq6/9hDZrjg2ZE6SyA=
|
||||
golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE=
|
||||
golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI=
|
||||
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
|
@@ -3,13 +3,13 @@ package address
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ed25519"
|
||||
"math/rand"
|
||||
"crypto/rand"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAddress_Address_IsValid(t *testing.T) {
|
||||
var address Address
|
||||
rand.Read(address[:])
|
||||
_, _ = rand.Read(address[:])
|
||||
|
||||
address[0] = 0
|
||||
|
||||
@@ -32,7 +32,7 @@ func TestAddress_Address_IsValid(t *testing.T) {
|
||||
|
||||
func TestAddress_Subnet_IsValid(t *testing.T) {
|
||||
var subnet Subnet
|
||||
rand.Read(subnet[:])
|
||||
_, _ = rand.Read(subnet[:])
|
||||
|
||||
subnet[0] = 0
|
||||
|
||||
|
@@ -1,5 +1,10 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
type RemovePeerRequest struct {
|
||||
Uri string `json:"uri"`
|
||||
Sintf string `json:"interface,omitempty"`
|
||||
@@ -8,5 +13,9 @@ type RemovePeerRequest struct {
|
||||
type RemovePeerResponse struct{}
|
||||
|
||||
func (a *AdminSocket) removePeerHandler(req *RemovePeerRequest, res *RemovePeerResponse) error {
|
||||
return a.core.RemovePeer(req.Uri, req.Sintf)
|
||||
u, err := url.Parse(req.Uri)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to parse peering URI: %w", err)
|
||||
}
|
||||
return a.core.RemovePeer(u, req.Sintf)
|
||||
}
|
||||
|
@@ -40,22 +40,19 @@ import (
|
||||
// options that are necessary for an Yggdrasil node to run. You will need to
|
||||
// supply one of these structs to the Yggdrasil core when starting a node.
|
||||
type NodeConfig struct {
|
||||
PrivateKey KeyBytes `comment:"Your private key. DO NOT share this with anyone!"`
|
||||
PrivateKeyPath string `json:",omitempty"`
|
||||
Certificate *tls.Certificate `json:"-"`
|
||||
CertificatePath string `json:",omitempty"`
|
||||
RootCertificates []*x509.Certificate `json:"-"`
|
||||
RootCertificatePaths []string `json:",omitempty"`
|
||||
Peers []string `comment:"List of connection strings for outbound peer connections in URI format,\ne.g. tls://a.b.c.d:e or socks://a.b.c.d:e/f.g.h.i:j. These connections\nwill obey the operating system routing table, therefore you should\nuse this section when you may connect via different interfaces."`
|
||||
InterfacePeers map[string][]string `comment:"List of connection strings for outbound peer connections in URI format,\narranged by source interface, e.g. { \"eth0\": [ \"tls://a.b.c.d:e\" ] }.\nNote that SOCKS peerings will NOT be affected by this option and should\ngo in the \"Peers\" section instead."`
|
||||
Listen []string `comment:"Listen addresses for incoming connections. You will need to add\nlisteners in order to accept incoming peerings from non-local nodes.\nMulticast peer discovery will work regardless of any listeners set\nhere. Each listener should be specified in URI format as above, e.g.\ntls://0.0.0.0:0 or tls://[::]:0 to listen on all interfaces."`
|
||||
AdminListen string `comment:"Listen address for admin connections. Default is to listen for local\nconnections either on TCP/9001 or a UNIX socket depending on your\nplatform. Use this value for yggdrasilctl -endpoint=X. To disable\nthe admin socket, use the value \"none\" instead."`
|
||||
MulticastInterfaces []MulticastInterfaceConfig `comment:"Configuration for which interfaces multicast peer discovery should be\nenabled on. Each entry in the list should be a json object which may\ncontain Regex, Beacon, Listen, and Port. Regex is a regular expression\nwhich is matched against an interface name, and interfaces use the\nfirst configuration that they match gainst. Beacon configures whether\nor not the node should send link-local multicast beacons to advertise\ntheir presence, while listening for incoming connections on Port.\nListen controls whether or not the node listens for multicast beacons\nand opens outgoing connections."`
|
||||
AllowedPublicKeys []string `comment:"List of peer public keys to allow incoming peering connections\nfrom. If left empty/undefined then all connections will be allowed\nby default. This does not affect outgoing peerings, nor does it\naffect link-local peers discovered via multicast."`
|
||||
IfName string `comment:"Local network interface name for TUN adapter, or \"auto\" to select\nan interface automatically, or \"none\" to run without TUN."`
|
||||
IfMTU uint64 `comment:"Maximum Transmission Unit (MTU) size for your local TUN interface.\nDefault is the largest supported size for your platform. The lowest\npossible value is 1280."`
|
||||
NodeInfoPrivacy bool `comment:"By default, nodeinfo contains some defaults including the platform,\narchitecture and Yggdrasil version. These can help when surveying\nthe network and diagnosing network routing problems. Enabling\nnodeinfo privacy prevents this, so that only items specified in\n\"NodeInfo\" are sent back if specified."`
|
||||
NodeInfo map[string]interface{} `comment:"Optional node info. This must be a { \"key\": \"value\", ... } map\nor set as null. This is entirely optional but, if set, is visible\nto the whole network on request."`
|
||||
PrivateKey KeyBytes `comment:"Your private key. DO NOT share this with anyone!"`
|
||||
PrivateKeyPath string `json:",omitempty"`
|
||||
Certificate *tls.Certificate `json:"-"`
|
||||
Peers []string `comment:"List of connection strings for outbound peer connections in URI format,\ne.g. tls://a.b.c.d:e or socks://a.b.c.d:e/f.g.h.i:j. These connections\nwill obey the operating system routing table, therefore you should\nuse this section when you may connect via different interfaces."`
|
||||
InterfacePeers map[string][]string `comment:"List of connection strings for outbound peer connections in URI format,\narranged by source interface, e.g. { \"eth0\": [ \"tls://a.b.c.d:e\" ] }.\nNote that SOCKS peerings will NOT be affected by this option and should\ngo in the \"Peers\" section instead."`
|
||||
Listen []string `comment:"Listen addresses for incoming connections. You will need to add\nlisteners in order to accept incoming peerings from non-local nodes.\nMulticast peer discovery will work regardless of any listeners set\nhere. Each listener should be specified in URI format as above, e.g.\ntls://0.0.0.0:0 or tls://[::]:0 to listen on all interfaces."`
|
||||
AdminListen string `comment:"Listen address for admin connections. Default is to listen for local\nconnections either on TCP/9001 or a UNIX socket depending on your\nplatform. Use this value for yggdrasilctl -endpoint=X. To disable\nthe admin socket, use the value \"none\" instead."`
|
||||
MulticastInterfaces []MulticastInterfaceConfig `comment:"Configuration for which interfaces multicast peer discovery should be\nenabled on. Each entry in the list should be a json object which may\ncontain Regex, Beacon, Listen, and Port. Regex is a regular expression\nwhich is matched against an interface name, and interfaces use the\nfirst configuration that they match gainst. Beacon configures whether\nor not the node should send link-local multicast beacons to advertise\ntheir presence, while listening for incoming connections on Port.\nListen controls whether or not the node listens for multicast beacons\nand opens outgoing connections."`
|
||||
AllowedPublicKeys []string `comment:"List of peer public keys to allow incoming peering connections\nfrom. If left empty/undefined then all connections will be allowed\nby default. This does not affect outgoing peerings, nor does it\naffect link-local peers discovered via multicast."`
|
||||
IfName string `comment:"Local network interface name for TUN adapter, or \"auto\" to select\nan interface automatically, or \"none\" to run without TUN."`
|
||||
IfMTU uint64 `comment:"Maximum Transmission Unit (MTU) size for your local TUN interface.\nDefault is the largest supported size for your platform. The lowest\npossible value is 1280."`
|
||||
NodeInfoPrivacy bool `comment:"By default, nodeinfo contains some defaults including the platform,\narchitecture and Yggdrasil version. These can help when surveying\nthe network and diagnosing network routing problems. Enabling\nnodeinfo privacy prevents this, so that only items specified in\n\"NodeInfo\" are sent back if specified."`
|
||||
NodeInfo map[string]interface{} `comment:"Optional node info. This must be a { \"key\": \"value\", ... } map\nor set as null. This is entirely optional but, if set, is visible\nto the whole network on request."`
|
||||
}
|
||||
|
||||
type MulticastInterfaceConfig struct {
|
||||
@@ -64,6 +61,7 @@ type MulticastInterfaceConfig struct {
|
||||
Listen bool
|
||||
Port uint16
|
||||
Priority uint64 // really uint8, but gobind won't export it
|
||||
Password string
|
||||
}
|
||||
|
||||
// Generates default configuration and returns a pointer to the resulting
|
||||
@@ -138,53 +136,18 @@ func (cfg *NodeConfig) postprocessConfig() error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if cfg.CertificatePath != "" {
|
||||
if cfg.PrivateKeyPath == "" {
|
||||
return fmt.Errorf("CertificatePath requires PrivateKeyPath")
|
||||
}
|
||||
cfg.Certificate = nil
|
||||
f, err := os.ReadFile(cfg.CertificatePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cfg.UnmarshalPEMCertificate(f); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if cfg.Certificate == nil {
|
||||
switch {
|
||||
case cfg.Certificate == nil:
|
||||
// No self-signed certificate has been generated yet.
|
||||
fallthrough
|
||||
case !bytes.Equal(cfg.Certificate.PrivateKey.(ed25519.PrivateKey), cfg.PrivateKey):
|
||||
// A self-signed certificate was generated but the private
|
||||
// key has changed since then, possibly because a new config
|
||||
// was parsed.
|
||||
if err := cfg.GenerateSelfSignedCertificate(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
cfg.RootCertificates = cfg.RootCertificates[:0]
|
||||
for _, path := range cfg.RootCertificatePaths {
|
||||
f, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cfg.UnmarshalRootCertificate(f); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cfg *NodeConfig) UnmarshalRootCertificate(b []byte) error {
|
||||
p, _ := pem.Decode(b)
|
||||
if p == nil {
|
||||
return fmt.Errorf("failed to parse PEM file")
|
||||
}
|
||||
if p.Type != "CERTIFICATE" {
|
||||
return fmt.Errorf("unexpected PEM type %q", p.Type)
|
||||
}
|
||||
cert, err := x509.ParseCertificate(p.Bytes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load X.509 keypair: %w", err)
|
||||
}
|
||||
if !cert.IsCA {
|
||||
return fmt.Errorf("supplied root certificate is not a certificate authority")
|
||||
}
|
||||
cfg.RootCertificates = append(cfg.RootCertificates, cert)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -208,26 +171,6 @@ func (cfg *NodeConfig) GenerateSelfSignedCertificate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cfg *NodeConfig) GenerateCertificateSigningRequest() ([]byte, error) {
|
||||
template := &x509.CertificateRequest{
|
||||
Subject: pkix.Name{
|
||||
CommonName: hex.EncodeToString(cfg.PrivateKey),
|
||||
},
|
||||
SignatureAlgorithm: x509.PureEd25519,
|
||||
}
|
||||
|
||||
csrBytes, err := x509.CreateCertificateRequest(rand.Reader, template, ed25519.PrivateKey(cfg.PrivateKey))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pemBytes := bytes.NewBuffer(nil)
|
||||
if err := pem.Encode(pemBytes, &pem.Block{Type: "CERTIFICATE REQUEST", Bytes: csrBytes}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pemBytes.Bytes(), nil
|
||||
}
|
||||
|
||||
func (cfg *NodeConfig) MarshalPEMCertificate() ([]byte, error) {
|
||||
privateKey := ed25519.PrivateKey(cfg.PrivateKey)
|
||||
publicKey := privateKey.Public().(ed25519.PublicKey)
|
||||
@@ -256,15 +199,6 @@ func (cfg *NodeConfig) MarshalPEMCertificate() ([]byte, error) {
|
||||
return pem.EncodeToMemory(block), nil
|
||||
}
|
||||
|
||||
func (cfg *NodeConfig) UnmarshalPEMCertificate(b []byte) error {
|
||||
tlsCert, err := tls.LoadX509KeyPair(cfg.CertificatePath, cfg.PrivateKeyPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load X.509 keypair: %w", err)
|
||||
}
|
||||
cfg.Certificate = &tlsCert
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cfg *NodeConfig) NewPrivateKey() {
|
||||
_, spriv, err := ed25519.GenerateKey(nil)
|
||||
if err != nil {
|
||||
|
@@ -3,7 +3,6 @@ package core
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"sync/atomic"
|
||||
@@ -192,28 +191,8 @@ func (c *Core) AddPeer(u *url.URL, sintf string) error {
|
||||
|
||||
// RemovePeer removes a peer. The peer should be specified in URI format, see AddPeer.
|
||||
// The peer is not disconnected immediately.
|
||||
func (c *Core) RemovePeer(uri string, sourceInterface string) error {
|
||||
return fmt.Errorf("not implemented yet")
|
||||
/*
|
||||
var err error
|
||||
phony.Block(c, func() {
|
||||
peer := Peer{uri, sourceInterface}
|
||||
linkInfo, ok := c.config._peers[peer]
|
||||
if !ok {
|
||||
err = fmt.Errorf("peer not configured")
|
||||
return
|
||||
}
|
||||
if ok && linkInfo != nil {
|
||||
c.links.Act(nil, func() {
|
||||
if link := c.links._links[*linkInfo]; link != nil {
|
||||
_ = link.conn.Close()
|
||||
}
|
||||
})
|
||||
}
|
||||
delete(c.config._peers, peer)
|
||||
})
|
||||
return err
|
||||
*/
|
||||
func (c *Core) RemovePeer(u *url.URL, sintf string) error {
|
||||
return c.links.remove(u, sintf, linkTypePersistent)
|
||||
}
|
||||
|
||||
// CallPeer calls a peer once. This should be specified in the peer URI format,
|
||||
|
@@ -4,8 +4,6 @@ import (
|
||||
"context"
|
||||
"crypto/ed25519"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
@@ -39,8 +37,7 @@ type Core struct {
|
||||
log Logger
|
||||
addPeerTimer *time.Timer
|
||||
config struct {
|
||||
tls *tls.Config // immutable after startup
|
||||
roots *x509.CertPool // immutable after startup
|
||||
tls *tls.Config // immutable after startup
|
||||
//_peers map[Peer]*linkInfo // configurable after startup
|
||||
_listeners map[ListenAddress]struct{} // configurable after startup
|
||||
nodeinfo NodeInfo // immutable after startup
|
||||
@@ -106,13 +103,6 @@ func New(cert *tls.Certificate, logger Logger, opts ...SetupOption) (*Core, erro
|
||||
); err != nil {
|
||||
return nil, fmt.Errorf("error creating encryption: %w", err)
|
||||
}
|
||||
address, subnet := c.Address(), c.Subnet()
|
||||
c.log.Infof("Your public key is %s", hex.EncodeToString(c.public))
|
||||
c.log.Infof("Your IPv6 address is %s", address.String())
|
||||
c.log.Infof("Your IPv6 subnet is %s", subnet.String())
|
||||
if c.config.roots != nil {
|
||||
c.log.Println("Yggdrasil is running in TLS-only mode")
|
||||
}
|
||||
c.proto.init(c)
|
||||
if err := c.links.init(c); err != nil {
|
||||
return nil, fmt.Errorf("error initialising links: %w", err)
|
||||
@@ -145,7 +135,14 @@ func New(cert *tls.Certificate, logger Logger, opts ...SetupOption) (*Core, erro
|
||||
}
|
||||
|
||||
func (c *Core) RetryPeersNow() {
|
||||
// TODO: figure out a way to retrigger peer connections.
|
||||
phony.Block(&c.links, func() {
|
||||
for _, l := range c.links._links {
|
||||
select {
|
||||
case l.kick <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Stop shuts down the Yggdrasil node.
|
||||
@@ -169,10 +166,6 @@ func (c *Core) _close() error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Core) isTLSOnly() bool {
|
||||
return c.config.roots != nil
|
||||
}
|
||||
|
||||
func (c *Core) MTU() uint64 {
|
||||
const sessionTypeOverhead = 1
|
||||
MTU := c.PacketConn.MTU() - sessionTypeOverhead
|
||||
|
@@ -2,7 +2,7 @@ package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math/rand"
|
||||
"crypto/rand"
|
||||
"net/url"
|
||||
"os"
|
||||
"testing"
|
||||
@@ -146,7 +146,7 @@ func TestCore_Start_Transfer(t *testing.T) {
|
||||
|
||||
// Send
|
||||
msg := make([]byte, msgLen)
|
||||
rand.Read(msg[40:])
|
||||
_, _ = rand.Read(msg[40:])
|
||||
msg[0] = 0x60
|
||||
copy(msg[8:24], nodeB.Address())
|
||||
copy(msg[24:40], nodeA.Address())
|
||||
@@ -178,7 +178,7 @@ func BenchmarkCore_Start_Transfer(b *testing.B) {
|
||||
|
||||
// Send
|
||||
msg := make([]byte, msgLen)
|
||||
rand.Read(msg[40:])
|
||||
_, _ = rand.Read(msg[40:])
|
||||
msg[0] = 0x60
|
||||
copy(msg[8:24], nodeB.Address())
|
||||
copy(msg[24:40], nodeA.Address())
|
||||
|
@@ -1,6 +1,3 @@
|
||||
//go:build debug
|
||||
// +build debug
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
@@ -8,28 +5,13 @@ import (
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
"github.com/gologme/log"
|
||||
)
|
||||
|
||||
// Start the profiler in debug builds, if the required environment variable is set.
|
||||
// Start the profiler if the required environment variable is set.
|
||||
func init() {
|
||||
envVarName := "PPROFLISTEN"
|
||||
hostPort := os.Getenv(envVarName)
|
||||
switch {
|
||||
case hostPort == "":
|
||||
fmt.Fprintf(os.Stderr, "DEBUG: %s not set, profiler not started.\n", envVarName)
|
||||
default:
|
||||
if hostPort := os.Getenv(envVarName); hostPort != "" {
|
||||
fmt.Fprintf(os.Stderr, "DEBUG: Starting pprof on %s\n", hostPort)
|
||||
go func() { fmt.Println(http.ListenAndServe(hostPort, nil)) }()
|
||||
go fmt.Println(http.ListenAndServe(hostPort, nil))
|
||||
}
|
||||
}
|
||||
|
||||
// Starts the function profiler. This is only supported when built with
|
||||
// '-tags build'.
|
||||
func StartProfiler(log *log.Logger) error {
|
||||
runtime.SetBlockProfileRate(1)
|
||||
go func() { log.Println(http.ListenAndServe("localhost:6060", nil)) }()
|
||||
return nil
|
||||
}
|
||||
|
138
src/core/link.go
138
src/core/link.go
@@ -4,7 +4,6 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
@@ -18,6 +17,7 @@ import (
|
||||
|
||||
"github.com/Arceliar/phony"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||
"golang.org/x/crypto/blake2b"
|
||||
)
|
||||
|
||||
type linkType int
|
||||
@@ -35,12 +35,13 @@ type links struct {
|
||||
tls *linkTLS // TLS interface support
|
||||
unix *linkUNIX // UNIX interface support
|
||||
socks *linkSOCKS // SOCKS interface support
|
||||
quic *linkQUIC // QUIC interface support
|
||||
// _links can only be modified safely from within the links actor
|
||||
_links map[linkInfo]*link // *link is nil if connection in progress
|
||||
}
|
||||
|
||||
type linkProtocol interface {
|
||||
dial(url *url.URL, info linkInfo, options linkOptions) (net.Conn, error)
|
||||
dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error)
|
||||
listen(ctx context.Context, url *url.URL, sintf string) (net.Listener, error)
|
||||
}
|
||||
|
||||
@@ -52,9 +53,11 @@ type linkInfo struct {
|
||||
|
||||
// link tracks the state of a connection, either persistent or non-persistent
|
||||
type link struct {
|
||||
kick chan struct{} // Attempt to reconnect now, if backing off
|
||||
linkType linkType // Type of link, i.e. outbound/inbound, persistent/ephemeral
|
||||
linkProto string // Protocol carrier of link, e.g. TCP, AWDL
|
||||
ctx context.Context // Connection context
|
||||
cancel context.CancelFunc // Stop future redial attempts (when peer removed)
|
||||
kick chan struct{} // Attempt to reconnect now, if backing off
|
||||
linkType linkType // Type of link, i.e. outbound/inbound, persistent/ephemeral
|
||||
linkProto string // Protocol carrier of link, e.g. TCP, AWDL
|
||||
// The remaining fields can only be modified safely from within the links actor
|
||||
_conn *linkConn // Connected link, if any, nil if not connected
|
||||
_err error // Last error on the connection, if any
|
||||
@@ -65,6 +68,7 @@ type linkOptions struct {
|
||||
pinnedEd25519Keys map[keyArray]struct{}
|
||||
priority uint8
|
||||
tlsSNI string
|
||||
password []byte
|
||||
}
|
||||
|
||||
type Listener struct {
|
||||
@@ -90,6 +94,7 @@ func (l *links) init(c *Core) error {
|
||||
l.tls = l.newLinkTLS(l.tcp)
|
||||
l.unix = l.newLinkUNIX()
|
||||
l.socks = l.newLinkSOCKS()
|
||||
l.quic = l.newLinkQUIC()
|
||||
l._links = make(map[linkInfo]*link)
|
||||
|
||||
var listeners []ListenAddress
|
||||
@@ -126,8 +131,10 @@ type linkError string
|
||||
func (e linkError) Error() string { return string(e) }
|
||||
|
||||
const ErrLinkAlreadyConfigured = linkError("peer is already configured")
|
||||
const ErrLinkNotConfigured = linkError("peer is not configured")
|
||||
const ErrLinkPriorityInvalid = linkError("priority value is invalid")
|
||||
const ErrLinkPinnedKeyInvalid = linkError("pinned public key is invalid")
|
||||
const ErrLinkPasswordInvalid = linkError("password is invalid")
|
||||
const ErrLinkUnrecognisedSchema = linkError("link schema unknown")
|
||||
|
||||
func (l *links) add(u *url.URL, sintf string, linkType linkType) error {
|
||||
@@ -165,6 +172,13 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error {
|
||||
}
|
||||
options.priority = uint8(pi)
|
||||
}
|
||||
if p := u.Query().Get("password"); p != "" {
|
||||
if len(p) > blake2b.Size {
|
||||
retErr = ErrLinkPasswordInvalid
|
||||
return
|
||||
}
|
||||
options.password = []byte(p)
|
||||
}
|
||||
|
||||
// If we think we're already connected to this peer, load up
|
||||
// the existing peer state. Try to kick the peer if possible,
|
||||
@@ -188,6 +202,7 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error {
|
||||
linkProto: strings.ToUpper(u.Scheme),
|
||||
kick: make(chan struct{}),
|
||||
}
|
||||
state.ctx, state.cancel = context.WithCancel(l.core.ctx)
|
||||
|
||||
// Store the state of the link so that it can be queried later.
|
||||
l._links[info] = state
|
||||
@@ -207,12 +222,14 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error {
|
||||
backoff++
|
||||
duration := time.Second * time.Duration(math.Exp2(float64(backoff)))
|
||||
select {
|
||||
case <-time.After(duration):
|
||||
return true
|
||||
case <-state.kick:
|
||||
return true
|
||||
case <-state.ctx.Done():
|
||||
return false
|
||||
case <-l.core.ctx.Done():
|
||||
return false
|
||||
case <-time.After(duration):
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,19 +238,25 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error {
|
||||
// then the loop will run endlessly, using backoffs as needed.
|
||||
// Otherwise the loop will end, cleaning up the link entry.
|
||||
go func() {
|
||||
defer func() {
|
||||
phony.Block(l, func() {
|
||||
if l._links[info] == state {
|
||||
delete(l._links, info)
|
||||
}
|
||||
})
|
||||
}()
|
||||
defer phony.Block(l, func() {
|
||||
if l._links[info] == state {
|
||||
delete(l._links, info)
|
||||
}
|
||||
})
|
||||
|
||||
// This loop will run each and every time we want to attempt
|
||||
// a connection to this peer.
|
||||
// TODO get rid of this loop, this is *exactly* what time.AfterFunc is for, we should just send a signal to the links actor to kick off a goroutine as needed
|
||||
for {
|
||||
conn, err := l.connect(u, info, options)
|
||||
select {
|
||||
case <-state.ctx.Done():
|
||||
// The peering context has been cancelled, so don't try
|
||||
// to dial again.
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
conn, err := l.connect(state.ctx, u, info, options)
|
||||
if err != nil {
|
||||
if linkType == linkTypePersistent {
|
||||
// If the link is a persistent configured peering,
|
||||
@@ -250,15 +273,13 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error {
|
||||
// the next connection.
|
||||
if backoffNow() {
|
||||
continue
|
||||
} else {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// Ephemeral and incoming connections don't remain
|
||||
// after a connection failure, so exit out of the
|
||||
// loop and clean up the link entry.
|
||||
break
|
||||
return
|
||||
}
|
||||
// Ephemeral and incoming connections don't remain
|
||||
// after a connection failure, so exit out of the
|
||||
// loop and clean up the link entry.
|
||||
break
|
||||
}
|
||||
|
||||
// The linkConn wrapper allows us to track the number of
|
||||
@@ -307,11 +328,8 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error {
|
||||
if linkType == linkTypePersistent {
|
||||
if backoffNow() {
|
||||
continue
|
||||
} else {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
break
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
@@ -319,6 +337,33 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error {
|
||||
return retErr
|
||||
}
|
||||
|
||||
func (l *links) remove(u *url.URL, sintf string, linkType linkType) error {
|
||||
var retErr error
|
||||
phony.Block(l, func() {
|
||||
// Generate the link info and see whether we think we already
|
||||
// have an open peering to this peer.
|
||||
lu := urlForLinkInfo(*u)
|
||||
info := linkInfo{
|
||||
uri: lu.String(),
|
||||
sintf: sintf,
|
||||
}
|
||||
|
||||
// If this peer is already configured then we will close the
|
||||
// connection and stop it from retrying.
|
||||
state, ok := l._links[info]
|
||||
if ok && state != nil {
|
||||
state.cancel()
|
||||
if conn := state._conn; conn != nil {
|
||||
retErr = conn.Close()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
retErr = ErrLinkNotConfigured
|
||||
})
|
||||
return retErr
|
||||
}
|
||||
|
||||
func (l *links) listen(u *url.URL, sintf string) (*Listener, error) {
|
||||
ctx, cancel := context.WithCancel(l.core.ctx)
|
||||
var protocol linkProtocol
|
||||
@@ -329,6 +374,8 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) {
|
||||
protocol = l.tls
|
||||
case "unix":
|
||||
protocol = l.unix
|
||||
case "quic":
|
||||
protocol = l.quic
|
||||
default:
|
||||
cancel()
|
||||
return nil, ErrLinkUnrecognisedSchema
|
||||
@@ -352,6 +399,12 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) {
|
||||
}
|
||||
options.priority = uint8(pi)
|
||||
}
|
||||
if p := u.Query().Get("password"); p != "" {
|
||||
if len(p) > blake2b.Size {
|
||||
return nil, ErrLinkPasswordInvalid
|
||||
}
|
||||
options.password = []byte(p)
|
||||
}
|
||||
|
||||
go func() {
|
||||
l.core.log.Printf("%s listener started on %s", strings.ToUpper(u.Scheme), listener.Addr())
|
||||
@@ -359,7 +412,7 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) {
|
||||
for {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
continue
|
||||
return
|
||||
}
|
||||
go func(conn net.Conn) {
|
||||
defer conn.Close()
|
||||
@@ -438,7 +491,7 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) {
|
||||
return li, nil
|
||||
}
|
||||
|
||||
func (l *links) connect(u *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
|
||||
func (l *links) connect(ctx context.Context, u *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
|
||||
var dialer linkProtocol
|
||||
switch strings.ToLower(u.Scheme) {
|
||||
case "tcp":
|
||||
@@ -465,16 +518,22 @@ func (l *links) connect(u *url.URL, info linkInfo, options linkOptions) (net.Con
|
||||
dialer = l.socks
|
||||
case "unix":
|
||||
dialer = l.unix
|
||||
case "quic":
|
||||
dialer = l.quic
|
||||
default:
|
||||
return nil, ErrLinkUnrecognisedSchema
|
||||
}
|
||||
return dialer.dial(u, info, options)
|
||||
return dialer.dial(ctx, u, info, options)
|
||||
}
|
||||
|
||||
func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn) error {
|
||||
meta := version_getBaseMetadata()
|
||||
meta.publicKey = l.core.public
|
||||
metaBytes := meta.encode()
|
||||
meta.priority = options.priority
|
||||
metaBytes, err := meta.encode(l.core.secret, options.password)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate handshake: %w", err)
|
||||
}
|
||||
if err := conn.SetDeadline(time.Now().Add(time.Second * 6)); err != nil {
|
||||
return fmt.Errorf("failed to set handshake deadline: %w", err)
|
||||
}
|
||||
@@ -485,16 +544,10 @@ func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn) e
|
||||
case err == nil && n != len(metaBytes):
|
||||
return fmt.Errorf("incomplete handshake send")
|
||||
}
|
||||
if _, err = io.ReadFull(conn, metaBytes); err != nil {
|
||||
return fmt.Errorf("read handshake: %w", err)
|
||||
}
|
||||
if err = conn.SetDeadline(time.Time{}); err != nil {
|
||||
return fmt.Errorf("failed to clear handshake deadline: %w", err)
|
||||
}
|
||||
meta = version_metadata{}
|
||||
base := version_getBaseMetadata()
|
||||
if !meta.decode(metaBytes) {
|
||||
return errors.New("failed to decode metadata")
|
||||
if !meta.decode(conn, options.password) {
|
||||
return conn.Close()
|
||||
}
|
||||
if !meta.check() {
|
||||
return fmt.Errorf("remote node incompatible version (local %s, remote %s)",
|
||||
@@ -502,6 +555,9 @@ func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn) e
|
||||
fmt.Sprintf("%d.%d", meta.majorVer, meta.minorVer),
|
||||
)
|
||||
}
|
||||
if err = conn.SetDeadline(time.Time{}); err != nil {
|
||||
return fmt.Errorf("failed to clear handshake deadline: %w", err)
|
||||
}
|
||||
// Check if the remote side matches the keys we expected. This is a bit of a weak
|
||||
// check - in future versions we really should check a signature or something like that.
|
||||
if pinned := options.pinnedEd25519Keys; len(pinned) > 0 {
|
||||
@@ -534,10 +590,14 @@ func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn) e
|
||||
remoteAddr := net.IP(address.AddrForKey(meta.publicKey)[:]).String()
|
||||
remoteStr := fmt.Sprintf("%s@%s", remoteAddr, conn.RemoteAddr())
|
||||
localStr := conn.LocalAddr()
|
||||
priority := options.priority
|
||||
if meta.priority > priority {
|
||||
priority = meta.priority
|
||||
}
|
||||
l.core.log.Infof("Connected %s: %s, source %s",
|
||||
dir, remoteStr, localStr)
|
||||
|
||||
err = l.core.HandleConn(meta.publicKey, conn, options.priority)
|
||||
err = l.core.HandleConn(meta.publicKey, conn, priority)
|
||||
switch err {
|
||||
case io.EOF, net.ErrClosed, nil:
|
||||
l.core.log.Infof("Disconnected %s: %s, source %s",
|
||||
|
96
src/core/link_quic.go
Normal file
96
src/core/link_quic.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/Arceliar/phony"
|
||||
"github.com/quic-go/quic-go"
|
||||
)
|
||||
|
||||
type linkQUIC struct {
|
||||
phony.Inbox
|
||||
*links
|
||||
tlsconfig *tls.Config
|
||||
quicconfig *quic.Config
|
||||
}
|
||||
|
||||
type linkQUICStream struct {
|
||||
quic.Connection
|
||||
quic.Stream
|
||||
}
|
||||
|
||||
type linkQUICListener struct {
|
||||
*quic.EarlyListener
|
||||
ch <-chan *linkQUICStream
|
||||
}
|
||||
|
||||
func (l *linkQUICListener) Accept() (net.Conn, error) {
|
||||
qs := <-l.ch
|
||||
if qs == nil {
|
||||
return nil, context.Canceled
|
||||
}
|
||||
return qs, nil
|
||||
}
|
||||
|
||||
func (l *links) newLinkQUIC() *linkQUIC {
|
||||
lt := &linkQUIC{
|
||||
links: l,
|
||||
tlsconfig: l.core.config.tls.Clone(),
|
||||
quicconfig: &quic.Config{
|
||||
MaxIdleTimeout: time.Minute,
|
||||
KeepAlivePeriod: time.Second * 20,
|
||||
TokenStore: quic.NewLRUTokenStore(255, 255),
|
||||
},
|
||||
}
|
||||
return lt
|
||||
}
|
||||
|
||||
func (l *linkQUIC) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
|
||||
qc, err := quic.DialAddrEarly(ctx, url.Host, l.tlsconfig, l.quicconfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
qs, err := qc.OpenStream()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &linkQUICStream{
|
||||
Connection: qc,
|
||||
Stream: qs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (l *linkQUIC) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) {
|
||||
ql, err := quic.ListenAddrEarly(url.Host, l.tlsconfig, l.quicconfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ch := make(chan *linkQUICStream)
|
||||
lql := &linkQUICListener{
|
||||
EarlyListener: ql,
|
||||
ch: ch,
|
||||
}
|
||||
go func() {
|
||||
for {
|
||||
qc, err := ql.Accept(ctx)
|
||||
if err != nil {
|
||||
ql.Close()
|
||||
return
|
||||
}
|
||||
qs, err := qc.AcceptStream(ctx)
|
||||
if err != nil {
|
||||
ql.Close()
|
||||
return
|
||||
}
|
||||
ch <- &linkQUICStream{
|
||||
Connection: qc,
|
||||
Stream: qs,
|
||||
}
|
||||
}
|
||||
}()
|
||||
return lql, nil
|
||||
}
|
@@ -21,7 +21,7 @@ func (l *links) newLinkSOCKS() *linkSOCKS {
|
||||
return lt
|
||||
}
|
||||
|
||||
func (l *linkSOCKS) dial(url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
|
||||
func (l *linkSOCKS) dial(_ context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
|
||||
var proxyAuth *proxy.Auth
|
||||
if url.User != nil && url.User.Username() != "" {
|
||||
proxyAuth = &proxy.Auth{
|
||||
|
@@ -68,10 +68,7 @@ func (l *linkTCP) dialersFor(url *url.URL, info linkInfo) ([]*tcpDialer, error)
|
||||
return dialers, nil
|
||||
}
|
||||
|
||||
func (l *linkTCP) dial(url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
|
||||
if l.core.isTLSOnly() {
|
||||
return nil, fmt.Errorf("TCP peer prohibited in TLS-only mode")
|
||||
}
|
||||
func (l *linkTCP) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
|
||||
dialers, err := l.dialersFor(url, info)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -81,7 +78,7 @@ func (l *linkTCP) dial(url *url.URL, info linkInfo, options linkOptions) (net.Co
|
||||
}
|
||||
for _, d := range dialers {
|
||||
var conn net.Conn
|
||||
conn, err = d.dialer.DialContext(l.core.ctx, "tcp", d.addr.String())
|
||||
conn, err = d.dialer.DialContext(ctx, "tcp", d.addr.String())
|
||||
if err != nil {
|
||||
l.core.log.Warnf("Failed to connect to %s: %s", d.addr, err)
|
||||
continue
|
||||
@@ -92,9 +89,6 @@ func (l *linkTCP) dial(url *url.URL, info linkInfo, options linkOptions) (net.Co
|
||||
}
|
||||
|
||||
func (l *linkTCP) listen(ctx context.Context, url *url.URL, sintf string) (net.Listener, error) {
|
||||
if l.core.isTLSOnly() {
|
||||
return nil, fmt.Errorf("TCP listener prohibited in TLS-only mode")
|
||||
}
|
||||
hostport := url.Host
|
||||
if sintf != "" {
|
||||
if host, port, err := net.SplitHostPort(hostport); err == nil {
|
||||
|
@@ -12,22 +12,6 @@ import (
|
||||
// WARNING: This context is used both by net.Dialer and net.Listen in tcp.go
|
||||
|
||||
func (t *linkTCP) tcpContext(network, address string, c syscall.RawConn) error {
|
||||
var control error
|
||||
var bbr error
|
||||
|
||||
control = c.Control(func(fd uintptr) {
|
||||
bbr = unix.SetsockoptString(int(fd), unix.IPPROTO_TCP, unix.TCP_CONGESTION, "bbr")
|
||||
})
|
||||
|
||||
// Log any errors
|
||||
if bbr != nil {
|
||||
t.links.core.log.Debugln("Failed to set tcp_congestion_control to bbr for socket, SetsockoptString error:", bbr)
|
||||
}
|
||||
if control != nil {
|
||||
t.links.core.log.Debugln("Failed to set tcp_congestion_control to bbr for socket, Control error:", control)
|
||||
}
|
||||
|
||||
// Return nil because errors here are not considered fatal for the connection, it just means congestion control is suboptimal
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@@ -33,7 +33,7 @@ func (l *links) newLinkTLS(tcp *linkTCP) *linkTLS {
|
||||
return lt
|
||||
}
|
||||
|
||||
func (l *linkTLS) dial(url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
|
||||
func (l *linkTLS) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
|
||||
dialers, err := l.tcp.dialersFor(url, info)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -49,7 +49,7 @@ func (l *linkTLS) dial(url *url.URL, info linkInfo, options linkOptions) (net.Co
|
||||
Config: tlsconfig,
|
||||
}
|
||||
var conn net.Conn
|
||||
conn, err = tlsdialer.DialContext(l.core.ctx, "tcp", d.addr.String())
|
||||
conn, err = tlsdialer.DialContext(ctx, "tcp", d.addr.String())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
@@ -32,12 +32,12 @@ func (l *links) newLinkUNIX() *linkUNIX {
|
||||
return lt
|
||||
}
|
||||
|
||||
func (l *linkUNIX) dial(url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
|
||||
func (l *linkUNIX) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
|
||||
addr, err := net.ResolveUnixAddr("unix", url.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return l.dialer.DialContext(l.core.ctx, "unix", addr.String())
|
||||
return l.dialer.DialContext(ctx, "unix", addr.String())
|
||||
}
|
||||
|
||||
func (l *linkUNIX) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) {
|
||||
|
@@ -2,19 +2,12 @@ package core
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
func (c *Core) _applyOption(opt SetupOption) (err error) {
|
||||
switch v := opt.(type) {
|
||||
case RootCertificate:
|
||||
cert := x509.Certificate(v)
|
||||
if c.config.roots == nil {
|
||||
c.config.roots = x509.NewCertPool()
|
||||
}
|
||||
c.config.roots.AddCert(&cert)
|
||||
case Peer:
|
||||
u, err := url.Parse(v.URI)
|
||||
if err != nil {
|
||||
@@ -39,7 +32,6 @@ type SetupOption interface {
|
||||
isSetupOption()
|
||||
}
|
||||
|
||||
type RootCertificate x509.Certificate
|
||||
type ListenAddress string
|
||||
type Peer struct {
|
||||
URI string
|
||||
@@ -49,7 +41,6 @@ type NodeInfo map[string]interface{}
|
||||
type NodeInfoPrivacy bool
|
||||
type AllowedPublicKey ed25519.PublicKey
|
||||
|
||||
func (a RootCertificate) isSetupOption() {}
|
||||
func (a ListenAddress) isSetupOption() {}
|
||||
func (a Peer) isSetupOption() {}
|
||||
func (a NodeInfo) isSetupOption() {}
|
||||
|
@@ -3,13 +3,12 @@ package core
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func (c *Core) generateTLSConfig(cert *tls.Certificate) (*tls.Config, error) {
|
||||
config := &tls.Config{
|
||||
Certificates: []tls.Certificate{*cert},
|
||||
ClientAuth: tls.RequireAnyClientCert,
|
||||
ClientAuth: tls.NoClientCert,
|
||||
GetClientCertificate: func(cri *tls.CertificateRequestInfo) (*tls.Certificate, error) {
|
||||
return cert, nil
|
||||
},
|
||||
@@ -21,43 +20,10 @@ func (c *Core) generateTLSConfig(cert *tls.Certificate) (*tls.Config, error) {
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func (c *Core) verifyTLSCertificate(rawCerts [][]byte, _ [][]*x509.Certificate) error {
|
||||
if c.config.roots == nil {
|
||||
// If there's no certificate pool configured then we will
|
||||
// accept all TLS certificates.
|
||||
return nil
|
||||
}
|
||||
if len(rawCerts) == 0 {
|
||||
return fmt.Errorf("expected at least one certificate")
|
||||
}
|
||||
|
||||
opts := x509.VerifyOptions{
|
||||
Roots: c.config.roots,
|
||||
}
|
||||
|
||||
for i, rawCert := range rawCerts {
|
||||
if i == 0 {
|
||||
// The first certificate is the leaf certificate. All other
|
||||
// certificates in the list are intermediates, so add them
|
||||
// into the VerifyOptions.
|
||||
continue
|
||||
}
|
||||
cert, err := x509.ParseCertificate(rawCert)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse intermediate certificate: %w", err)
|
||||
}
|
||||
opts.Intermediates.AddCert(cert)
|
||||
}
|
||||
|
||||
cert, err := x509.ParseCertificate(rawCerts[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse leaf certificate: %w", err)
|
||||
}
|
||||
|
||||
_, err = cert.Verify(opts)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Core) verifyTLSConnection(cs tls.ConnectionState) error {
|
||||
func (c *Core) verifyTLSCertificate(_ [][]byte, _ [][]*x509.Certificate) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Core) verifyTLSConnection(_ tls.ConnectionState) error {
|
||||
return nil
|
||||
}
|
||||
|
@@ -8,6 +8,10 @@ import (
|
||||
"bytes"
|
||||
"crypto/ed25519"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"golang.org/x/crypto/blake2b"
|
||||
)
|
||||
|
||||
// This is the version-specific metadata exchanged at the start of a connection.
|
||||
@@ -25,6 +29,8 @@ const (
|
||||
ProtocolVersionMinor uint16 = 5
|
||||
)
|
||||
|
||||
// Once a major/minor version is released, it is not safe to change any of these
|
||||
// (including their ordering), it is only safe to add new ones.
|
||||
const (
|
||||
metaVersionMajor uint16 = iota // uint16
|
||||
metaVersionMinor // uint16
|
||||
@@ -41,9 +47,10 @@ func version_getBaseMetadata() version_metadata {
|
||||
}
|
||||
|
||||
// Encodes version metadata into its wire format.
|
||||
func (m *version_metadata) encode() []byte {
|
||||
func (m *version_metadata) encode(privateKey ed25519.PrivateKey, password []byte) ([]byte, error) {
|
||||
bs := make([]byte, 0, 64)
|
||||
bs = append(bs, 'm', 'e', 't', 'a')
|
||||
bs = append(bs, 0, 0) // Remaining message length
|
||||
|
||||
bs = binary.BigEndian.AppendUint16(bs, metaVersionMajor)
|
||||
bs = binary.BigEndian.AppendUint16(bs, 2)
|
||||
@@ -61,16 +68,46 @@ func (m *version_metadata) encode() []byte {
|
||||
bs = binary.BigEndian.AppendUint16(bs, 1)
|
||||
bs = append(bs, m.priority)
|
||||
|
||||
return bs
|
||||
hasher, err := blake2b.New512(password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
n, err := hasher.Write(m.publicKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if n != ed25519.PublicKeySize {
|
||||
return nil, fmt.Errorf("hash writer only wrote %d bytes", n)
|
||||
}
|
||||
hash := hasher.Sum(nil)
|
||||
bs = append(bs, ed25519.Sign(privateKey, hash)...)
|
||||
|
||||
binary.BigEndian.PutUint16(bs[4:6], uint16(len(bs)-6))
|
||||
return bs, nil
|
||||
}
|
||||
|
||||
// Decodes version metadata from its wire format into the struct.
|
||||
func (m *version_metadata) decode(bs []byte) bool {
|
||||
meta := [4]byte{'m', 'e', 't', 'a'}
|
||||
if !bytes.Equal(bs[:4], meta[:]) {
|
||||
func (m *version_metadata) decode(r io.Reader, password []byte) bool {
|
||||
bh := [6]byte{}
|
||||
if _, err := io.ReadFull(r, bh[:]); err != nil {
|
||||
return false
|
||||
}
|
||||
for bs = bs[4:]; len(bs) >= 4; {
|
||||
meta := [4]byte{'m', 'e', 't', 'a'}
|
||||
if !bytes.Equal(bh[:4], meta[:]) {
|
||||
return false
|
||||
}
|
||||
bs := make([]byte, binary.BigEndian.Uint16(bh[4:6]))
|
||||
if _, err := io.ReadFull(r, bs); err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(bs) < ed25519.SignatureSize {
|
||||
return false
|
||||
}
|
||||
sig := bs[len(bs)-ed25519.SignatureSize:]
|
||||
bs = bs[:len(bs)-ed25519.SignatureSize]
|
||||
|
||||
for len(bs) >= 4 {
|
||||
op := binary.BigEndian.Uint16(bs[:2])
|
||||
oplen := binary.BigEndian.Uint16(bs[2:4])
|
||||
if bs = bs[4:]; len(bs) < int(oplen) {
|
||||
@@ -92,7 +129,17 @@ func (m *version_metadata) decode(bs []byte) bool {
|
||||
}
|
||||
bs = bs[oplen:]
|
||||
}
|
||||
return true
|
||||
|
||||
hasher, err := blake2b.New512(password)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
n, err := hasher.Write(m.publicKey)
|
||||
if err != nil || n != ed25519.PublicKeySize {
|
||||
return false
|
||||
}
|
||||
hash := hasher.Sum(nil)
|
||||
return ed25519.Verify(m.publicKey, hash, sig)
|
||||
}
|
||||
|
||||
// Checks that the "meta" bytes and the version numbers are the expected values.
|
||||
|
@@ -1,34 +1,78 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ed25519"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestVersionRoundtrip(t *testing.T) {
|
||||
for _, test := range []*version_metadata{
|
||||
{majorVer: 1},
|
||||
{majorVer: 256},
|
||||
{majorVer: 2, minorVer: 4},
|
||||
{majorVer: 2, minorVer: 257},
|
||||
{majorVer: 258, minorVer: 259},
|
||||
{majorVer: 3, minorVer: 5, priority: 6},
|
||||
{majorVer: 260, minorVer: 261, priority: 7},
|
||||
func TestVersionPasswordAuth(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
password1 []byte // The password on node 1
|
||||
password2 []byte // The password on node 2
|
||||
allowed bool // Should the connection have been allowed?
|
||||
}{
|
||||
{nil, nil, true}, // Allow: No passwords (both nil)
|
||||
{nil, []byte(""), true}, // Allow: No passwords (mixed nil and empty string)
|
||||
{nil, []byte("foo"), false}, // Reject: One node has a password, the other doesn't
|
||||
{[]byte("foo"), []byte(""), false}, // Reject: One node has a password, the other doesn't
|
||||
{[]byte("foo"), []byte("foo"), true}, // Allow: Same password
|
||||
{[]byte("foo"), []byte("bar"), false}, // Reject: Different passwords
|
||||
} {
|
||||
// Generate a random public key for each time, since it is
|
||||
// a required field.
|
||||
test.publicKey = make(ed25519.PublicKey, ed25519.PublicKeySize)
|
||||
rand.Read(test.publicKey)
|
||||
|
||||
encoded := test.encode()
|
||||
decoded := &version_metadata{}
|
||||
if !decoded.decode(encoded) {
|
||||
t.Fatalf("failed to decode")
|
||||
pk1, sk1, err := ed25519.GenerateKey(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Node 1 failed to generate key: %s", err)
|
||||
}
|
||||
if !reflect.DeepEqual(test, decoded) {
|
||||
t.Fatalf("round-trip failed\nwant: %+v\n got: %+v", test, decoded)
|
||||
|
||||
metadata1 := &version_metadata{
|
||||
publicKey: pk1,
|
||||
}
|
||||
encoded, err := metadata1.encode(sk1, tt.password1)
|
||||
if err != nil {
|
||||
t.Fatalf("Node 1 failed to encode metadata: %s", err)
|
||||
}
|
||||
|
||||
var decoded version_metadata
|
||||
if allowed := decoded.decode(bytes.NewBuffer(encoded), tt.password2); allowed != tt.allowed {
|
||||
t.Fatalf("Permutation %q -> %q should have been %v but was %v", tt.password1, tt.password2, tt.allowed, allowed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestVersionRoundtrip(t *testing.T) {
|
||||
for _, password := range [][]byte{
|
||||
nil, []byte(""), []byte("foo"),
|
||||
} {
|
||||
for _, test := range []*version_metadata{
|
||||
{majorVer: 1},
|
||||
{majorVer: 256},
|
||||
{majorVer: 2, minorVer: 4},
|
||||
{majorVer: 2, minorVer: 257},
|
||||
{majorVer: 258, minorVer: 259},
|
||||
{majorVer: 3, minorVer: 5, priority: 6},
|
||||
{majorVer: 260, minorVer: 261, priority: 7},
|
||||
} {
|
||||
// Generate a random public key for each time, since it is
|
||||
// a required field.
|
||||
pk, sk, err := ed25519.GenerateKey(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
test.publicKey = pk
|
||||
meta, err := test.encode(sk, password)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
encoded := bytes.NewBuffer(meta)
|
||||
decoded := &version_metadata{}
|
||||
if !decoded.decode(encoded, password) {
|
||||
t.Fatalf("failed to decode")
|
||||
}
|
||||
if !reflect.DeepEqual(test, decoded) {
|
||||
t.Fatalf("round-trip failed\nwant: %+v\n got: %+v", test, decoded)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -7,22 +7,33 @@ import (
|
||||
)
|
||||
|
||||
type multicastAdvertisement struct {
|
||||
PublicKey ed25519.PublicKey
|
||||
Port uint16
|
||||
MajorVersion uint16
|
||||
MinorVersion uint16
|
||||
PublicKey ed25519.PublicKey
|
||||
Port uint16
|
||||
Hash []byte
|
||||
}
|
||||
|
||||
func (m *multicastAdvertisement) MarshalBinary() ([]byte, error) {
|
||||
b := make([]byte, 0, ed25519.PublicKeySize+2)
|
||||
b := make([]byte, 0, ed25519.PublicKeySize+8+len(m.Hash))
|
||||
b = binary.BigEndian.AppendUint16(b, m.MajorVersion)
|
||||
b = binary.BigEndian.AppendUint16(b, m.MinorVersion)
|
||||
b = append(b, m.PublicKey...)
|
||||
b = binary.BigEndian.AppendUint16(b, m.Port)
|
||||
b = binary.BigEndian.AppendUint16(b, uint16(len(m.Hash)))
|
||||
b = append(b, m.Hash...)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (m *multicastAdvertisement) UnmarshalBinary(b []byte) error {
|
||||
if len(b) < ed25519.PublicKeySize+2 {
|
||||
if len(b) < ed25519.PublicKeySize+8 {
|
||||
return fmt.Errorf("invalid multicast beacon")
|
||||
}
|
||||
m.PublicKey = b[:ed25519.PublicKeySize]
|
||||
m.Port = binary.BigEndian.Uint16(b[ed25519.PublicKeySize:])
|
||||
m.MajorVersion = binary.BigEndian.Uint16(b[0:2])
|
||||
m.MinorVersion = binary.BigEndian.Uint16(b[2:4])
|
||||
m.PublicKey = append(m.PublicKey[:0], b[4:4+ed25519.PublicKeySize]...)
|
||||
m.Port = binary.BigEndian.Uint16(b[4+ed25519.PublicKeySize : 6+ed25519.PublicKeySize])
|
||||
dl := binary.BigEndian.Uint16(b[6+ed25519.PublicKeySize : 8+ed25519.PublicKeySize])
|
||||
m.Hash = append(m.Hash[:0], b[8+ed25519.PublicKeySize:8+ed25519.PublicKeySize+dl]...)
|
||||
return nil
|
||||
}
|
||||
|
38
src/multicast/advertisement_test.go
Normal file
38
src/multicast/advertisement_test.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package multicast
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMulticastAdvertisementRoundTrip(t *testing.T) {
|
||||
pk, sk, err := ed25519.GenerateKey(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
orig := multicastAdvertisement{
|
||||
MajorVersion: 1,
|
||||
MinorVersion: 2,
|
||||
PublicKey: pk,
|
||||
Port: 3,
|
||||
Hash: sk, // any bytes will do
|
||||
}
|
||||
|
||||
ob, err := orig.MarshalBinary()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var new multicastAdvertisement
|
||||
if err := new.UnmarshalBinary(ob); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(orig, new) {
|
||||
t.Logf("original: %+v", orig)
|
||||
t.Logf("new: %+v", new)
|
||||
t.Fatalf("differences found after round-trip")
|
||||
}
|
||||
}
|
@@ -1,7 +1,9 @@
|
||||
package multicast
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/ed25519"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
@@ -13,6 +15,7 @@ import (
|
||||
"github.com/gologme/log"
|
||||
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/core"
|
||||
"golang.org/x/crypto/blake2b"
|
||||
"golang.org/x/net/ipv6"
|
||||
)
|
||||
|
||||
@@ -42,6 +45,8 @@ type interfaceInfo struct {
|
||||
listen bool
|
||||
port uint16
|
||||
priority uint8
|
||||
password []byte
|
||||
hash []byte
|
||||
}
|
||||
|
||||
type listenerInfo struct {
|
||||
@@ -175,6 +180,7 @@ func (m *Multicast) _getAllowedInterfaces() map[string]*interfaceInfo {
|
||||
return nil
|
||||
}
|
||||
// Work out which interfaces to announce on
|
||||
pk := m.core.PublicKey()
|
||||
for _, iface := range allifaces {
|
||||
switch {
|
||||
case iface.Flags&net.FlagUp == 0:
|
||||
@@ -193,12 +199,23 @@ func (m *Multicast) _getAllowedInterfaces() map[string]*interfaceInfo {
|
||||
if !ifcfg.Regex.MatchString(iface.Name) {
|
||||
continue
|
||||
}
|
||||
hasher, err := blake2b.New512([]byte(ifcfg.Password))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if n, err := hasher.Write(pk); err != nil {
|
||||
continue
|
||||
} else if n != ed25519.PublicKeySize {
|
||||
continue
|
||||
}
|
||||
interfaces[iface.Name] = &interfaceInfo{
|
||||
iface: iface,
|
||||
beacon: ifcfg.Beacon,
|
||||
listen: ifcfg.Listen,
|
||||
port: ifcfg.Port,
|
||||
priority: ifcfg.Priority,
|
||||
password: []byte(ifcfg.Password),
|
||||
hash: hasher.Sum(nil),
|
||||
}
|
||||
break
|
||||
}
|
||||
@@ -295,10 +312,13 @@ func (m *Multicast) _announce() {
|
||||
var linfo *listenerInfo
|
||||
if _, ok := m._listeners[iface.Name]; !ok {
|
||||
// No listener was found - let's create one
|
||||
urlString := fmt.Sprintf("tls://[%s]:%d", addrIP, info.port)
|
||||
u, err := url.Parse(urlString)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
v := &url.Values{}
|
||||
v.Add("priority", fmt.Sprintf("%d", info.priority))
|
||||
v.Add("password", string(info.password))
|
||||
u := &url.URL{
|
||||
Scheme: "tls",
|
||||
Host: net.JoinHostPort(addrIP.String(), fmt.Sprintf("%d", info.port)),
|
||||
RawQuery: v.Encode(),
|
||||
}
|
||||
if li, err := m.core.Listen(u, iface.Name); err == nil {
|
||||
m.log.Debugln("Started multicasting on", iface.Name)
|
||||
@@ -321,8 +341,11 @@ func (m *Multicast) _announce() {
|
||||
}
|
||||
addr := linfo.listener.Addr().(*net.TCPAddr)
|
||||
adv := multicastAdvertisement{
|
||||
PublicKey: m.core.PublicKey(),
|
||||
Port: uint16(addr.Port),
|
||||
MajorVersion: core.ProtocolVersionMajor,
|
||||
MinorVersion: core.ProtocolVersionMinor,
|
||||
PublicKey: m.core.PublicKey(),
|
||||
Port: uint16(addr.Port),
|
||||
Hash: info.hash,
|
||||
}
|
||||
msg, err := adv.MarshalBinary()
|
||||
if err != nil {
|
||||
@@ -350,6 +373,7 @@ func (m *Multicast) listen() {
|
||||
panic(err)
|
||||
}
|
||||
bs := make([]byte, 2048)
|
||||
hb := make([]byte, 0, blake2b.Size) // Reused to reduce hash allocations
|
||||
for {
|
||||
n, rcm, fromAddr, err := m.sock.ReadFrom(bs)
|
||||
if err != nil {
|
||||
@@ -373,7 +397,12 @@ func (m *Multicast) listen() {
|
||||
if err := adv.UnmarshalBinary(bs[:n]); err != nil {
|
||||
continue
|
||||
}
|
||||
if adv.PublicKey.Equal(m.core.PublicKey()) {
|
||||
switch {
|
||||
case adv.MajorVersion != core.ProtocolVersionMajor:
|
||||
continue
|
||||
case adv.MinorVersion != core.ProtocolVersionMinor:
|
||||
continue
|
||||
case adv.PublicKey.Equal(m.core.PublicKey()):
|
||||
continue
|
||||
}
|
||||
from := fromAddr.(*net.UDPAddr)
|
||||
@@ -383,9 +412,22 @@ func (m *Multicast) listen() {
|
||||
interfaces = m._interfaces
|
||||
})
|
||||
if info, ok := interfaces[from.Zone]; ok && info.listen {
|
||||
hasher, err := blake2b.New512(info.password)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if n, err := hasher.Write(adv.PublicKey); err != nil {
|
||||
continue
|
||||
} else if n != ed25519.PublicKeySize {
|
||||
continue
|
||||
}
|
||||
if !bytes.Equal(hasher.Sum(hb[:0]), adv.Hash) {
|
||||
continue
|
||||
}
|
||||
v := &url.Values{}
|
||||
v.Add("key", hex.EncodeToString(adv.PublicKey))
|
||||
v.Add("priority", fmt.Sprintf("%d", info.priority))
|
||||
v.Add("password", string(info.password))
|
||||
u := &url.URL{
|
||||
Scheme: "tls",
|
||||
Host: from.String(),
|
||||
|
@@ -21,6 +21,7 @@ type MulticastInterface struct {
|
||||
Listen bool
|
||||
Port uint16
|
||||
Priority uint8
|
||||
Password string
|
||||
}
|
||||
|
||||
type GroupAddress string
|
||||
|
@@ -6,6 +6,8 @@ func (m *TunAdapter) _applyOption(opt SetupOption) {
|
||||
m.config.name = v
|
||||
case InterfaceMTU:
|
||||
m.config.mtu = v
|
||||
case FileDescriptor:
|
||||
m.config.fd = int32(v)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +17,8 @@ type SetupOption interface {
|
||||
|
||||
type InterfaceName string
|
||||
type InterfaceMTU uint64
|
||||
type FileDescriptor int32
|
||||
|
||||
func (a InterfaceName) isSetupOption() {}
|
||||
func (a InterfaceMTU) isSetupOption() {}
|
||||
func (a InterfaceName) isSetupOption() {}
|
||||
func (a InterfaceMTU) isSetupOption() {}
|
||||
func (a FileDescriptor) isSetupOption() {}
|
||||
|
@@ -8,6 +8,7 @@ package tun
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
|
||||
"github.com/Arceliar/phony"
|
||||
@@ -16,27 +17,34 @@ import (
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/config"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/core"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/ipv6rwc"
|
||||
)
|
||||
|
||||
type MTU uint16
|
||||
|
||||
type ReadWriteCloser interface {
|
||||
io.ReadWriteCloser
|
||||
Address() address.Address
|
||||
Subnet() address.Subnet
|
||||
MaxMTU() uint64
|
||||
SetMTU(uint64)
|
||||
}
|
||||
|
||||
// TunAdapter represents a running TUN interface and extends the
|
||||
// yggdrasil.Adapter type. In order to use the TUN adapter with Yggdrasil, you
|
||||
// should pass this object to the yggdrasil.SetRouterAdapter() function before
|
||||
// calling yggdrasil.Start().
|
||||
type TunAdapter struct {
|
||||
rwc *ipv6rwc.ReadWriteCloser
|
||||
rwc ReadWriteCloser
|
||||
log core.Logger
|
||||
addr address.Address
|
||||
subnet address.Subnet
|
||||
mtu uint64
|
||||
iface tun.Device
|
||||
phony.Inbox // Currently only used for _handlePacket from the reader, TODO: all the stuff that currently needs a mutex below
|
||||
//mutex sync.RWMutex // Protects the below
|
||||
isOpen bool
|
||||
isEnabled bool // Used by the writer to drop sessionTraffic if not enabled
|
||||
config struct {
|
||||
isOpen bool
|
||||
isEnabled bool // Used by the writer to drop sessionTraffic if not enabled
|
||||
config struct {
|
||||
fd int32
|
||||
name InterfaceName
|
||||
mtu InterfaceMTU
|
||||
}
|
||||
@@ -90,7 +98,7 @@ func MaximumMTU() uint64 {
|
||||
|
||||
// Init initialises the TUN module. You must have acquired a Listener from
|
||||
// the Yggdrasil core before this point and it must not be in use elsewhere.
|
||||
func New(rwc *ipv6rwc.ReadWriteCloser, log core.Logger, opts ...SetupOption) (*TunAdapter, error) {
|
||||
func New(rwc ReadWriteCloser, log core.Logger, opts ...SetupOption) (*TunAdapter, error) {
|
||||
tun := &TunAdapter{
|
||||
rwc: rwc,
|
||||
log: log,
|
||||
@@ -108,7 +116,10 @@ func (tun *TunAdapter) _start() error {
|
||||
tun.addr = tun.rwc.Address()
|
||||
tun.subnet = tun.rwc.Subnet()
|
||||
prefix := address.GetPrefix()
|
||||
addr := fmt.Sprintf("%s/%d", net.IP(tun.addr[:]).String(), 8*len(prefix[:])-1)
|
||||
var addr string
|
||||
if tun.addr.IsValid() {
|
||||
addr = fmt.Sprintf("%s/%d", net.IP(tun.addr[:]).String(), 8*len(prefix[:])-1)
|
||||
}
|
||||
if tun.config.name == "none" || tun.config.name == "dummy" {
|
||||
tun.log.Debugln("Not starting TUN as ifname is none or dummy")
|
||||
tun.isEnabled = false
|
||||
@@ -119,7 +130,13 @@ func (tun *TunAdapter) _start() error {
|
||||
if tun.rwc.MaxMTU() < mtu {
|
||||
mtu = tun.rwc.MaxMTU()
|
||||
}
|
||||
if err := tun.setup(string(tun.config.name), addr, mtu); err != nil {
|
||||
var err error
|
||||
if tun.config.fd > 0 {
|
||||
err = tun.setupFD(tun.config.fd, addr, mtu)
|
||||
} else {
|
||||
err = tun.setup(string(tun.config.name), addr, mtu)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tun.MTU() != mtu {
|
||||
|
@@ -5,6 +5,7 @@ package tun
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -85,7 +86,15 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error {
|
||||
} else {
|
||||
tun.mtu = 0
|
||||
}
|
||||
return tun.setupAddress(addr)
|
||||
if addr != "" {
|
||||
return tun.setupAddress(addr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Configures the "utun" adapter from an existing file descriptor.
|
||||
func (tun *TunAdapter) setupFD(fd int32, addr string, mtu uint64) error {
|
||||
return fmt.Errorf("setup via FD not supported on this platform")
|
||||
}
|
||||
|
||||
func (tun *TunAdapter) setupAddress(addr string) error {
|
||||
|
@@ -1,5 +1,5 @@
|
||||
//go:build !mobile
|
||||
// +build !mobile
|
||||
//go:build darwin || ios
|
||||
// +build darwin ios
|
||||
|
||||
package tun
|
||||
|
||||
@@ -7,6 +7,7 @@ package tun
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unsafe"
|
||||
@@ -31,7 +32,35 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error {
|
||||
} else {
|
||||
tun.mtu = 0
|
||||
}
|
||||
return tun.setupAddress(addr)
|
||||
if addr != "" {
|
||||
return tun.setupAddress(addr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Configures the "utun" adapter from an existing file descriptor.
|
||||
func (tun *TunAdapter) setupFD(fd int32, addr string, mtu uint64) error {
|
||||
dfd, err := unix.Dup(int(fd))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = unix.SetNonblock(dfd, true)
|
||||
if err != nil {
|
||||
unix.Close(dfd)
|
||||
return err
|
||||
}
|
||||
iface, err := wgtun.CreateTUNFromFile(os.NewFile(uintptr(dfd), "/dev/tun"), 0)
|
||||
if err != nil {
|
||||
unix.Close(dfd)
|
||||
return err
|
||||
}
|
||||
tun.iface = iface
|
||||
if m, err := iface.MTU(); err == nil {
|
||||
tun.mtu = getSupportedMTU(uint64(m))
|
||||
} else {
|
||||
tun.mtu = 0
|
||||
}
|
||||
return nil // tun.setupAddress(addr)
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -117,13 +146,13 @@ func (tun *TunAdapter) setupAddress(addr string) error {
|
||||
tun.log.Infof("Interface IPv6: %s", addr)
|
||||
tun.log.Infof("Interface MTU: %d", ir.ifru_mtu)
|
||||
|
||||
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(darwin_SIOCAIFADDR_IN6), uintptr(unsafe.Pointer(&ar))); errno != 0 {
|
||||
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(darwin_SIOCAIFADDR_IN6), uintptr(unsafe.Pointer(&ar))); errno != 0 { // nolint:staticcheck
|
||||
err = errno
|
||||
tun.log.Errorf("Error in darwin_SIOCAIFADDR_IN6: %v", errno)
|
||||
return err
|
||||
}
|
||||
|
||||
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(unix.SIOCSIFMTU), uintptr(unsafe.Pointer(&ir))); errno != 0 {
|
||||
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(unix.SIOCSIFMTU), uintptr(unsafe.Pointer(&ir))); errno != 0 { // nolint:staticcheck
|
||||
err = errno
|
||||
tun.log.Errorf("Error in SIOCSIFMTU: %v", errno)
|
||||
return err
|
||||
|
@@ -1,11 +1,13 @@
|
||||
//go:build !mobile
|
||||
// +build !mobile
|
||||
//go:build linux || android
|
||||
// +build linux android
|
||||
|
||||
package tun
|
||||
|
||||
// The linux platform specific tun parts
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/vishvananda/netlink"
|
||||
wgtun "golang.zx2c4.com/wireguard/tun"
|
||||
)
|
||||
@@ -25,7 +27,15 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error {
|
||||
} else {
|
||||
tun.mtu = 0
|
||||
}
|
||||
return tun.setupAddress(addr)
|
||||
if addr != "" {
|
||||
return tun.setupAddress(addr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Configures the "utun" adapter from an existing file descriptor.
|
||||
func (tun *TunAdapter) setupFD(fd int32, addr string, mtu uint64) error {
|
||||
return fmt.Errorf("setup via FD not supported on this platform")
|
||||
}
|
||||
|
||||
// Configures the TUN adapter with the correct IPv6 address and MTU. Netlink
|
||||
|
@@ -1,5 +1,5 @@
|
||||
//go:build !linux && !darwin && !windows && !openbsd && !freebsd && !mobile
|
||||
// +build !linux,!darwin,!windows,!openbsd,!freebsd,!mobile
|
||||
//go:build !linux && !darwin && !ios && !android && !windows && !openbsd && !freebsd && !mobile
|
||||
// +build !linux,!darwin,!ios,!android,!windows,!openbsd,!freebsd,!mobile
|
||||
|
||||
package tun
|
||||
|
||||
@@ -7,6 +7,8 @@ package tun
|
||||
// If your platform supports tun devices, you could try configuring it manually
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
wgtun "golang.zx2c4.com/wireguard/tun"
|
||||
)
|
||||
|
||||
@@ -22,7 +24,15 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error {
|
||||
} else {
|
||||
tun.mtu = 0
|
||||
}
|
||||
return tun.setupAddress(addr)
|
||||
if addr != "" {
|
||||
return tun.setupAddress(addr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Configures the "utun" adapter from an existing file descriptor.
|
||||
func (tun *TunAdapter) setupFD(fd int32, addr string, mtu uint64) error {
|
||||
return fmt.Errorf("setup via FD not supported on this platform")
|
||||
}
|
||||
|
||||
// We don't know how to set the IPv6 address on an unknown platform, therefore
|
||||
|
@@ -4,10 +4,10 @@
|
||||
package tun
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/netip"
|
||||
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/config"
|
||||
"golang.org/x/sys/windows"
|
||||
@@ -35,9 +35,11 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error {
|
||||
return err
|
||||
}
|
||||
tun.iface = iface
|
||||
if err = tun.setupAddress(addr); err != nil {
|
||||
tun.log.Errorln("Failed to set up TUN address:", err)
|
||||
return err
|
||||
if addr != "" {
|
||||
if err = tun.setupAddress(addr); err != nil {
|
||||
tun.log.Errorln("Failed to set up TUN address:", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err = tun.setupMTU(getSupportedMTU(mtu)); err != nil {
|
||||
tun.log.Errorln("Failed to set up TUN MTU:", err)
|
||||
@@ -50,6 +52,11 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error {
|
||||
})
|
||||
}
|
||||
|
||||
// Configures the "utun" adapter from an existing file descriptor.
|
||||
func (tun *TunAdapter) setupFD(fd int32, addr string, mtu uint64) error {
|
||||
return fmt.Errorf("setup via FD not supported on this platform")
|
||||
}
|
||||
|
||||
// Sets the MTU of the TUN adapter.
|
||||
func (tun *TunAdapter) setupMTU(mtu uint64) error {
|
||||
if tun.iface == nil || tun.Name() == "" {
|
||||
@@ -83,13 +90,9 @@ func (tun *TunAdapter) setupAddress(addr string) error {
|
||||
return errors.New("Can't configure IPv6 address as TUN adapter is not present")
|
||||
}
|
||||
if intf, ok := tun.iface.(*wgtun.NativeTun); ok {
|
||||
if ipaddr, ipnet, err := net.ParseCIDR(addr); err == nil {
|
||||
if ipnet, err := netip.ParsePrefix(addr); err == nil {
|
||||
luid := winipcfg.LUID(intf.LUID())
|
||||
addresses := append([]net.IPNet{}, net.IPNet{
|
||||
IP: ipaddr,
|
||||
Mask: ipnet.Mask,
|
||||
})
|
||||
|
||||
addresses := []netip.Prefix{ipnet}
|
||||
err := luid.SetIPAddressesForFamily(windows.AF_INET6, addresses)
|
||||
if err == windows.ERROR_OBJECT_ALREADY_EXISTS {
|
||||
cleanupAddressesOnDisconnectedInterfaces(windows.AF_INET6, addresses)
|
||||
@@ -112,24 +115,13 @@ func (tun *TunAdapter) setupAddress(addr string) error {
|
||||
* SPDX-License-Identifier: MIT
|
||||
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
func cleanupAddressesOnDisconnectedInterfaces(family winipcfg.AddressFamily, addresses []net.IPNet) {
|
||||
func cleanupAddressesOnDisconnectedInterfaces(family winipcfg.AddressFamily, addresses []netip.Prefix) {
|
||||
if len(addresses) == 0 {
|
||||
return
|
||||
}
|
||||
includedInAddresses := func(a net.IPNet) bool {
|
||||
// TODO: this makes the whole algorithm O(n^2). But we can't stick net.IPNet in a Go hashmap. Bummer!
|
||||
for _, addr := range addresses {
|
||||
ip := addr.IP
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
ip = ip4
|
||||
}
|
||||
mA, _ := addr.Mask.Size()
|
||||
mB, _ := a.Mask.Size()
|
||||
if bytes.Equal(ip, a.IP) && mA == mB {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
addrHash := make(map[netip.Addr]bool, len(addresses))
|
||||
for i := range addresses {
|
||||
addrHash[addresses[i].Addr()] = true
|
||||
}
|
||||
interfaces, err := winipcfg.GetAdaptersAddresses(family, winipcfg.GAAFlagDefault)
|
||||
if err != nil {
|
||||
@@ -140,11 +132,10 @@ func cleanupAddressesOnDisconnectedInterfaces(family winipcfg.AddressFamily, add
|
||||
continue
|
||||
}
|
||||
for address := iface.FirstUnicastAddress; address != nil; address = address.Next {
|
||||
ip := address.Address.IP()
|
||||
ipnet := net.IPNet{IP: ip, Mask: net.CIDRMask(int(address.OnLinkPrefixLength), 8*len(ip))}
|
||||
if includedInAddresses(ipnet) {
|
||||
log.Printf("Cleaning up stale address %s from interface ‘%s’", ipnet.String(), iface.FriendlyName())
|
||||
iface.LUID.DeleteIPAddress(ipnet)
|
||||
if ip, _ := netip.AddrFromSlice(address.Address.IP()); addrHash[ip] {
|
||||
prefix := netip.PrefixFrom(ip, int(address.OnLinkPrefixLength))
|
||||
log.Printf("Cleaning up stale address %s from interface ‘%s’", prefix.String(), iface.FriendlyName())
|
||||
iface.LUID.DeleteIPAddress(prefix)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user