Merge branch 'develop' into setuid

This commit is contained in:
Neil 2024-09-22 16:44:01 +01:00 committed by GitHub
commit fb587b16dd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 128 additions and 118 deletions

View file

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

View file

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

View file

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

View file

@ -174,7 +174,7 @@ func run() int {
if err := json.Unmarshal(recv.Response, &resp); err != nil { if err := json.Unmarshal(recv.Response, &resp); err != nil {
panic(err) panic(err)
} }
table.SetHeader([]string{"URI", "State", "Dir", "IP Address", "Uptime", "RTT", "RX", "TX", "Pr", "Last Error"}) table.SetHeader([]string{"URI", "State", "Dir", "IP Address", "Uptime", "RTT", "RX", "TX", "Pr", "Cost", "Last Error"})
for _, peer := range resp.Peers { for _, peer := range resp.Peers {
state, lasterr, dir, rtt := "Up", "-", "Out", "-" state, lasterr, dir, rtt := "Up", "-", "Out", "-"
if !peer.Up { if !peer.Up {
@ -200,6 +200,7 @@ func run() int {
peer.RXBytes.String(), peer.RXBytes.String(),
peer.TXBytes.String(), peer.TXBytes.String(),
fmt.Sprintf("%d", peer.Priority), fmt.Sprintf("%d", peer.Priority),
fmt.Sprintf("%d", peer.Cost),
lasterr, lasterr,
}) })
} }

View file

@ -15,8 +15,6 @@ import (
"github.com/yggdrasil-network/yggdrasil-go/src/multicast" "github.com/yggdrasil-network/yggdrasil-go/src/multicast"
"github.com/yggdrasil-network/yggdrasil-go/src/tun" "github.com/yggdrasil-network/yggdrasil-go/src/tun"
"github.com/yggdrasil-network/yggdrasil-go/src/version" "github.com/yggdrasil-network/yggdrasil-go/src/version"
_ "golang.org/x/mobile/bind"
) )
// Yggdrasil mobile package is meant to "plug the gap" for mobile support, as // Yggdrasil mobile package is meant to "plug the gap" for mobile support, as

6
go.mod
View file

