diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a3f34789..57a0d2a7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,10 +17,10 @@ jobs: steps: - uses: actions/setup-go@v5 with: - go-version: 1.21 + go-version: stable - uses: actions/checkout@v4 - name: golangci-lint - uses: golangci/golangci-lint-action@v3 + uses: golangci/golangci-lint-action@v6 with: args: --issues-exit-code=1 @@ -51,7 +51,7 @@ jobs: strategy: fail-fast: false matrix: - goversion: ["1.21", "1.22"] + goversion: ["1.21", "1.22", "1.23"] name: Build & Test (Linux, Go ${{ matrix.goversion }}) needs: [lint] @@ -75,7 +75,7 @@ jobs: strategy: fail-fast: false matrix: - goversion: ["1.21", "1.22"] + goversion: ["1.21", "1.22", "1.23"] name: Build & Test (Windows, Go ${{ matrix.goversion }}) needs: [lint] @@ -99,7 +99,7 @@ jobs: strategy: fail-fast: false matrix: - goversion: ["1.21", "1.22"] + goversion: ["1.21", "1.22", "1.23"] name: Build & Test (macOS, Go ${{ matrix.goversion }}) needs: [lint] @@ -123,7 +123,7 @@ jobs: strategy: fail-fast: false matrix: - goversion: ["1.21", "1.22"] + goversion: ["1.21", "1.22", "1.23"] goos: - freebsd - openbsd diff --git a/.golangci.yml b/.golangci.yml index c35edee4..836af618 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -2,9 +2,10 @@ run: build-tags: - lint issues-exit-code: 0 # TODO: change this to 1 when we want it to fail builds - skip-dirs: +issues: + exclude-dirs: - contrib/ - misc/ linters: disable: - - gocyclo \ No newline at end of file + - gocyclo diff --git a/CHANGELOG.md b/CHANGELOG.md index b339294c..34c7fe30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,32 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - 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 * Go 1.21 is now required to build Yggdrasil diff --git a/cmd/yggdrasilctl/main.go b/cmd/yggdrasilctl/main.go index 2a1d70b1..8a30f438 100644 --- a/cmd/yggdrasilctl/main.go +++ b/cmd/yggdrasilctl/main.go @@ -174,7 +174,7 @@ func run() int { if err := json.Unmarshal(recv.Response, &resp); err != nil { panic(err) } - table.SetHeader([]string{"URI", "State", "Dir", "IP Address", "Uptime", "RTT", "RX", "TX", "Pr", "Last Error"}) + table.SetHeader([]string{"URI", "State", "Dir", "IP Address", "Uptime", "RTT", "RX", "TX", "Pr", "Cost", "Last Error"}) for _, peer := range resp.Peers { state, lasterr, dir, rtt := "Up", "-", "Out", "-" if !peer.Up { @@ -200,6 +200,7 @@ func run() int { peer.RXBytes.String(), peer.TXBytes.String(), fmt.Sprintf("%d", peer.Priority), + fmt.Sprintf("%d", peer.Cost), lasterr, }) } diff --git a/contrib/mobile/mobile.go b/contrib/mobile/mobile.go index 82e73485..06f48027 100644 --- a/contrib/mobile/mobile.go +++ b/contrib/mobile/mobile.go @@ -15,8 +15,6 @@ import ( "github.com/yggdrasil-network/yggdrasil-go/src/multicast" "github.com/yggdrasil-network/yggdrasil-go/src/tun" "github.com/yggdrasil-network/yggdrasil-go/src/version" - - _ "golang.org/x/mobile/bind" ) // Yggdrasil mobile package is meant to "plug the gap" for mobile support, as diff --git a/go.mod b/go.mod index 5ff4479a..e10feb95 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/yggdrasil-network/yggdrasil-go go 1.21 require ( - github.com/Arceliar/ironwood v0.0.0-20240529054413-b8e59574e2b2 + github.com/Arceliar/ironwood v0.0.0-20240921214443-277f642d5db3 github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d github.com/cheggaaa/pb/v3 v3.1.5 github.com/gologme/log v1.3.0 @@ -12,11 +12,12 @@ require ( github.com/kardianos/minwinsvc v1.0.2 github.com/quic-go/quic-go v0.45.1 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/mobile v0.0.0-20240716161057-1ad2df20a8b6 golang.org/x/net v0.27.0 golang.org/x/sys v0.22.0 golang.org/x/text v0.16.0 + golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 golang.zx2c4.com/wireguard/windows v0.5.3 nhooyr.io/websocket v1.8.11 @@ -35,7 +36,6 @@ require ( golang.org/x/mod v0.19.0 // indirect golang.org/x/sync v0.7.0 // indirect golang.org/x/tools v0.23.0 // indirect - golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect ) require ( diff --git a/go.sum b/go.sum index 1cb8f5b8..5ddf4169 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Arceliar/ironwood v0.0.0-20240529054413-b8e59574e2b2 h1:SBdYBKeXYUUFef5wi2CMhYmXFVGiYaRpTvbki0Bu+JQ= -github.com/Arceliar/ironwood v0.0.0-20240529054413-b8e59574e2b2/go.mod h1:6WP4799FX0OuWdENGQAh+0RXp9FLh0y7NZ7tM9cJyXk= +github.com/Arceliar/ironwood v0.0.0-20240921214443-277f642d5db3 h1:OJ49qTfdw5MNkpnRraNEsVQbHahSvShh8Z9WYpZrYa0= +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/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q= 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-20210104183010-2eb08e3e575f h1:p4VB7kIXpOQvVn1ZaTIVp+3vuYAXFe3OJEvjbUYJLaA= 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= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= 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/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= -golang.org/x/mobile v0.0.0-20240716161057-1ad2df20a8b6 h1:/VlmIrkuLf2wzPjkZ8imSpckHoW7Y71h66dxbLHSpi8= -golang.org/x/mobile v0.0.0-20240716161057-1ad2df20a8b6/go.mod h1:TCsc78+c4cqb8IKEosz2LwJ6YRNkIjMuAYeHYjchGDE= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= diff --git a/src/admin/getpeers.go b/src/admin/getpeers.go index e44428c3..48234fb1 100644 --- a/src/admin/getpeers.go +++ b/src/admin/getpeers.go @@ -24,6 +24,7 @@ type PeerEntry struct { PublicKey string `json:"key"` Port uint64 `json:"port"` Priority uint64 `json:"priority"` + Cost uint64 `json:"cost"` RXBytes DataUnit `json:"bytes_recvd,omitempty"` TXBytes DataUnit `json:"bytes_sent,omitempty"` Uptime float64 `json:"uptime,omitempty"` @@ -41,6 +42,7 @@ func (a *AdminSocket) getPeersHandler(_ *GetPeersRequest, res *GetPeersResponse) Up: p.Up, Inbound: p.Inbound, Priority: uint64(p.Priority), // can't be uint8 thanks to gobind + Cost: p.Cost, URI: p.URI, RXBytes: DataUnit(p.RXBytes), TXBytes: DataUnit(p.TXBytes), diff --git a/src/core/api.go b/src/core/api.go index 875d7bf2..c236b1b5 100644 --- a/src/core/api.go +++ b/src/core/api.go @@ -30,6 +30,7 @@ type PeerInfo struct { Coords []uint64 Port uint64 Priority uint8 + Cost uint64 RXBytes uint64 TXBytes uint64 Uptime time.Duration @@ -94,6 +95,7 @@ func (c *Core) GetPeers() []PeerInfo { peerinfo.Port = p.Port peerinfo.Priority = p.Priority peerinfo.Latency = p.Latency + peerinfo.Cost = p.Cost } peers = append(peers, peerinfo) } diff --git a/src/core/link.go b/src/core/link.go index ee2ad06f..1ead4e32 100644 --- a/src/core/link.go +++ b/src/core/link.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "encoding/hex" + "errors" "fmt" "io" "net" @@ -40,7 +41,8 @@ type links struct { ws *linkWS // WS interface support wss *linkWSS // WSS interface support // _links can only be modified safely from within the links actor - _links map[linkInfo]*link // *link is nil if connection in progress + _links map[linkInfo]*link // *link is nil if connection in progress + _listeners map[*Listener]context.CancelFunc } type linkProtocol interface { @@ -85,13 +87,6 @@ func (l *Listener) Addr() net.Addr { return l.listener.Addr() } -func (l *Listener) Close() error { - l.Cancel() - err := l.listener.Close() - <-l.ctx.Done() - return err -} - func (l *links) init(c *Core) error { l.core = c l.tcp = l.newLinkTCP() @@ -102,32 +97,18 @@ func (l *links) init(c *Core) error { l.ws = l.newLinkWS() l.wss = l.newLinkWSS() l._links = make(map[linkInfo]*link) - - 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) - } - }) + l._listeners = make(map[*Listener]context.CancelFunc) return nil } func (l *links) shutdown() { - phony.Block(l.tcp, func() { - for l := range l.tcp._listeners { - _ = l.Close() + phony.Block(l, func() { + for listener := range l._listeners { + _ = listener.listener.Close() } - }) - phony.Block(l.tls, func() { - for l := range l.tls._listeners { - _ = l.Close() - } - }) - phony.Block(l.unix, func() { - for l := range l.unix._listeners { - _ = l.Close() + for _, link := range l._links { + _ = link._conn.Close() } }) } @@ -457,11 +438,18 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) { options.password = []byte(p) } + phony.Block(l, func() { + l._listeners[li] = cancel + }) + go func() { - l.core.log.Infof("%s listener started on %s", strings.ToUpper(u.Scheme), listener.Addr()) - defer l.core.log.Infof("%s listener stopped 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), li.listener.Addr()) + defer phony.Block(l, func() { + delete(l._listeners, li) + }) for { - conn, err := listener.Accept() + conn, err := li.listener.Accept() if err != nil { 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. l._links[info] = state }) + defer phony.Block(l, func() { + if l._links[info] == state { + delete(l._links, info) + } + }) if lc == nil { return } // Give the connection to the handler. The handler will block // for the lifetime of the connection. - 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) } @@ -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 // drop the link state. _ = lc.Close() - phony.Block(l, func() { - if l._links[info] == state { - delete(l._links, info) - } - }) }(conn) } }() diff --git a/src/core/link_tcp.go b/src/core/link_tcp.go index f595aeb9..4ee50941 100644 --- a/src/core/link_tcp.go +++ b/src/core/link_tcp.go @@ -15,7 +15,6 @@ type linkTCP struct { phony.Inbox *links listenconfig *net.ListenConfig - _listeners map[*Listener]context.CancelFunc } func (l *links) newLinkTCP() *linkTCP { @@ -24,7 +23,6 @@ func (l *links) newLinkTCP() *linkTCP { listenconfig: &net.ListenConfig{ KeepAlive: -1, }, - _listeners: map[*Listener]context.CancelFunc{}, } lt.listenconfig.Control = lt.tcpContext return lt diff --git a/src/core/link_tls.go b/src/core/link_tls.go index a93227f6..d51d0ce5 100644 --- a/src/core/link_tls.go +++ b/src/core/link_tls.go @@ -13,10 +13,9 @@ import ( type linkTLS struct { phony.Inbox *links - tcp *linkTCP - listener *net.ListenConfig - config *tls.Config - _listeners map[*Listener]context.CancelFunc + tcp *linkTCP + listener *net.ListenConfig + config *tls.Config } func (l *links) newLinkTLS(tcp *linkTCP) *linkTLS { @@ -27,8 +26,7 @@ func (l *links) newLinkTLS(tcp *linkTCP) *linkTLS { Control: tcp.tcpContext, KeepAlive: -1, }, - config: l.core.config.tls.Clone(), - _listeners: map[*Listener]context.CancelFunc{}, + config: l.core.config.tls.Clone(), } return lt } diff --git a/src/core/link_unix.go b/src/core/link_unix.go index 8dde8946..ddbfa0ad 100644 --- a/src/core/link_unix.go +++ b/src/core/link_unix.go @@ -12,9 +12,8 @@ import ( type linkUNIX struct { phony.Inbox *links - dialer *net.Dialer - listener *net.ListenConfig - _listeners map[*Listener]context.CancelFunc + dialer *net.Dialer + listener *net.ListenConfig } func (l *links) newLinkUNIX() *linkUNIX { @@ -27,7 +26,6 @@ func (l *links) newLinkUNIX() *linkUNIX { listener: &net.ListenConfig{ KeepAlive: -1, }, - _listeners: map[*Listener]context.CancelFunc{}, } return lt } diff --git a/src/core/link_ws.go b/src/core/link_ws.go index f323b025..7a7d66f7 100644 --- a/src/core/link_ws.go +++ b/src/core/link_ws.go @@ -14,6 +14,7 @@ import ( type linkWS struct { phony.Inbox *links + listenconfig *net.ListenConfig } type linkWSConn struct { @@ -78,6 +79,9 @@ func (s *wsServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (l *links) newLinkWS() *linkWS { lt := &linkWS{ links: l, + listenconfig: &net.ListenConfig{ + KeepAlive: -1, + }, } 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) { - nl, err := net.Listen("tcp", url.Host) + nl, err := l.listenconfig.Listen(ctx, "tcp", url.Host) if err != nil { return nil, err } diff --git a/src/multicast/multicast.go b/src/multicast/multicast.go index bf10370f..902d7729 100644 --- a/src/multicast/multicast.go +++ b/src/multicast/multicast.go @@ -9,10 +9,12 @@ import ( "math/rand" "net" "net/url" + "sync/atomic" "time" "github.com/Arceliar/phony" "github.com/gologme/log" + "github.com/wlynxg/anet" "github.com/yggdrasil-network/yggdrasil-go/src/core" "golang.org/x/crypto/blake2b" @@ -28,7 +30,7 @@ type Multicast struct { core *core.Core log *log.Logger sock *ipv6.PacketConn - _isOpen bool + running atomic.Bool _listeners map[string]*listenerInfo _interfaces map[string]*interfaceInfo _timer *time.Timer @@ -79,7 +81,7 @@ func New(core *core.Core, log *log.Logger, opts ...SetupOption) (*Multicast, err } func (m *Multicast) _start() error { - if m._isOpen { + if !m.running.CompareAndSwap(false, true) { return fmt.Errorf("multicast module is already started") } var anyEnabled bool @@ -87,12 +89,14 @@ func (m *Multicast) _start() error { anyEnabled = anyEnabled || intf.Beacon || intf.Listen } if !anyEnabled { + m.running.Store(false) return nil } m.log.Debugln("Starting multicast module") defer m.log.Debugln("Started multicast module") addr, err := net.ResolveUDPAddr("udp", string(m.config._groupAddr)) if err != nil { + m.running.Store(false) return err } listenString := fmt.Sprintf("[::]:%v", addr.Port) @@ -101,6 +105,7 @@ func (m *Multicast) _start() error { } conn, err := lc.ListenPacket(context.Background(), "udp6", listenString) if err != nil { + m.running.Store(false) return err } m.sock = ipv6.NewPacketConn(conn) @@ -108,7 +113,6 @@ func (m *Multicast) _start() error { // Windows can't set this flag, so we need to handle it in other ways } - m._isOpen = true go m.listen() m.Act(nil, m._multicastStarted) m.Act(nil, m._announce) @@ -118,11 +122,7 @@ func (m *Multicast) _start() error { // IsStarted returns true if the module has been started. func (m *Multicast) IsStarted() bool { - var isOpen bool - phony.Block(m, func() { - isOpen = m._isOpen - }) - return isOpen + return m.running.Load() } // Stop stops the multicast module. @@ -136,8 +136,10 @@ func (m *Multicast) Stop() error { } func (m *Multicast) _stop() error { + if !m.running.CompareAndSwap(true, false) { + return nil + } m.log.Infoln("Stopping multicast module") - m._isOpen = false if m.sock != nil { m.sock.Close() } @@ -147,7 +149,8 @@ func (m *Multicast) _stop() error { func (m *Multicast) _updateInterfaces() { interfaces := m._getAllowedInterfaces() for name, info := range interfaces { - addrs, err := info.iface.Addrs() + // 'anet' package is used here to avoid https://github.com/golang/go/issues/40569 + addrs, err := anet.InterfaceAddrsByInterface(&info.iface) if err != nil { m.log.Warnf("Failed up get addresses for interface %s: %s", name, err) delete(interfaces, name) @@ -155,6 +158,7 @@ func (m *Multicast) _updateInterfaces() { } info.addrs = addrs interfaces[name] = info + m.log.Debugf("Discovered addresses for interface %s: %s", name, addrs) } m._interfaces = interfaces } @@ -173,10 +177,11 @@ func (m *Multicast) Interfaces() map[string]net.Interface { func (m *Multicast) _getAllowedInterfaces() map[string]*interfaceInfo { interfaces := make(map[string]*interfaceInfo) // Ask the system for network interfaces - allifaces, err := net.Interfaces() + // 'anet' package is used here to avoid https://github.com/golang/go/issues/40569 + allifaces, err := anet.Interfaces() if err != nil { // Don't panic, since this may be from e.g. too many open files (from too much connection spam) - // TODO? log something + m.log.Debugf("Failed to get interfaces: %s", err) return nil } // Work out which interfaces to announce on @@ -233,7 +238,7 @@ func (m *Multicast) AnnounceNow() { } func (m *Multicast) _announce() { - if !m._isOpen { + if !m.running.Load() { return } m._updateInterfaces() @@ -250,7 +255,7 @@ func (m *Multicast) _announce() { for name, info := range m._listeners { // Prepare our stop function! stop := func() { - info.listener.Close() + info.listener.Cancel() delete(m._listeners, name) m.log.Debugln("No longer multicasting on", name) } @@ -376,6 +381,9 @@ func (m *Multicast) listen() { bs := make([]byte, 2048) hb := make([]byte, 0, blake2b.Size) // Reused to reduce hash allocations for { + if !m.running.Load() { + return + } n, rcm, fromAddr, err := m.sock.ReadFrom(bs) if err != nil { if !m.IsStarted() { diff --git a/src/multicast/multicast_darwin_cgo.go b/src/multicast/multicast_darwin_cgo.go index 5c2af7ab..16266381 100644 --- a/src/multicast/multicast_darwin_cgo.go +++ b/src/multicast/multicast_darwin_cgo.go @@ -31,7 +31,7 @@ import ( ) func (m *Multicast) _multicastStarted() { - if !m._isOpen { + if !m.running.Load() { return } C.StopAWDLBrowsing() diff --git a/src/tun/tun.go b/src/tun/tun.go index e6795c64..e7ba3a0f 100644 --- a/src/tun/tun.go +++ b/src/tun/tun.go @@ -11,7 +11,6 @@ import ( "io" "net" "sync" - "time" "github.com/Arceliar/phony" wgtun "golang.zx2c4.com/wireguard/tun" @@ -65,20 +64,6 @@ func getSupportedMTU(mtu uint64) uint64 { 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 // return a canonical adapter name instead. func (tun *TunAdapter) Name() string { diff --git a/src/tun/tun_bsd.go b/src/tun/tun_bsd.go index 7f26260c..da5b3297 100644 --- a/src/tun/tun_bsd.go +++ b/src/tun/tun_bsd.go @@ -80,9 +80,6 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error { if err != nil { 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 if mtu, err := iface.MTU(); err == nil { tun.mtu = getSupportedMTU(uint64(mtu)) diff --git a/src/tun/tun_darwin.go b/src/tun/tun_darwin.go index a9d734bb..deeb115e 100644 --- a/src/tun/tun_darwin.go +++ b/src/tun/tun_darwin.go @@ -27,9 +27,6 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error { if err != nil { 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 if m, err := iface.MTU(); err == nil { tun.mtu = getSupportedMTU(uint64(m)) @@ -58,9 +55,6 @@ func (tun *TunAdapter) setupFD(fd int32, addr string, mtu uint64) error { unix.Close(dfd) 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 if m, err := iface.MTU(); err == nil { tun.mtu = getSupportedMTU(uint64(m)) diff --git a/src/tun/tun_linux.go b/src/tun/tun_linux.go index c98cdd73..98d63db4 100644 --- a/src/tun/tun_linux.go +++ b/src/tun/tun_linux.go @@ -21,9 +21,6 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error { if err != nil { 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 if mtu, err := iface.MTU(); err == nil { tun.mtu = getSupportedMTU(uint64(mtu)) diff --git a/src/tun/tun_other.go b/src/tun/tun_other.go index fc940818..0ddd0c1e 100644 --- a/src/tun/tun_other.go +++ b/src/tun/tun_other.go @@ -18,9 +18,6 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error { if err != nil { 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 if mtu, err := iface.MTU(); err == nil { tun.mtu = getSupportedMTU(uint64(mtu)) diff --git a/src/tun/tun_windows.go b/src/tun/tun_windows.go index 7bcdb7ac..a2861894 100644 --- a/src/tun/tun_windows.go +++ b/src/tun/tun_windows.go @@ -8,10 +8,12 @@ import ( "fmt" "log" "net/netip" + "time" "github.com/yggdrasil-network/yggdrasil-go/src/config" "golang.org/x/sys/windows" + "golang.zx2c4.com/wintun" wgtun "golang.zx2c4.com/wireguard/tun" "golang.zx2c4.com/wireguard/windows/elevate" "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg" @@ -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 { return err } - if iface, err = wgtun.CreateTUNWithRequestedGUID(ifname, &guid, int(mtu)); err != nil { - return err - } - if !waitForTUNUp(iface.Events()) { - return fmt.Errorf("TUN did not come up in time") + iface, err = wgtun.CreateTUNWithRequestedGUID(ifname, &guid, int(mtu)) + if err != nil { + // Very rare condition, it will purge the old device and create new + tun.log.Printf("Error creating TUN: '%s'", err) + wintun.Uninstall() + time.Sleep(3 * time.Second) + tun.log.Printf("Trying again") + iface, err = wgtun.CreateTUNWithRequestedGUID(ifname, &guid, int(mtu)) + if err != nil { + return err + } } + tun.log.Printf("Waiting for TUN to come up") + time.Sleep(1 * time.Second) tun.iface = iface if addr != "" { + tun.log.Printf("Setting up address") if err = tun.setupAddress(addr); err != nil { tun.log.Errorln("Failed to set up TUN address:", err) return err @@ -51,6 +62,7 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error { if mtu, err := iface.MTU(); err == nil { tun.mtu = uint64(mtu) } + tun.log.Printf("TUN is set up successfully") return nil }) }