mirror of https://github.com/tailscale/tailscale/
wgengine: make NewUserspaceEngine wait for TUN interface to be up on Windows
Updates #474 Signed-off-by: Aleksandar Pesic <peske.nis@gmail.com>pull/1302/head
parent
ea3715e3ce
commit
daf6de4f14
@ -0,0 +1,19 @@
|
||||
// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !windows
|
||||
|
||||
package wgengine
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/tailscale/wireguard-go/tun"
|
||||
"tailscale.com/types/logger"
|
||||
)
|
||||
|
||||
// Dummy implementation that does nothing.
|
||||
func waitInterfaceUp(iface tun.Device, timeout time.Duration, logf logger.Logf) error {
|
||||
return nil
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package wgengine
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/tailscale/wireguard-go/tun"
|
||||
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
|
||||
"tailscale.com/types/logger"
|
||||
)
|
||||
|
||||
// ifaceWatcher waits for an interface to be up.
|
||||
type ifaceWatcher struct {
|
||||
logf logger.Logf
|
||||
luid winipcfg.LUID
|
||||
|
||||
mu sync.Mutex // guards following
|
||||
done bool
|
||||
sig chan bool
|
||||
}
|
||||
|
||||
// callback is the callback we register with Windows to call when IP interface changes.
|
||||
func (iw *ifaceWatcher) callback(notificationType winipcfg.MibNotificationType, iface *winipcfg.MibIPInterfaceRow) {
|
||||
// Probably should check only when MibParameterNotification, but just in case included MibAddInstance also.
|
||||
if notificationType == winipcfg.MibParameterNotification || notificationType == winipcfg.MibAddInstance {
|
||||
// Out of paranoia, start a goroutine to finish our work, to return to Windows out of this callback.
|
||||
go iw.isUp()
|
||||
}
|
||||
}
|
||||
|
||||
func (iw *ifaceWatcher) isUp() bool {
|
||||
iw.mu.Lock()
|
||||
defer iw.mu.Unlock()
|
||||
|
||||
if iw.done {
|
||||
// We already know that it's up
|
||||
return true
|
||||
}
|
||||
|
||||
if iw.getOperStatus() != winipcfg.IfOperStatusUp {
|
||||
return false
|
||||
}
|
||||
|
||||
iw.done = true
|
||||
iw.sig <- true
|
||||
return true
|
||||
}
|
||||
|
||||
func (iw *ifaceWatcher) getOperStatus() winipcfg.IfOperStatus {
|
||||
ifc, err := iw.luid.Interface()
|
||||
if err != nil {
|
||||
iw.logf("iw.luid.Interface error: %v", err)
|
||||
return 0
|
||||
}
|
||||
return ifc.OperStatus
|
||||
}
|
||||
|
||||
func waitInterfaceUp(iface tun.Device, timeout time.Duration, logf logger.Logf) error {
|
||||
iw := &ifaceWatcher{
|
||||
luid: winipcfg.LUID(iface.(*tun.NativeTun).LUID()),
|
||||
logf: logger.WithPrefix(logf, "waitInterfaceUp: "),
|
||||
}
|
||||
|
||||
// Just in case check the status first
|
||||
if iw.getOperStatus() == winipcfg.IfOperStatusUp {
|
||||
iw.logf("TUN interface already up; no need to wait")
|
||||
return nil
|
||||
}
|
||||
|
||||
iw.sig = make(chan bool, 1)
|
||||
cb, err := winipcfg.RegisterInterfaceChangeCallback(iw.callback)
|
||||
if err != nil {
|
||||
iw.logf("RegisterInterfaceChangeCallback error: %v", err)
|
||||
return err
|
||||
}
|
||||
defer cb.Unregister()
|
||||
|
||||
t0 := time.Now()
|
||||
expires := t0.Add(timeout)
|
||||
ticker := time.NewTicker(10 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
iw.logf("waiting for TUN interface to come up...")
|
||||
|
||||
select {
|
||||
case <-iw.sig:
|
||||
iw.logf("TUN interface is up after %v", time.Since(t0))
|
||||
return nil
|
||||
case <-ticker.C:
|
||||
break
|
||||
}
|
||||
|
||||
if iw.isUp() {
|
||||
// Very unlikely to happen - either NotifyIpInterfaceChange doesn't work
|
||||
// or it came up in the same moment as tick. Indicate this in the log message.
|
||||
iw.logf("TUN interface is up after %v (on poll, without notification)", time.Since(t0))
|
||||
return nil
|
||||
}
|
||||
|
||||
if expires.Before(time.Now()) {
|
||||
iw.logf("timeout waiting %v for TUN interface to come up", timeout)
|
||||
return fmt.Errorf("timeout waiting for TUN interface to come up")
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue