aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/tunnel/service.go
diff options
context:
space:
mode:
Diffstat (limited to 'tunnel/service.go')
-rw-r--r--tunnel/service.go183
1 files changed, 94 insertions, 89 deletions
diff --git a/tunnel/service.go b/tunnel/service.go
index e535894b..a56ed1f3 100644
--- a/tunnel/service.go
+++ b/tunnel/service.go
@@ -1,33 +1,27 @@
/* SPDX-License-Identifier: MIT
*
- * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
+ * Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved.
*/
package tunnel
import (
- "bufio"
"bytes"
"fmt"
"log"
- "net"
"os"
"runtime"
- "runtime/debug"
- "strings"
"time"
+ "golang.org/x/sys/windows"
"golang.org/x/sys/windows/svc"
"golang.org/x/sys/windows/svc/mgr"
- "golang.zx2c4.com/wireguard/device"
- "golang.zx2c4.com/wireguard/ipc"
- "golang.zx2c4.com/wireguard/tun"
-
"golang.zx2c4.com/wireguard/windows/conf"
+ "golang.zx2c4.com/wireguard/windows/driver"
"golang.zx2c4.com/wireguard/windows/elevate"
"golang.zx2c4.com/wireguard/windows/ringlogger"
"golang.zx2c4.com/wireguard/windows/services"
- "golang.zx2c4.com/wireguard/windows/version"
+ "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
)
type tunnelService struct {
@@ -35,12 +29,13 @@ type tunnelService struct {
}
func (service *tunnelService) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (svcSpecificEC bool, exitCode uint32) {
- changes <- svc.Status{State: svc.StartPending}
+ serviceState := svc.StartPending
+ changes <- svc.Status{State: serviceState}
- var dev *device.Device
- var uapi net.Listener
var watcher *interfaceWatcher
- var nativeTun *tun.NativeTun
+ var adapter *driver.Adapter
+ var luid winipcfg.LUID
+ var config *conf.Config
var err error
serviceError := services.ErrorSuccess
@@ -50,7 +45,8 @@ func (service *tunnelService) Execute(args []string, r <-chan svc.ChangeRequest,
if logErr != nil {
log.Println(logErr)
}
- changes <- svc.Status{State: svc.StopPending}
+ serviceState = svc.StopPending
+ changes <- svc.Status{State: serviceState}
stopIt := make(chan bool, 1)
go func() {
@@ -84,65 +80,63 @@ func (service *tunnelService) Execute(args []string, r <-chan svc.ChangeRequest,
}
}()
+ if logErr == nil && adapter != nil && config != nil {
+ logErr = runScriptCommand(config.Interface.PreDown, config.Name)
+ }
if watcher != nil {
watcher.Destroy()
}
- if uapi != nil {
- uapi.Close()
+ if adapter != nil {
+ adapter.Close()
}
- if dev != nil {
- dev.Close()
+ if logErr == nil && adapter != nil && config != nil {
+ _ = runScriptCommand(config.Interface.PostDown, config.Name)
}
stopIt <- true
log.Println("Shutting down")
}()
- err = ringlogger.InitGlobalLogger("TUN")
+ var logFile string
+ logFile, err = conf.LogFile(true)
if err != nil {
serviceError = services.ErrorRingloggerOpen
return
}
- defer func() {
- if x := recover(); x != nil {
- for _, line := range append([]string{fmt.Sprint(x)}, strings.Split(string(debug.Stack()), "\n")...) {
- if len(strings.TrimSpace(line)) > 0 {
- log.Println(line)
- }
- }
- panic(x)
- }
- }()
-
- conf, err := conf.LoadFromPath(service.Path)
+ err = ringlogger.InitGlobalLogger(logFile, "TUN")
if err != nil {
- serviceError = services.ErrorLoadConfiguration
+ serviceError = services.ErrorRingloggerOpen
return
}
- conf.DeduplicateNetworkEntries()
- err = CopyConfigOwnerToIPCSecurityDescriptor(service.Path)
+
+ config, err = conf.LoadFromPath(service.Path)
if err != nil {
serviceError = services.ErrorLoadConfiguration
return
}
+ config.DeduplicateNetworkEntries()
- logPrefix := fmt.Sprintf("[%s] ", conf.Name)
- log.SetPrefix(logPrefix)
+ log.SetPrefix(fmt.Sprintf("[%s] ", config.Name))
- log.Println("Starting", version.UserAgent())
+ services.PrintStarting()
- if m, err := mgr.Connect(); err == nil {
- if lockStatus, err := m.LockStatus(); err == nil && lockStatus.IsLocked {
- /* If we don't do this, then the Wintun installation will block forever, because
- * installing a Wintun device starts a service too. Apparently at boot time, Windows
- * 8.1 locks the SCM for each service start, creating a deadlock if we don't announce
- * that we're running before starting additional services.
- */
- log.Printf("SCM locked for %v by %s, marking service as started", lockStatus.Age, lockStatus.Owner)
- changes <- svc.Status{State: svc.Running}
+ if services.StartedAtBoot() {
+ if m, err := mgr.Connect(); err == nil {
+ if lockStatus, err := m.LockStatus(); err == nil && lockStatus.IsLocked {
+ /* If we don't do this, then the driver installation will block forever, because
+ * installing a network adapter starts the driver service too. Apparently at boot time,
+ * Windows 8.1 locks the SCM for each service start, creating a deadlock if we don't
+ * announce that we're running before starting additional services.
+ */
+ log.Printf("SCM locked for %v by %s, marking service as started", lockStatus.Age, lockStatus.Owner)
+ serviceState = svc.Running
+ changes <- svc.Status{State: serviceState}
+ }
+ m.Disconnect()
}
- m.Disconnect()
}
+ evaluateStaticPitfalls()
+
log.Println("Watching network interfaces")
watcher, err = watchInterface()
if err != nil {
@@ -151,28 +145,49 @@ func (service *tunnelService) Execute(args []string, r <-chan svc.ChangeRequest,
}
log.Println("Resolving DNS names")
- uapiConf, err := conf.ToUAPI()
+ err = config.ResolveEndpoints()
if err != nil {
serviceError = services.ErrorDNSLookup
return
}
- log.Println("Creating Wintun interface")
- wintun, err := tun.CreateTUNWithRequestedGUID(conf.Name, deterministicGUID(conf), 0)
+ log.Println("Creating network adapter")
+ for i := 0; i < 15; i++ {
+ if i > 0 {
+ time.Sleep(time.Second)
+ log.Printf("Retrying adapter creation after failure because system just booted (T+%v): %v", windows.DurationSinceBoot(), err)
+ }
+ adapter, err = driver.CreateAdapter(config.Name, "WireGuard", deterministicGUID(config))
+ if err == nil || !services.StartedAtBoot() {
+ break
+ }
+ }
if err != nil {
- serviceError = services.ErrorCreateWintun
+ err = fmt.Errorf("Error creating adapter: %w", err)
+ serviceError = services.ErrorCreateNetworkAdapter
return
}
- nativeTun = wintun.(*tun.NativeTun)
- wintunVersion, ndisVersion, err := nativeTun.Version()
+ luid = adapter.LUID()
+ driverVersion, err := driver.RunningVersion()
if err != nil {
- log.Printf("Warning: unable to determine Wintun version: %v", err)
+ log.Printf("Warning: unable to determine driver version: %v", err)
} else {
- log.Printf("Using Wintun/%s (NDIS %s)", wintunVersion, ndisVersion)
+ log.Printf("Using WireGuardNT/%d.%d", (driverVersion>>16)&0xffff, driverVersion&0xffff)
+ }
+ err = adapter.SetLogging(driver.AdapterLogOn)
+ if err != nil {
+ err = fmt.Errorf("Error enabling adapter logging: %w", err)
+ serviceError = services.ErrorCreateNetworkAdapter
+ return
}
- log.Println("Enabling firewall rules")
- err = enableFirewall(conf, nativeTun)
+ err = runScriptCommand(config.Interface.PreUp, config.Name)
+ if err != nil {
+ serviceError = services.ErrorRunScript
+ return
+ }
+
+ err = enableFirewall(config, luid)
if err != nil {
serviceError = services.ErrorFirewall
return
@@ -185,43 +200,28 @@ func (service *tunnelService) Execute(args []string, r <-chan svc.ChangeRequest,
return
}
- log.Println("Creating interface instance")
- logOutput := log.New(ringlogger.Global, logPrefix, 0)
- logger := &device.Logger{logOutput, logOutput, logOutput}
- dev = device.NewDevice(wintun, logger)
-
log.Println("Setting interface configuration")
- uapi, err = ipc.UAPIListen(conf.Name)
+ err = adapter.SetConfiguration(config.ToDriverConfiguration())
if err != nil {
- serviceError = services.ErrorUAPIListen
+ serviceError = services.ErrorDeviceSetConfig
return
}
- ipcErr := dev.IpcSetOperation(bufio.NewReader(strings.NewReader(uapiConf)))
- if ipcErr != nil {
- err = ipcErr
- serviceError = services.ErrorDeviceSetConfig
+ err = adapter.SetAdapterState(driver.AdapterStateUp)
+ if err != nil {
+ serviceError = services.ErrorDeviceBringUp
return
}
+ watcher.Configure(adapter, config, luid)
- log.Println("Bringing peers up")
- dev.Up()
-
- watcher.Configure(dev, conf, nativeTun)
-
- log.Println("Listening for UAPI requests")
- go func() {
- for {
- conn, err := uapi.Accept()
- if err != nil {
- continue
- }
- go dev.IpcHandle(conn)
- }
- }()
+ err = runScriptCommand(config.Interface.PostUp, config.Name)
+ if err != nil {
+ serviceError = services.ErrorRunScript
+ return
+ }
- changes <- svc.Status{State: svc.Running, Accepts: svc.AcceptStop | svc.AcceptShutdown}
- log.Println("Startup complete")
+ changes <- svc.Status{State: serviceState, Accepts: svc.AcceptStop | svc.AcceptShutdown}
+ var started bool
for {
select {
case c := <-r:
@@ -233,8 +233,13 @@ func (service *tunnelService) Execute(args []string, r <-chan svc.ChangeRequest,
default:
log.Printf("Unexpected service control request #%d\n", c)
}
- case <-dev.Wait():
- return
+ case <-watcher.started:
+ if !started {
+ serviceState = svc.Running
+ changes <- svc.Status{State: serviceState, Accepts: svc.AcceptStop | svc.AcceptShutdown}
+ log.Println("Startup complete")
+ started = true
+ }
case e := <-watcher.errors:
serviceError, err = e.serviceError, e.err
return
@@ -247,7 +252,7 @@ func Run(confPath string) error {
if err != nil {
return err
}
- serviceName, err := services.ServiceNameOfTunnel(name)
+ serviceName, err := conf.ServiceNameOfTunnel(name)
if err != nil {
return err
}