@ -3,7 +3,7 @@ module github.com/yggdrasil-network/yggdrasil-go
go 1.21 go 1.21
require ( require (
github.com/Arceliar/ironwood v0.0.0-20240529054413-b8e59574e2b2 github.com/Arceliar/ironwood v0.0.0-20240921214443-277f642d5db3
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d
github.com/cheggaaa/pb/v3 v3.1.5 github.com/cheggaaa/pb/v3 v3.1.5
github.com/gologme/log v1.3.0 github.com/gologme/log v1.3.0
@ -12,11 +12,12 @@ require (
github.com/kardianos/minwinsvc v1.0.2 github.com/kardianos/minwinsvc v1.0.2
github.com/quic-go/quic-go v0.45.1 github.com/quic-go/quic-go v0.45.1
github.com/vishvananda/netlink v1.1.0 github.com/vishvananda/netlink v1.1.0
github.com/wlynxg/anet v0.0.4-0.20240806025826-e684438fc7c6
golang.org/x/crypto v0.25.0 golang.org/x/crypto v0.25.0
golang.org/x/mobile v0.0.0-20240716161057-1ad2df20a8b6
golang.org/x/net v0.27.0 golang.org/x/net v0.27.0
golang.org/x/sys v0.22.0 golang.org/x/sys v0.22.0
golang.org/x/text v0.16.0 golang.org/x/text v0.16.0
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
golang.zx2c4.com/wireguard/windows v0.5.3 golang.zx2c4.com/wireguard/windows v0.5.3
nhooyr.io/websocket v1.8.11 nhooyr.io/websocket v1.8.11
@ -35,7 +36,6 @@ require (
golang.org/x/mod v0.19.0 // indirect golang.org/x/mod v0.19.0 // indirect
golang.org/x/sync v0.7.0 // indirect golang.org/x/sync v0.7.0 // indirect
golang.org/x/tools v0.23.0 // indirect golang.org/x/tools v0.23.0 // indirect
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
) )
require ( require (

8
go.sum
View file

@ -1,5 +1,5 @@
github.com/Arceliar/ironwood v0.0.0-20240529054413-b8e59574e2b2 h1:SBdYBKeXYUUFef5wi2CMhYmXFVGiYaRpTvbki0Bu+JQ= github.com/Arceliar/ironwood v0.0.0-20240921214443-277f642d5db3 h1:OJ49qTfdw5MNkpnRraNEsVQbHahSvShh8Z9WYpZrYa0=
github.com/Arceliar/ironwood v0.0.0-20240529054413-b8e59574e2b2/go.mod h1:6WP4799FX0OuWdENGQAh+0RXp9FLh0y7NZ7tM9cJyXk= github.com/Arceliar/ironwood v0.0.0-20240921214443-277f642d5db3/go.mod h1:6WP4799FX0OuWdENGQAh+0RXp9FLh0y7NZ7tM9cJyXk=
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM=
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q=
github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
@ -70,6 +70,8 @@ github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYp
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f h1:p4VB7kIXpOQvVn1ZaTIVp+3vuYAXFe3OJEvjbUYJLaA= github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f h1:p4VB7kIXpOQvVn1ZaTIVp+3vuYAXFe3OJEvjbUYJLaA=
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/wlynxg/anet v0.0.4-0.20240806025826-e684438fc7c6 h1:c/wkXIJvpg2oot7iFqPESTBAO9UvhWTBnW97y9aPgyU=
github.com/wlynxg/anet v0.0.4-0.20240806025826-e684438fc7c6/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
@ -81,8 +83,6 @@ golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/mobile v0.0.0-20240716161057-1ad2df20a8b6 h1:/VlmIrkuLf2wzPjkZ8imSpckHoW7Y71h66dxbLHSpi8=
golang.org/x/mobile v0.0.0-20240716161057-1ad2df20a8b6/go.mod h1:TCsc78+c4cqb8IKEosz2LwJ6YRNkIjMuAYeHYjchGDE=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=

View file

@ -24,6 +24,7 @@ type PeerEntry struct {
PublicKey string `json:"key"` PublicKey string `json:"key"`
Port uint64 `json:"port"` Port uint64 `json:"port"`
Priority uint64 `json:"priority"` Priority uint64 `json:"priority"`
Cost uint64 `json:"cost"`
RXBytes DataUnit `json:"bytes_recvd,omitempty"` RXBytes DataUnit `json:"bytes_recvd,omitempty"`
TXBytes DataUnit `json:"bytes_sent,omitempty"` TXBytes DataUnit `json:"bytes_sent,omitempty"`
Uptime float64 `json:"uptime,omitempty"` Uptime float64 `json:"uptime,omitempty"`
@ -41,6 +42,7 @@ func (a *AdminSocket) getPeersHandler(_ *GetPeersRequest, res *GetPeersResponse)
Up: p.Up, Up: p.Up,
Inbound: p.Inbound, Inbound: p.Inbound,
Priority: uint64(p.Priority), // can't be uint8 thanks to gobind Priority: uint64(p.Priority), // can't be uint8 thanks to gobind
Cost: p.Cost,
URI: p.URI, URI: p.URI,
RXBytes: DataUnit(p.RXBytes), RXBytes: DataUnit(p.RXBytes),
TXBytes: DataUnit(p.TXBytes), TXBytes: DataUnit(p.TXBytes),

View file

@ -30,6 +30,7 @@ type PeerInfo struct {
Coords []uint64 Coords []uint64
Port uint64 Port uint64
Priority uint8 Priority uint8
Cost uint64
RXBytes uint64 RXBytes uint64
TXBytes uint64 TXBytes uint64
Uptime time.Duration Uptime time.Duration
@ -94,6 +95,7 @@ func (c *Core) GetPeers() []PeerInfo {
peerinfo.Port = p.Port peerinfo.Port = p.Port
peerinfo.Priority = p.Priority peerinfo.Priority = p.Priority
peerinfo.Latency = p.Latency peerinfo.Latency = p.Latency
peerinfo.Cost = p.Cost
} }
peers = append(peers, peerinfo) peers = append(peers, peerinfo)
} }

View file

@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"context" "context"
"encoding/hex" "encoding/hex"
"errors"
"fmt" "fmt"
"io" "io"
"net" "net"
@ -40,7 +41,8 @@ type links struct {
ws *linkWS // WS interface support ws *linkWS // WS interface support
wss *linkWSS // WSS interface support wss *linkWSS // WSS interface support
// _links can only be modified safely from within the links actor // _links can only be modified safely from within the links actor
_links map[linkInfo]*link // *link is nil if connection in progress _links map[linkInfo]*link // *link is nil if connection in progress
_listeners map[*Listener]context.CancelFunc
} }
type linkProtocol interface { type linkProtocol interface {
@ -85,13 +87,6 @@ func (l *Listener) Addr() net.Addr {
return l.listener.Addr() return l.listener.Addr()
} }
func (l *Listener) Close() error {
l.Cancel()
err := l.listener.Close()
<-l.ctx.Done()
return err
}
func (l *links) init(c *Core) error { func (l *links) init(c *Core) error {
l.core = c l.core = c
l.tcp = l.newLinkTCP() l.tcp = l.newLinkTCP()
@ -102,32 +97,18 @@ func (l *links) init(c *Core) error {
l.ws = l.newLinkWS() l.ws = l.newLinkWS()
l.wss = l.newLinkWSS() l.wss = l.newLinkWSS()
l._links = make(map[linkInfo]*link) l._links = make(map[linkInfo]*link)
l._listeners = make(map[*Listener]context.CancelFunc)
var listeners []ListenAddress
phony.Block(c, func() {
listeners = make([]ListenAddress, 0, len(c.config._listeners))
for listener := range c.config._listeners {
listeners = append(listeners, listener)
}
})
return nil return nil
} }
func (l *links) shutdown() { func (l *links) shutdown() {
phony.Block(l.tcp, func() { phony.Block(l, func() {
for l := range l.tcp._listeners { for listener := range l._listeners {
_ = l.Close() _ = listener.listener.Close()
} }
}) for _, link := range l._links {
phony.Block(l.tls, func() { _ = link._conn.Close()
for l := range l.tls._listeners {
_ = l.Close()
}
})
phony.Block(l.unix, func() {
for l := range l.unix._listeners {
_ = l.Close()
} }
}) })
} }
@ -457,11 +438,18 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) {
options.password = []byte(p) options.password = []byte(p)
} }
phony.Block(l, func() {
l._listeners[li] = cancel
})
go func() { go func() {
l.core.log.Infof("%s listener started on %s", strings.ToUpper(u.Scheme), listener.Addr()) l.core.log.Infof("%s listener started on %s", strings.ToUpper(u.Scheme), li.listener.Addr())
defer l.core.log.Infof("%s listener stopped on %s", strings.ToUpper(u.Scheme), listener.Addr()) defer l.core.log.Infof("%s listener stopped on %s", strings.ToUpper(u.Scheme), li.listener.Addr())
defer phony.Block(l, func() {
delete(l._listeners, li)
})
for { for {
conn, err := listener.Accept() conn, err := li.listener.Accept()
if err != nil { if err != nil {
return return
} }
@ -517,13 +505,22 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) {
// Store the state of the link so that it can be queried later. // Store the state of the link so that it can be queried later.
l._links[info] = state l._links[info] = state
}) })
defer phony.Block(l, func() {
if l._links[info] == state {
delete(l._links, info)
}
})
if lc == nil { if lc == nil {
return return
} }
// Give the connection to the handler. The handler will block // Give the connection to the handler. The handler will block
// for the lifetime of the connection. // for the lifetime of the connection.
if err = l.handler(linkTypeIncoming, options, lc, nil); err != nil && err != io.EOF { switch err = l.handler(linkTypeIncoming, options, lc, nil); {
case err == nil:
case errors.Is(err, io.EOF):
case errors.Is(err, net.ErrClosed):
default:
l.core.log.Debugf("Link %s error: %s\n", u.Host, err) l.core.log.Debugf("Link %s error: %s\n", u.Host, err)
} }
@ -531,11 +528,6 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) {
// try to close the underlying socket just in case and then // try to close the underlying socket just in case and then
// drop the link state. // drop the link state.
_ = lc.Close() _ = lc.Close()
phony.Block(l, func() {
if l._links[info] == state {
delete(l._links, info)
}
})
}(conn) }(conn)
} }
}() }()

