1
0
mirror of https://git.zx2c4.com/wireguard-windows synced 2024-11-10 16:59:18 +00:00
wireguard-windows/tunnel/interfacewatcher.go
Jason A. Donenfeld 6ed37f30f5 global: bump date
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2022-01-06 17:28:13 +01:00

192 lines
5.3 KiB
Go

/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved.
*/
package tunnel
import (
"errors"
"fmt"
"log"
"sync"
"time"
"golang.org/x/sys/windows"
"golang.zx2c4.com/wireguard/windows/conf"
"golang.zx2c4.com/wireguard/windows/driver"
"golang.zx2c4.com/wireguard/windows/services"
"golang.zx2c4.com/wireguard/windows/tunnel/firewall"
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
)
type interfaceWatcherError struct {
serviceError services.Error
err error
}
type interfaceWatcherEvent struct {
luid winipcfg.LUID
family winipcfg.AddressFamily
}
type interfaceWatcher struct {
errors chan interfaceWatcherError
started chan winipcfg.AddressFamily
conf *conf.Config
adapter *driver.Adapter
luid winipcfg.LUID
setupMutex sync.Mutex
interfaceChangeCallback winipcfg.ChangeCallback
changeCallbacks4 []winipcfg.ChangeCallback
changeCallbacks6 []winipcfg.ChangeCallback
storedEvents []interfaceWatcherEvent
watchdog *time.Timer
}
func (iw *interfaceWatcher) setup(family winipcfg.AddressFamily) {
iw.watchdog.Stop()
var changeCallbacks *[]winipcfg.ChangeCallback
var ipversion string
if family == windows.AF_INET {
changeCallbacks = &iw.changeCallbacks4
ipversion = "v4"
} else if family == windows.AF_INET6 {
changeCallbacks = &iw.changeCallbacks6
ipversion = "v6"
} else {
return
}
if len(*changeCallbacks) != 0 {
for _, cb := range *changeCallbacks {
cb.Unregister()
}
*changeCallbacks = nil
}
var err error
if iw.conf.Interface.MTU == 0 {
log.Printf("Monitoring MTU of default %s routes", ipversion)
*changeCallbacks, err = monitorMTU(family, iw.luid)
if err != nil {
iw.errors <- interfaceWatcherError{services.ErrorMonitorMTUChanges, err}
return
}
}
log.Printf("Setting device %s addresses", ipversion)
err = configureInterface(family, iw.conf, iw.luid)
if err != nil {
iw.errors <- interfaceWatcherError{services.ErrorSetNetConfig, err}
return
}
evaluateDynamicPitfalls(family, iw.conf, iw.luid)
iw.started <- family
}
func watchInterface() (*interfaceWatcher, error) {
iw := &interfaceWatcher{
errors: make(chan interfaceWatcherError, 2),
started: make(chan winipcfg.AddressFamily, 4),
}
iw.watchdog = time.AfterFunc(time.Duration(1<<63-1), func() {
iw.errors <- interfaceWatcherError{services.ErrorCreateNetworkAdapter, errors.New("TCP/IP interface for adapter did not appear after one minute")}
})
iw.watchdog.Stop()
var err error
iw.interfaceChangeCallback, err = winipcfg.RegisterInterfaceChangeCallback(func(notificationType winipcfg.MibNotificationType, iface *winipcfg.MibIPInterfaceRow) {
iw.setupMutex.Lock()
defer iw.setupMutex.Unlock()
if notificationType != winipcfg.MibAddInstance {
return
}
if iw.luid == 0 {
iw.storedEvents = append(iw.storedEvents, interfaceWatcherEvent{iface.InterfaceLUID, iface.Family})
return
}
if iface.InterfaceLUID != iw.luid {
return
}
iw.setup(iface.Family)
if state, err := iw.adapter.AdapterState(); err == nil && state == driver.AdapterStateDown {
log.Println("Reinitializing adapter configuration")
err = iw.adapter.SetConfiguration(iw.conf.ToDriverConfiguration())
if err != nil {
log.Println(fmt.Errorf("%v: %w", services.ErrorDeviceSetConfig, err))
}
err = iw.adapter.SetAdapterState(driver.AdapterStateUp)
if err != nil {
log.Println(fmt.Errorf("%v: %w", services.ErrorDeviceBringUp, err))
}
}
})
if err != nil {
return nil, fmt.Errorf("unable to register interface change callback: %w", err)
}
return iw, nil
}
func (iw *interfaceWatcher) Configure(adapter *driver.Adapter, conf *conf.Config, luid winipcfg.LUID) {
iw.setupMutex.Lock()
defer iw.setupMutex.Unlock()
iw.watchdog.Reset(time.Minute)
iw.adapter, iw.conf, iw.luid = adapter, conf, luid
for _, event := range iw.storedEvents {
if event.luid == luid {
iw.setup(event.family)
}
}
iw.storedEvents = nil
}
func (iw *interfaceWatcher) Destroy() {
iw.setupMutex.Lock()
iw.watchdog.Stop()
changeCallbacks4 := iw.changeCallbacks4
changeCallbacks6 := iw.changeCallbacks6
interfaceChangeCallback := iw.interfaceChangeCallback
luid := iw.luid
iw.setupMutex.Unlock()
if interfaceChangeCallback != nil {
interfaceChangeCallback.Unregister()
}
for _, cb := range changeCallbacks4 {
cb.Unregister()
}
for _, cb := range changeCallbacks6 {
cb.Unregister()
}
iw.setupMutex.Lock()
if interfaceChangeCallback == iw.interfaceChangeCallback {
iw.interfaceChangeCallback = nil
}
for len(changeCallbacks4) > 0 && len(iw.changeCallbacks4) > 0 {
iw.changeCallbacks4 = iw.changeCallbacks4[1:]
changeCallbacks4 = changeCallbacks4[1:]
}
for len(changeCallbacks6) > 0 && len(iw.changeCallbacks6) > 0 {
iw.changeCallbacks6 = iw.changeCallbacks6[1:]
changeCallbacks6 = changeCallbacks6[1:]
}
firewall.DisableFirewall()
if luid != 0 && iw.luid == luid {
// It seems that the Windows networking stack doesn't like it when we destroy interfaces that have active
// routes, so to be certain, just remove everything before destroying.
luid.FlushRoutes(windows.AF_INET)
luid.FlushIPAddresses(windows.AF_INET)
luid.FlushDNS(windows.AF_INET)
luid.FlushRoutes(windows.AF_INET6)
luid.FlushIPAddresses(windows.AF_INET6)
luid.FlushDNS(windows.AF_INET6)
}
iw.setupMutex.Unlock()
}