add other executable that starts to emulate wireguard running as a service

This commit is contained in:
dovholuknf
2020-04-07 17:56:01 -04:00
parent b496f6caf7
commit c6efa3b2ce
4 changed files with 686 additions and 8 deletions
+210 -8
View File
@@ -1,17 +1,24 @@
package main
import (
"bufio"
"fmt"
"log"
"strings"
"wintun-testing/wireguard"
"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/tunnel/winipcfg"
"net"
"os"
"os/signal"
user2 "os/user"
"syscall"
"golang.zx2c4.com/wireguard/windows/conf"
"golang.zx2c4.com/wireguard/windows/elevate"
"golang.zx2c4.com/wireguard/windows/ringlogger"
"golang.zx2c4.com/wireguard/windows/services"
"golang.zx2c4.com/wireguard/windows/version"
)
const (
@@ -20,8 +27,176 @@ const (
)
func main(){
var r <-chan svc.ChangeRequest
var changes chan<- svc.Status
confPath := "c:\\git\\github\\ziti-windows-uwp\\wintun-testing\\simple.conf"
interfaceName := "ZitiTUN"
serviceError := services.ErrorSuccess
conf, err := conf.LoadFromPath(confPath)
if err != nil {
serviceError = services.ErrorLoadConfiguration
return
}
conf.DeduplicateNetworkEntries()
logPrefix := fmt.Sprintf("[%s] ", conf.Name)
log.SetPrefix(logPrefix)
log.Println("Starting", version.UserAgent())
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}
}
m.Disconnect()
}
if serviceError != services.ErrorSuccess {
panic(serviceError)
}
var watcher *wireguard.InterfaceWatcher
log.Println("Watching network interfaces")
watcher, err = wireguard.WatchInterface()
if err != nil {
serviceError = services.ErrorSetNetConfig
return
}
log.Println("Resolving DNS names")
uapiConf, err := conf.ToUAPI()
if err != nil {
serviceError = services.ErrorDNSLookup
return
}
log.Println("Creating Wintun interface")
tunDevice, err := tun.CreateTUN(interfaceName, 0)
defer tunDevice.Close()
if err != nil {
serviceError = services.ErrorCreateWintun
return
}
nativeTun := tunDevice.(*tun.NativeTun)
wintunVersion, ndisVersion, err := nativeTun.Version()
if err != nil {
log.Printf("Warning: unable to determine Wintun version: %v", err)
} else {
log.Printf("Using Wintun/%s (NDIS %s)", wintunVersion, ndisVersion)
}
log.Println("Enabling firewall rules")
err = wireguard.EnableFirewall(conf, nativeTun)
if err != nil {
log.Printf("ERROR: %v", err)
serviceError = services.ErrorFirewall
return
}
log.Println("Enabling firewall rules")
err = wireguard.EnableFirewall(conf, nativeTun)
if err != nil {
serviceError = services.ErrorFirewall
return
}
log.Println("Dropping privileges")
err = elevate.DropAllPrivileges(true)
if err != nil {
serviceError = services.ErrorDropPrivileges
return
}
log.Println("Creating interface instance")
logOutput := log.New(ringlogger.Global, logPrefix, 0)
logger := &device.Logger{logOutput, logOutput, logOutput}
dev := device.NewDevice(tunDevice, logger)
log.Println("Setting interface configuration")
uapi, err := ipc.UAPIListen(conf.Name)
if err != nil {
serviceError = services.ErrorUAPIListen
return
}
ipcErr := dev.IpcSetOperation(bufio.NewReader(strings.NewReader(uapiConf)))
if ipcErr != nil {
err = ipcErr
serviceError = services.ErrorDeviceSetConfig
return
}
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)
}
}()
changes <- svc.Status{State: svc.Running, Accepts: svc.AcceptStop | svc.AcceptShutdown}
log.Println("Startup complete")
for {
select {
case c := <-r:
switch c.Cmd {
case svc.Stop, svc.Shutdown:
return
case svc.Interrogate:
changes <- c.CurrentStatus
default:
log.Printf("Unexpected service control request #%d\n", c)
}
case <-dev.Wait():
return
case e := <-watcher.Errors:
serviceError, err = e.ServiceError, e.Err
return
}
}
/*
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
"golang.zx2c4.com/wireguard/windows/version"
"log"
"net"
"os"
"os/signal"
user2 "os/user"
"strings"
"sync"
"syscall"
fmt.Printf("bootstrapped conf: %v", serviceName)
user,err := user2.Current()
fmt.Printf("Hi there: %v", user)
@@ -54,6 +229,33 @@ func main(){
nativeTunDevice := tunDevice.(*tun.NativeTun)
wintunVersion, ndisVersion, err := nativeTunDevice.Version()
if err != nil {
log.Printf("Warning: unable to determine Wintun version: %v", err)
} else {
log.Printf("Using Wintun/%s (NDIS %s)", wintunVersion, ndisVersion)
}
log.Println("Dropping privileges")
err = elevate.DropAllPrivileges(true)
if err != nil {
serviceError = services.ErrorDropPrivileges
return
}
log.Println("Creating interface instance")
logOutput := log.New(ringlogger.Global, logPrefix, 0)
logger := &device.Logger{logOutput, logOutput, logOutput}
dev := device.NewDevice(wintun, logger)
luid := winipcfg.LUID(nativeTunDevice.LUID())
//xxx ip, ipnet, _ := net.ParseCIDR("10.82.31.4/24")
ip, ipnet, _ := net.ParseCIDR("169.254.100.200/32")
@@ -113,10 +315,10 @@ func main(){
case <-errs:
case <-device.Wait():
}
*/
// clean up
logger.Info.Println("Shutting down")
// logger.Info.Println("Shutting down")
}
@@ -0,0 +1,30 @@
package wireguard
import (
"golang.zx2c4.com/wireguard/tun"
"golang.zx2c4.com/wireguard/windows/conf"
"golang.zx2c4.com/wireguard/windows/tunnel/firewall"
"log"
)
func EnableFirewall(conf *conf.Config, tun *tun.NativeTun) error {
restrictAll := false
if len(conf.Peers) == 1 {
nextallowedip:
for _, allowedip := range conf.Peers[0].AllowedIPs {
if allowedip.Cidr == 0 {
for _, b := range allowedip.IP {
if b != 0 {
continue nextallowedip
}
}
restrictAll = true
break
}
}
}
if restrictAll && len(conf.Interface.DNS) == 0 {
log.Println("Warning: no DNS server specified, despite having an allowed IPs of 0.0.0.0/0 or ::/0. There may be connectivity issues.")
}
return firewall.EnableFirewall(tun.LUID(), conf.Interface.DNS, restrictAll)
}
+9
View File
@@ -0,0 +1,9 @@
[Interface]
PrivateKey = 4GZLLHriLdsToVzCloLuQzK2zp3MWyYB5s/6MLB91Ew=
Address = 169.254.100.200/32
[Peer]
PublicKey = dPaSKJOyE5aVAof9hJh/NoPLCnbhG2APiGNS918HgRI=
AllowedIPs = 169.254.100.100
Endpoint = 192.168.1.115:51820
PersistentKeepalive = 30
+437
View File
@@ -0,0 +1,437 @@
package wireguard
import (
"bytes"
"golang.org/x/sys/windows"
"golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/tun"
"golang.zx2c4.com/wireguard/windows/conf"
"golang.zx2c4.com/wireguard/windows/services"
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
"log"
"net"
"sort"
"sync"
"time"
)
type InterfaceWatcherError struct {
ServiceError services.Error
Err error
}
type InterfaceWatcherEvent struct {
luid winipcfg.LUID
family winipcfg.AddressFamily
}
type InterfaceWatcher struct {
Errors chan InterfaceWatcherError
device *device.Device
conf *conf.Config
tun *tun.NativeTun
setupMutex sync.Mutex
interfaceChangeCallback winipcfg.ChangeCallback
changeCallbacks4 []winipcfg.ChangeCallback
changeCallbacks6 []winipcfg.ChangeCallback
storedEvents []InterfaceWatcherEvent
}
func WatchInterface() (*InterfaceWatcher, error) {
iw := &InterfaceWatcher{
Errors: make(chan InterfaceWatcherError, 2),
}
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.tun == nil {
iw.storedEvents = append(iw.storedEvents, InterfaceWatcherEvent{iface.InterfaceLUID, iface.Family})
return
}
if iface.InterfaceLUID != winipcfg.LUID(iw.tun.LUID()) {
return
}
iw.setup(iface.Family)
})
if err != nil {
return nil, err
}
return iw, nil
}
func (iw *InterfaceWatcher) setup(family winipcfg.AddressFamily) {
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
log.Printf("Monitoring default %s routes", ipversion)
*changeCallbacks, err = monitorDefaultRoutes(family, iw.device, iw.conf.Interface.MTU == 0, hasDefaultRoute(family, iw.conf.Peers), iw.tun)
if err != nil {
iw.Errors <- InterfaceWatcherError{services.ErrorBindSocketsToDefaultRoutes, err}
return
}
log.Printf("Setting device %s addresses", ipversion)
err = configureInterface(family, iw.conf, iw.tun)
if err != nil {
iw.Errors <- InterfaceWatcherError{services.ErrorSetNetConfig, err}
return
}
}
func monitorDefaultRoutes(family winipcfg.AddressFamily, device *device.Device, autoMTU bool, blackholeWhenLoop bool, tun *tun.NativeTun) ([]winipcfg.ChangeCallback, error) {
var minMTU uint32
if family == windows.AF_INET {
minMTU = 576
} else if family == windows.AF_INET6 {
minMTU = 1280
}
ourLUID := winipcfg.LUID(tun.LUID())
lastLUID := winipcfg.LUID(0)
lastIndex := ^uint32(0)
lastMTU := uint32(0)
doIt := func() error {
err := bindSocketRoute(family, device, ourLUID, &lastLUID, &lastIndex, blackholeWhenLoop)
if err != nil {
return err
}
if !autoMTU {
return nil
}
mtu := uint32(0)
if lastLUID != 0 {
iface, err := lastLUID.Interface()
if err != nil {
return err
}
if iface.MTU > 0 {
mtu = iface.MTU
}
}
if mtu > 0 && lastMTU != mtu {
iface, err := ourLUID.IPInterface(family)
if err != nil {
return err
}
iface.NLMTU = mtu - 80
if iface.NLMTU < minMTU {
iface.NLMTU = minMTU
}
err = iface.Set()
if err != nil {
return err
}
tun.ForceMTU(int(iface.NLMTU)) // TODO: having one MTU for both v4 and v6 kind of breaks the windows model, so right now this just gets the second one which is... bad.
lastMTU = mtu
}
return nil
}
err := doIt()
if err != nil {
return nil, err
}
firstBurst := time.Time{}
burstMutex := sync.Mutex{}
burstTimer := time.AfterFunc(time.Hour*200, func() {
burstMutex.Lock()
firstBurst = time.Time{}
doIt()
burstMutex.Unlock()
})
burstTimer.Stop()
bump := func() {
burstMutex.Lock()
burstTimer.Reset(time.Millisecond * 150)
if firstBurst.IsZero() {
firstBurst = time.Now()
} else if time.Since(firstBurst) > time.Second*2 {
firstBurst = time.Time{}
burstTimer.Stop()
doIt()
}
burstMutex.Unlock()
}
cbr, err := winipcfg.RegisterRouteChangeCallback(func(notificationType winipcfg.MibNotificationType, route *winipcfg.MibIPforwardRow2) {
if route != nil && route.DestinationPrefix.PrefixLength == 0 {
bump()
}
})
if err != nil {
return nil, err
}
cbi, err := winipcfg.RegisterInterfaceChangeCallback(func(notificationType winipcfg.MibNotificationType, iface *winipcfg.MibIPInterfaceRow) {
if notificationType == winipcfg.MibParameterNotification {
bump()
}
})
if err != nil {
cbr.Unregister()
return nil, err
}
return []winipcfg.ChangeCallback{cbr, cbi}, nil
}
func bindSocketRoute(family winipcfg.AddressFamily, device *device.Device, ourLUID winipcfg.LUID, lastLUID *winipcfg.LUID, lastIndex *uint32, blackholeWhenLoop bool) error {
r, err := winipcfg.GetIPForwardTable2(family)
if err != nil {
return err
}
lowestMetric := ^uint32(0)
index := uint32(0) // Zero is "unspecified", which for IP_UNICAST_IF resets the value, which is what we want.
luid := winipcfg.LUID(0) // Hopefully luid zero is unspecified, but hard to find docs saying so.
for i := range r {
if r[i].DestinationPrefix.PrefixLength != 0 || r[i].InterfaceLUID == ourLUID {
continue
}
ifrow, err := r[i].InterfaceLUID.Interface()
if err != nil || ifrow.OperStatus != winipcfg.IfOperStatusUp {
continue
}
if r[i].Metric < lowestMetric {
lowestMetric = r[i].Metric
index = r[i].InterfaceIndex
luid = r[i].InterfaceLUID
}
}
if luid == *lastLUID && index == *lastIndex {
return nil
}
*lastLUID = luid
*lastIndex = index
blackhole := blackholeWhenLoop && index == 0
if family == windows.AF_INET {
log.Printf("Binding v4 socket to interface %d (blackhole=%v)", index, blackhole)
return device.BindSocketToInterface4(index, blackhole)
} else if family == windows.AF_INET6 {
log.Printf("Binding v6 socket to interface %d (blackhole=%v)", index, blackhole)
return device.BindSocketToInterface6(index, blackhole)
}
return nil
}
func configureInterface(family winipcfg.AddressFamily, conf *conf.Config, tun *tun.NativeTun) error {
luid := winipcfg.LUID(tun.LUID())
estimatedRouteCount := 0
for _, peer := range conf.Peers {
estimatedRouteCount += len(peer.AllowedIPs)
}
routes := make([]winipcfg.RouteData, 0, estimatedRouteCount)
addresses := make([]net.IPNet, len(conf.Interface.Addresses))
var haveV4Address, haveV6Address bool
for i, addr := range conf.Interface.Addresses {
addresses[i] = addr.IPNet()
if addr.Bits() == 32 {
haveV4Address = true
} else if addr.Bits() == 128 {
haveV6Address = true
}
}
foundDefault4 := false
foundDefault6 := false
for _, peer := range conf.Peers {
for _, allowedip := range peer.AllowedIPs {
if (allowedip.Bits() == 32 && !haveV4Address) || (allowedip.Bits() == 128 && !haveV6Address) {
continue
}
route := winipcfg.RouteData{
Destination: allowedip.IPNet(),
Metric: 0,
}
if allowedip.Bits() == 32 {
if allowedip.Cidr == 0 {
foundDefault4 = true
}
route.NextHop = net.IPv4zero
} else if allowedip.Bits() == 128 {
if allowedip.Cidr == 0 {
foundDefault6 = true
}
route.NextHop = net.IPv6zero
}
routes = append(routes, route)
}
}
err := luid.SetIPAddressesForFamily(family, addresses)
if err == windows.ERROR_OBJECT_ALREADY_EXISTS {
cleanupAddressesOnDisconnectedInterfaces(family, addresses)
err = luid.SetIPAddressesForFamily(family, addresses)
}
if err != nil {
return err
}
deduplicatedRoutes := make([]*winipcfg.RouteData, 0, len(routes))
sort.Slice(routes, func(i, j int) bool {
return routes[i].Metric < routes[j].Metric ||
bytes.Compare(routes[i].NextHop, routes[j].NextHop) == -1 ||
bytes.Compare(routes[i].Destination.IP, routes[j].Destination.IP) == -1 ||
bytes.Compare(routes[i].Destination.Mask, routes[j].Destination.Mask) == -1
})
for i := 0; i < len(routes); i++ {
if i > 0 && routes[i].Metric == routes[i-1].Metric &&
bytes.Equal(routes[i].NextHop, routes[i-1].NextHop) &&
bytes.Equal(routes[i].Destination.IP, routes[i-1].Destination.IP) &&
bytes.Equal(routes[i].Destination.Mask, routes[i-1].Destination.Mask) {
continue
}
deduplicatedRoutes = append(deduplicatedRoutes, &routes[i])
}
err = luid.SetRoutesForFamily(family, deduplicatedRoutes)
if err != nil {
return nil
}
ipif, err := luid.IPInterface(family)
if err != nil {
return err
}
if conf.Interface.MTU > 0 {
ipif.NLMTU = uint32(conf.Interface.MTU)
tun.ForceMTU(int(ipif.NLMTU))
}
if family == windows.AF_INET {
if foundDefault4 {
ipif.UseAutomaticMetric = false
ipif.Metric = 0
}
} else if family == windows.AF_INET6 {
if foundDefault6 {
ipif.UseAutomaticMetric = false
ipif.Metric = 0
}
ipif.DadTransmits = 0
ipif.RouterDiscoveryBehavior = winipcfg.RouterDiscoveryDisabled
}
err = ipif.Set()
if err != nil {
return err
}
err = luid.SetDNSForFamily(family, conf.Interface.DNS)
if err != nil {
return err
}
return nil
}
func cleanupAddressesOnDisconnectedInterfaces(family winipcfg.AddressFamily, addresses []net.IPNet) {
if len(addresses) == 0 {
return
}
includedInAddresses := func(a net.IPNet) bool {
// TODO: this makes the whole algorithm O(n^2). But we can't stick net.IPNet in a Go hashmap. Bummer!
for _, addr := range addresses {
ip := addr.IP
if ip4 := ip.To4(); ip4 != nil {
ip = ip4
}
mA, _ := addr.Mask.Size()
mB, _ := a.Mask.Size()
if bytes.Equal(ip, a.IP) && mA == mB {
return true
}
}
return false
}
interfaces, err := winipcfg.GetAdaptersAddresses(family, winipcfg.GAAFlagDefault)
if err != nil {
return
}
for _, iface := range interfaces {
if iface.OperStatus == winipcfg.IfOperStatusUp {
continue
}
for address := iface.FirstUnicastAddress; address != nil; address = address.Next {
ip := address.Address.IP()
ipnet := net.IPNet{IP: ip, Mask: net.CIDRMask(int(address.OnLinkPrefixLength), 8*len(ip))}
if includedInAddresses(ipnet) {
log.Printf("Cleaning up stale address %s from interface ‘%s’", ipnet.String(), iface.FriendlyName())
iface.LUID.DeleteIPAddress(ipnet)
}
}
}
}
func hasDefaultRoute(family winipcfg.AddressFamily, peers []conf.Peer) bool {
var (
foundV401 bool
foundV41281 bool
foundV600001 bool
foundV680001 bool
foundV400 bool
foundV600 bool
v40 = [4]byte{}
v60 = [16]byte{}
v48 = [4]byte{0x80}
v68 = [16]byte{0x80}
)
for _, peer := range peers {
for _, allowedip := range peer.AllowedIPs {
if allowedip.Cidr == 1 && len(allowedip.IP) == 16 && allowedip.IP.Equal(v60[:]) {
foundV600001 = true
} else if allowedip.Cidr == 1 && len(allowedip.IP) == 16 && allowedip.IP.Equal(v68[:]) {
foundV680001 = true
} else if allowedip.Cidr == 1 && len(allowedip.IP) == 4 && allowedip.IP.Equal(v40[:]) {
foundV401 = true
} else if allowedip.Cidr == 1 && len(allowedip.IP) == 4 && allowedip.IP.Equal(v48[:]) {
foundV41281 = true
} else if allowedip.Cidr == 0 && len(allowedip.IP) == 16 && allowedip.IP.Equal(v60[:]) {
foundV600 = true
} else if allowedip.Cidr == 0 && len(allowedip.IP) == 4 && allowedip.IP.Equal(v40[:]) {
foundV400 = true
}
}
}
if family == windows.AF_INET {
return foundV400 || (foundV401 && foundV41281)
} else if family == windows.AF_INET6 {
return foundV600 || (foundV600001 && foundV680001)
}
return false
}
func (iw *InterfaceWatcher) Configure(device *device.Device, conf *conf.Config, tun *tun.NativeTun) {
iw.setupMutex.Lock()
defer iw.setupMutex.Unlock()
iw.device, iw.conf, iw.tun = device, conf, tun
for _, event := range iw.storedEvents {
if event.luid == winipcfg.LUID(iw.tun.LUID()) {
iw.setup(event.family)
}
}
iw.storedEvents = nil
}