View file

@ -15,7 +15,6 @@ type linkTCP struct {
phony.Inbox phony.Inbox
*links *links
listenconfig *net.ListenConfig listenconfig *net.ListenConfig
_listeners map[*Listener]context.CancelFunc
} }
func (l *links) newLinkTCP() *linkTCP { func (l *links) newLinkTCP() *linkTCP {
@ -24,7 +23,6 @@ func (l *links) newLinkTCP() *linkTCP {
listenconfig: &net.ListenConfig{ listenconfig: &net.ListenConfig{
KeepAlive: -1, KeepAlive: -1,
}, },
_listeners: map[*Listener]context.CancelFunc{},
} }
lt.listenconfig.Control = lt.tcpContext lt.listenconfig.Control = lt.tcpContext
return lt return lt

View file

@ -13,10 +13,9 @@ import (
type linkTLS struct { type linkTLS struct {
phony.Inbox phony.Inbox
*links *links
tcp *linkTCP tcp *linkTCP
listener *net.ListenConfig listener *net.ListenConfig
config *tls.Config config *tls.Config
_listeners map[*Listener]context.CancelFunc
} }
func (l *links) newLinkTLS(tcp *linkTCP) *linkTLS { func (l *links) newLinkTLS(tcp *linkTCP) *linkTLS {
@ -27,8 +26,7 @@ func (l *links) newLinkTLS(tcp *linkTCP) *linkTLS {
Control: tcp.tcpContext, Control: tcp.tcpContext,
KeepAlive: -1, KeepAlive: -1,
}, },
config: l.core.config.tls.Clone(), config: l.core.config.tls.Clone(),
_listeners: map[*Listener]context.CancelFunc{},
} }
return lt return lt
} }

