From ed601b493d7262a93bff9170ac706d19881f2417 Mon Sep 17 00:00:00 2001 From: jyyi1 Date: Fri, 6 Dec 2024 18:30:06 -0500 Subject: [PATCH] Wait for device to be available --- client/electron/vpn_service.ts | 6 +++--- client/go/outline/device.go | 6 ++++-- client/go/outline/method_channel.go | 2 +- client/go/outline/vpn/nmconn_linux.go | 31 +++++++++++++++++++-------- 4 files changed, 30 insertions(+), 15 deletions(-) diff --git a/client/electron/vpn_service.ts b/client/electron/vpn_service.ts index 772e6cdfd5..6c8f65c8f2 100644 --- a/client/electron/vpn_service.ts +++ b/client/electron/vpn_service.ts @@ -40,7 +40,7 @@ export async function establishVpn(request: StartRequestJson) { const config: EstablishVpnRequest = { id: currentRequestId, - // TUN device name, compatible with old code: + // TUN device name, being compatible with old code: // https://github.com/Jigsaw-Code/outline-apps/blob/client/linux/v1.14.0/client/electron/linux_proxy_controller/outline_proxy_controller.h#L203 interfaceName: 'outline-tun0', @@ -48,11 +48,11 @@ export async function establishVpn(request: StartRequestJson) { // because Network Manager has a dedicated "VPN Connection" concept that we did not implement connectionName: 'Outline TUN Connection', - // TUN IP, compatible with old code: + // TUN IP, being compatible with old code: // https://github.com/Jigsaw-Code/outline-apps/blob/client/linux/v1.14.0/client/electron/linux_proxy_controller/outline_proxy_controller.h#L204 ipAddress: '10.0.85.1', - // DNS server list, compatible with old code: + // DNS server list, being compatible with old code: // https://github.com/Jigsaw-Code/outline-apps/blob/client/linux/v1.14.0/client/electron/linux_proxy_controller/outline_proxy_controller.h#L207 dnsServers: ['9.9.9.9'], diff --git a/client/go/outline/device.go b/client/go/outline/device.go index 3cacc86571..ef844a3129 100644 --- a/client/go/outline/device.go +++ b/client/go/outline/device.go @@ -86,8 +86,10 @@ func (d *Device) Close() (err error) { } func (d *Device) RefreshConnectivity() (err error) { + slog.Debug("[Outine] Testing connectivity of Outline server ...") result := CheckTCPAndUDPConnectivity(d.c) if result.TCPError != nil { + slog.Warn("[Outline] Outline server connectivity test failed", "err", result.TCPError) return result.TCPError } @@ -97,7 +99,7 @@ func (d *Device) RefreshConnectivity() (err error) { slog.Warn("[Outline] server cannot handle UDP traffic", "err", result.UDPError) proxy = d.fallback } else { - slog.Info("[Outline] server can handle UDP traffic") + slog.Debug("[Outline] server can handle UDP traffic") proxy = d.remote canHandleUDP = true } @@ -112,7 +114,7 @@ func (d *Device) RefreshConnectivity() (err error) { } } d.supportsUDP = &canHandleUDP - slog.Info("[OutlineNetDev] UDP handler refreshed", "supportsUDP", canHandleUDP) + slog.Info("[Outline] Outline server connectivity test done", "supportsUDP", canHandleUDP) return nil } diff --git a/client/go/outline/method_channel.go b/client/go/outline/method_channel.go index d43a34b097..61daceaf5c 100644 --- a/client/go/outline/method_channel.go +++ b/client/go/outline/method_channel.go @@ -45,7 +45,7 @@ const ( type Handler func(string) (string, error) // handlers is a map of registered handlers. -var handlers map[string]Handler +var handlers = make(map[string]Handler) // RegisterMethodHandler registers a native function handler for the given method. // diff --git a/client/go/outline/vpn/nmconn_linux.go b/client/go/outline/vpn/nmconn_linux.go index ecbbd69189..9a6e79612a 100644 --- a/client/go/outline/vpn/nmconn_linux.go +++ b/client/go/outline/vpn/nmconn_linux.go @@ -18,6 +18,7 @@ import ( "encoding/binary" "log/slog" "net" + "time" gonm "github.com/Wifx/gonetworkmanager/v2" "golang.org/x/sys/unix" @@ -45,7 +46,7 @@ func (c *linuxVPNConn) establishNMConnection() (err error) { } slog.Debug(nmLogPfx + "connected") - dev, err := c.nm.GetDeviceByIpIface(c.nmOpts.TUNName) + dev, err := c.waitForDeviceToBeAvailable() if err != nil { return errSetupVPN(nmLogPfx, "failed to find TUN device", err, "tun", c.nmOpts.TUNName) } @@ -94,6 +95,18 @@ func (c *linuxVPNConn) closeNMConnection() error { return nil } +func (c *linuxVPNConn) waitForDeviceToBeAvailable() (dev gonm.Device, err error) { + for retries := 20; retries > 0; retries-- { + dev, err = c.nm.GetDeviceByIpIface(c.nmOpts.TUNName) + if dev != nil && err == nil { + return + } + slog.Warn(nmLogPfx+"waiting for TUN device to be available", "err", err) + time.Sleep(50 * time.Millisecond) + } + return nil, errSetupVPN(nmLogPfx, "failed to find TUN device", err, "tun", c.nmOpts.TUNName) +} + func configureCommonProps(props map[string]map[string]interface{}, opts *nmConnectionOptions) { props["connection"] = map[string]interface{}{ "id": opts.Name, @@ -102,20 +115,20 @@ func configureCommonProps(props map[string]map[string]interface{}, opts *nmConne } func configureTUNProps(props map[string]map[string]interface{}) { - props["tun"] = make(map[string]interface{}) - - // The operating mode of the virtual device. - // Allowed values are 1 (tun) to create a layer 3 device and 2 (tap) to create an Ethernet-like layer 2 one. - props["tun"]["mode"] = uint32(1) + props["tun"] = map[string]interface{}{ + // The operating mode of the virtual device. + // Allowed values are 1 (tun) to create a layer 3 device and 2 (tap) to create an Ethernet-like layer 2 one. + "mode": uint32(1), + } } func configureIPv4Props(props map[string]map[string]interface{}, opts *nmConnectionOptions) { - props["ipv4"] = make(map[string]interface{}) - props["ipv4"]["method"] = "manual" + props["ipv4"] = map[string]interface{}{ + "method": "manual", + } // Array of IPv4 addresses. Each address dictionary contains at least 'address' and 'prefix' entries, // containing the IP address as a string, and the prefix length as a uint32. - addr := make(map[string]interface{}) addr["address"] = opts.TUNAddr.To4().String() addr["prefix"] = uint32(32)