View file

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

View file

@ -14,6 +14,7 @@ import (
type linkWS struct { type linkWS struct {
phony.Inbox phony.Inbox
*links *links
listenconfig *net.ListenConfig
} }
type linkWSConn struct { type linkWSConn struct {
@ -78,6 +79,9 @@ func (s *wsServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
func (l *links) newLinkWS() *linkWS { func (l *links) newLinkWS() *linkWS {
lt := &linkWS{ lt := &linkWS{
links: l, links: l,
listenconfig: &net.ListenConfig{
KeepAlive: -1,
},
} }
return lt return lt
} }
@ -95,7 +99,7 @@ func (l *linkWS) dial(ctx context.Context, url *url.URL, info linkInfo, options
} }
func (l *linkWS) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) { func (l *linkWS) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) {
nl, err := net.Listen("tcp", url.Host) nl, err := l.listenconfig.Listen(ctx, "tcp", url.Host)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

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

View file

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

View file

@ -11,7 +11,6 @@ import (
"io" "io"
"net" "net"
"sync" "sync"
"time"
"github.com/Arceliar/phony" "github.com/Arceliar/phony"
wgtun "golang.zx2c4.com/wireguard/tun" wgtun "golang.zx2c4.com/wireguard/tun"
@ -65,20 +64,6 @@ func getSupportedMTU(mtu uint64) uint64 {
return mtu return mtu
} }
func waitForTUNUp(ch <-chan wgtun.Event) bool {
t := time.After(time.Second * 5)
for {
select {
case ev := <-ch:
if ev == wgtun.EventUp {
return true
}
case <-t:
return false
}
}
}
// Name returns the name of the adapter, e.g. "tun0". On Windows, this may // Name returns the name of the adapter, e.g. "tun0". On Windows, this may
// return a canonical adapter name instead. // return a canonical adapter name instead.
func (tun *TunAdapter) Name() string { func (tun *TunAdapter) Name() string {

View file

@ -80,9 +80,6 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error {
if err != nil { if err != nil {
return fmt.Errorf("failed to create TUN: %w", err) return fmt.Errorf("failed to create TUN: %w", err)
} }
if !waitForTUNUp(iface.Events()) {
return fmt.Errorf("TUN did not come up in time")
}
tun.iface = iface tun.iface = iface
if mtu, err := iface.MTU(); err == nil { if mtu, err := iface.MTU(); err == nil {
tun.mtu = getSupportedMTU(uint64(mtu)) tun.mtu = getSupportedMTU(uint64(mtu))

View file

@ -27,9 +27,6 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error {
if err != nil { if err != nil {
return fmt.Errorf("failed to create TUN: %w", err) return fmt.Errorf("failed to create TUN: %w", err)
} }
if !waitForTUNUp(iface.Events()) {
return fmt.Errorf("TUN did not come up in time")
}
tun.iface = iface tun.iface = iface
if m, err := iface.MTU(); err == nil { if m, err := iface.MTU(); err == nil {
tun.mtu = getSupportedMTU(uint64(m)) tun.mtu = getSupportedMTU(uint64(m))
@ -58,9 +55,6 @@ func (tun *TunAdapter) setupFD(fd int32, addr string, mtu uint64) error {
unix.Close(dfd) unix.Close(dfd)
return fmt.Errorf("failed to create TUN from FD: %w", err) return fmt.Errorf("failed to create TUN from FD: %w", err)
} }
if !waitForTUNUp(iface.Events()) {
return fmt.Errorf("TUN did not come up in time")
}
tun.iface = iface tun.iface = iface
if m, err := iface.MTU(); err == nil { if m, err := iface.MTU(); err == nil {
tun.mtu = getSupportedMTU(uint64(m)) tun.mtu = getSupportedMTU(uint64(m))

View file

@ -21,9 +21,6 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error {
if err != nil { if err != nil {
return fmt.Errorf("failed to create TUN: %w", err) return fmt.Errorf("failed to create TUN: %w", err)
} }
if !waitForTUNUp(iface.Events()) {
return fmt.Errorf("TUN did not come up in time")
}
tun.iface = iface tun.iface = iface
if mtu, err := iface.MTU(); err == nil { if mtu, err := iface.MTU(); err == nil {
tun.mtu = getSupportedMTU(uint64(mtu)) tun.mtu = getSupportedMTU(uint64(mtu))

View file

@ -18,9 +18,6 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error {
if err != nil { if err != nil {
return fmt.Errorf("failed to create TUN: %w", err) return fmt.Errorf("failed to create TUN: %w", err)
} }
if !waitForTUNUp(iface.Events()) {
return fmt.Errorf("TUN did not come up in time")
}
tun.iface = iface tun.iface = iface
if mtu, err := iface.MTU(); err == nil { if mtu, err := iface.MTU(); err == nil {
tun.mtu = getSupportedMTU(uint64(mtu)) tun.mtu = getSupportedMTU(uint64(mtu))

View file

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