2019-02-07 03:18:27 +00:00
/ * SPDX - License - Identifier : MIT
*
* Copyright ( C ) 2019 WireGuard LLC . All Rights Reserved .
* /
package wintun
import (
"errors"
"fmt"
"strings"
"time"
"unsafe"
"golang.org/x/sys/windows"
"golang.org/x/sys/windows/registry"
2019-05-17 12:26:46 +00:00
2019-08-27 16:09:16 +00:00
"golang.zx2c4.com/wireguard/tun/wintun/iphlpapi"
2019-08-21 06:40:44 +00:00
"golang.zx2c4.com/wireguard/tun/wintun/nci"
2019-05-09 08:11:15 +00:00
registryEx "golang.zx2c4.com/wireguard/tun/wintun/registry"
2019-03-04 10:58:02 +00:00
"golang.zx2c4.com/wireguard/tun/wintun/setupapi"
2019-02-07 03:18:27 +00:00
)
2019-08-29 16:00:44 +00:00
type Pool string
2019-08-29 18:20:40 +00:00
type Interface struct {
2019-05-17 12:26:46 +00:00
cfgInstanceID windows . GUID
2019-07-23 09:45:13 +00:00
devInstanceID string
2019-05-17 12:26:46 +00:00
luidIndex uint32
ifType uint32
2019-08-29 18:13:16 +00:00
pool Pool
2019-03-22 14:28:33 +00:00
}
2019-02-07 03:18:27 +00:00
2019-03-04 13:27:16 +00:00
var deviceClassNetGUID = windows . GUID { Data1 : 0x4d36e972 , Data2 : 0xe325 , Data3 : 0x11ce , Data4 : [ 8 ] byte { 0xbf , 0xc1 , 0x08 , 0x00 , 0x2b , 0xe1 , 0x03 , 0x18 } }
2019-07-23 12:58:46 +00:00
var deviceInterfaceNetGUID = windows . GUID { Data1 : 0xcac88484 , Data2 : 0x7515 , Data3 : 0x4c03 , Data4 : [ 8 ] byte { 0x82 , 0xe6 , 0x71 , 0xa8 , 0x7a , 0xba , 0xc3 , 0x61 } }
2019-02-07 03:18:27 +00:00
2019-06-10 09:02:18 +00:00
const (
2019-06-18 14:08:28 +00:00
hardwareID = "Wintun"
2019-06-10 22:33:07 +00:00
waitForRegistryTimeout = time . Second * 10
2019-06-10 09:02:18 +00:00
)
2019-02-07 03:18:27 +00:00
2019-06-10 09:02:18 +00:00
// makeWintun creates a Wintun interface handle and populates it from the device's registry key.
2019-08-29 18:20:40 +00:00
func makeWintun ( deviceInfoSet setupapi . DevInfo , deviceInfoData * setupapi . DevInfoData , pool Pool ) ( * Interface , error ) {
2019-03-22 14:28:33 +00:00
// Open HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\<class>\<id> registry key.
2019-05-09 08:11:15 +00:00
key , err := deviceInfoSet . OpenDevRegKey ( deviceInfoData , setupapi . DICS_FLAG_GLOBAL , 0 , setupapi . DIREG_DRV , registry . QUERY_VALUE )
2019-03-22 14:28:33 +00:00
if err != nil {
2019-05-10 16:01:47 +00:00
return nil , fmt . Errorf ( "Device-specific registry key open failed: %v" , err )
2019-03-22 14:28:33 +00:00
}
defer key . Close ( )
2019-03-31 08:17:11 +00:00
// Read the NetCfgInstanceId value.
2019-05-09 08:11:15 +00:00
valueStr , err := registryEx . GetStringValue ( key , "NetCfgInstanceId" )
2019-03-22 14:28:33 +00:00
if err != nil {
2019-05-10 16:01:47 +00:00
return nil , fmt . Errorf ( "RegQueryStringValue(\"NetCfgInstanceId\") failed: %v" , err )
2019-03-22 14:28:33 +00:00
}
2019-06-06 20:28:13 +00:00
// Convert to GUID.
ifid , err := windows . GUIDFromString ( valueStr )
2019-03-22 14:28:33 +00:00
if err != nil {
return nil , fmt . Errorf ( "NetCfgInstanceId registry value is not a GUID (expected: \"{...}\", provided: %q)" , valueStr )
}
// Read the NetLuidIndex value.
2019-05-09 08:11:15 +00:00
luidIdx , _ , err := key . GetIntegerValue ( "NetLuidIndex" )
2019-03-22 14:28:33 +00:00
if err != nil {
2019-05-10 16:01:47 +00:00
return nil , fmt . Errorf ( "RegQueryValue(\"NetLuidIndex\") failed: %v" , err )
2019-03-22 14:28:33 +00:00
}
// Read the NetLuidIndex value.
2019-05-09 08:11:15 +00:00
ifType , _ , err := key . GetIntegerValue ( "*IfType" )
2019-03-22 14:28:33 +00:00
if err != nil {
2019-05-10 16:01:47 +00:00
return nil , fmt . Errorf ( "RegQueryValue(\"*IfType\") failed: %v" , err )
2019-03-22 14:28:33 +00:00
}
2019-08-02 12:53:02 +00:00
instanceID , err := deviceInfoSet . DeviceInstanceID ( deviceInfoData )
2019-07-23 09:45:13 +00:00
if err != nil {
return nil , fmt . Errorf ( "DeviceInstanceID failed: %v" , err )
}
2019-08-29 18:20:40 +00:00
return & Interface {
2019-06-06 20:28:13 +00:00
cfgInstanceID : ifid ,
2019-08-02 12:53:02 +00:00
devInstanceID : instanceID ,
2019-05-17 12:26:46 +00:00
luidIndex : uint32 ( luidIdx ) ,
ifType : uint32 ( ifType ) ,
2019-08-29 18:13:16 +00:00
pool : pool ,
2019-03-22 14:28:33 +00:00
} , nil
}
2019-08-28 14:08:07 +00:00
func removeNumberedSuffix ( ifname string ) string {
removed := strings . TrimRight ( ifname , "0123456789" )
if removed != ifname && len ( removed ) > 1 && removed [ len ( removed ) - 1 ] == ' ' {
return removed [ : len ( removed ) - 1 ]
}
return ifname
}
2019-06-10 09:02:18 +00:00
// GetInterface finds a Wintun interface by its name. This function returns
// the interface if found, or windows.ERROR_OBJECT_NOT_FOUND otherwise. If
2019-08-29 16:00:44 +00:00
// the interface is found but not a Wintun-class or a member of the pool,
// this function returns windows.ERROR_ALREADY_EXISTS.
2019-08-29 18:20:40 +00:00
func ( pool Pool ) GetInterface ( ifname string ) ( * Interface , error ) {
2019-08-29 23:42:28 +00:00
mutex , err := pool . takeNameMutex ( )
if err != nil {
return nil , err
}
defer func ( ) {
windows . ReleaseMutex ( mutex )
windows . CloseHandle ( mutex )
} ( )
2019-02-07 03:18:27 +00:00
// Create a list of network devices.
2019-06-10 09:02:18 +00:00
devInfoList , err := setupapi . SetupDiGetClassDevsEx ( & deviceClassNetGUID , "" , 0 , setupapi . DIGCF_PRESENT , setupapi . DevInfo ( 0 ) , "" )
2019-02-07 03:18:27 +00:00
if err != nil {
2019-06-06 20:28:13 +00:00
return nil , fmt . Errorf ( "SetupDiGetClassDevsEx(%v) failed: %v" , deviceClassNetGUID , err )
2019-02-07 03:18:27 +00:00
}
defer devInfoList . Close ( )
2019-02-07 18:42:59 +00:00
// Windows requires each interface to have a different name. When
// enforcing this, Windows treats interface names case-insensitive. If an
// interface "FooBar" exists and this function reports there is no
// interface "foobar", an attempt to create a new interface and name it
// "foobar" would cause conflict with "FooBar".
2019-02-07 03:18:27 +00:00
ifname = strings . ToLower ( ifname )
for index := 0 ; ; index ++ {
deviceData , err := devInfoList . EnumDeviceInfo ( index )
if err != nil {
2019-06-10 09:10:49 +00:00
if err == windows . ERROR_NO_MORE_ITEMS {
2019-02-07 03:18:27 +00:00
break
}
continue
}
2019-08-29 18:13:16 +00:00
wintun , err := makeWintun ( devInfoList , deviceData , pool )
2019-02-07 03:18:27 +00:00
if err != nil {
continue
}
2019-06-06 21:00:15 +00:00
// TODO: is there a better way than comparing ifnames?
2019-08-29 18:20:40 +00:00
ifname2 , err := wintun . Name ( )
2019-02-07 03:18:27 +00:00
if err != nil {
continue
}
2019-08-26 20:46:43 +00:00
ifname2 = strings . ToLower ( ifname2 )
2019-08-28 14:08:07 +00:00
ifname3 := removeNumberedSuffix ( ifname2 )
2019-02-07 03:18:27 +00:00
2019-08-28 14:08:07 +00:00
if ifname == ifname2 || ifname == ifname3 {
2019-06-10 09:20:49 +00:00
err = devInfoList . BuildDriverInfoList ( deviceData , setupapi . SPDIT_COMPATDRIVER )
2019-02-07 21:02:51 +00:00
if err != nil {
2019-05-10 16:01:47 +00:00
return nil , fmt . Errorf ( "SetupDiBuildDriverInfoList failed: %v" , err )
2019-02-07 21:02:51 +00:00
}
2019-06-10 09:20:49 +00:00
defer devInfoList . DestroyDriverInfoList ( deviceData , setupapi . SPDIT_COMPATDRIVER )
2019-02-07 21:02:51 +00:00
for index := 0 ; ; index ++ {
2019-06-10 09:20:49 +00:00
driverData , err := devInfoList . EnumDriverInfo ( deviceData , setupapi . SPDIT_COMPATDRIVER , index )
2019-02-07 21:02:51 +00:00
if err != nil {
2019-06-10 09:10:49 +00:00
if err == windows . ERROR_NO_MORE_ITEMS {
2019-02-07 21:02:51 +00:00
break
}
continue
}
// Get driver info details.
2019-05-22 17:31:52 +00:00
driverDetailData , err := devInfoList . DriverInfoDetail ( deviceData , driverData )
2019-02-07 21:02:51 +00:00
if err != nil {
continue
}
2019-03-04 13:27:16 +00:00
if driverDetailData . IsCompatible ( hardwareID ) {
2019-08-29 16:00:44 +00:00
isMember , err := pool . isMember ( devInfoList , deviceData )
if err != nil {
return nil , err
}
if ! isMember {
return nil , windows . ERROR_ALREADY_EXISTS
}
2019-03-22 14:28:33 +00:00
return wintun , nil
2019-02-07 21:02:51 +00:00
}
}
// This interface is not using Wintun driver.
2019-05-24 07:28:50 +00:00
return nil , windows . ERROR_ALREADY_EXISTS
2019-02-07 03:18:27 +00:00
}
}
2019-05-23 13:25:53 +00:00
return nil , windows . ERROR_OBJECT_NOT_FOUND
2019-02-07 03:18:27 +00:00
}
2019-08-29 23:42:28 +00:00
// CreateInterface creates a Wintun interface. ifname is the requested name of
// the interface, while requestedGUID is the GUID of the created network
// interface, which then influences NLA generation deterministically. If it is
// set to nil, the GUID is chosen by the system at random, and hence a new NLA
// entry is created for each new interface. It is called "requested" GUID
// because the API it uses is completely undocumented, and so there could be minor
// interesting complications with its usage. This function returns the network
// interface ID and a flag if reboot is required.
func ( pool Pool ) CreateInterface ( ifname string , requestedGUID * windows . GUID ) ( wintun * Interface , rebootRequired bool , err error ) {
2019-09-02 03:32:28 +00:00
mutex , err := pool . takeNameMutex ( )
if err != nil {
return
}
defer func ( ) {
windows . ReleaseMutex ( mutex )
windows . CloseHandle ( mutex )
} ( )
2019-02-07 03:18:27 +00:00
// Create an empty device info set for network adapter device class.
2019-06-10 09:02:18 +00:00
devInfoList , err := setupapi . SetupDiCreateDeviceInfoListEx ( & deviceClassNetGUID , 0 , "" )
2019-02-07 03:18:27 +00:00
if err != nil {
2019-08-02 12:53:02 +00:00
err = fmt . Errorf ( "SetupDiCreateDeviceInfoListEx(%v) failed: %v" , deviceClassNetGUID , err )
return
2019-02-07 03:18:27 +00:00
}
2019-05-10 18:19:11 +00:00
defer devInfoList . Close ( )
2019-02-07 03:18:27 +00:00
// Get the device class name from GUID.
2019-06-10 09:02:18 +00:00
className , err := setupapi . SetupDiClassNameFromGuidEx ( & deviceClassNetGUID , "" )
2019-02-07 03:18:27 +00:00
if err != nil {
2019-08-02 12:53:02 +00:00
err = fmt . Errorf ( "SetupDiClassNameFromGuidEx(%v) failed: %v" , deviceClassNetGUID , err )
return
2019-02-07 03:18:27 +00:00
}
// Create a new device info element and add it to the device info set.
2019-08-29 18:20:40 +00:00
deviceTypeName := pool . deviceTypeName ( )
2019-08-24 10:29:17 +00:00
deviceData , err := devInfoList . CreateDeviceInfo ( className , & deviceClassNetGUID , deviceTypeName , 0 , setupapi . DICD_GENERATE_ID )
2019-02-07 03:18:27 +00:00
if err != nil {
2019-08-02 12:53:02 +00:00
err = fmt . Errorf ( "SetupDiCreateDeviceInfo failed: %v" , err )
return
2019-02-07 03:18:27 +00:00
}
2019-06-10 09:02:18 +00:00
err = setQuietInstall ( devInfoList , deviceData )
if err != nil {
2019-08-02 12:53:02 +00:00
err = fmt . Errorf ( "Setting quiet installation failed: %v" , err )
return
2019-06-04 11:57:36 +00:00
}
2019-02-07 03:18:27 +00:00
// Set a device information element as the selected member of a device information set.
err = devInfoList . SetSelectedDevice ( deviceData )
if err != nil {
2019-08-02 12:53:02 +00:00
err = fmt . Errorf ( "SetupDiSetSelectedDevice failed: %v" , err )
return
2019-02-07 03:18:27 +00:00
}
// Set Plug&Play device hardware ID property.
2019-05-03 07:34:00 +00:00
err = devInfoList . SetDeviceRegistryPropertyString ( deviceData , setupapi . SPDRP_HARDWAREID , hardwareID )
2019-02-07 03:18:27 +00:00
if err != nil {
2019-08-02 12:53:02 +00:00
err = fmt . Errorf ( "SetupDiSetDeviceRegistryProperty(SPDRP_HARDWAREID) failed: %v" , err )
return
2019-02-07 03:18:27 +00:00
}
2019-06-10 09:20:49 +00:00
err = devInfoList . BuildDriverInfoList ( deviceData , setupapi . SPDIT_COMPATDRIVER ) // TODO: This takes ~510ms
2019-02-07 03:18:27 +00:00
if err != nil {
2019-08-02 12:53:02 +00:00
err = fmt . Errorf ( "SetupDiBuildDriverInfoList failed: %v" , err )
return
2019-02-07 03:18:27 +00:00
}
2019-06-10 09:20:49 +00:00
defer devInfoList . DestroyDriverInfoList ( deviceData , setupapi . SPDIT_COMPATDRIVER )
2019-02-07 03:18:27 +00:00
driverDate := windows . Filetime { }
driverVersion := uint64 ( 0 )
2019-06-06 21:00:15 +00:00
for index := 0 ; ; index ++ { // TODO: This loop takes ~600ms
2019-06-10 09:20:49 +00:00
driverData , err := devInfoList . EnumDriverInfo ( deviceData , setupapi . SPDIT_COMPATDRIVER , index )
2019-02-07 03:18:27 +00:00
if err != nil {
2019-06-10 09:10:49 +00:00
if err == windows . ERROR_NO_MORE_ITEMS {
2019-02-07 03:18:27 +00:00
break
}
continue
}
// Check the driver version first, since the check is trivial and will save us iterating over hardware IDs for any driver versioned prior our best match.
if driverData . IsNewer ( driverDate , driverVersion ) {
2019-05-22 17:31:52 +00:00
driverDetailData , err := devInfoList . DriverInfoDetail ( deviceData , driverData )
2019-02-07 03:18:27 +00:00
if err != nil {
continue
}
2019-03-04 13:27:16 +00:00
if driverDetailData . IsCompatible ( hardwareID ) {
2019-02-07 03:18:27 +00:00
err := devInfoList . SetSelectedDriver ( deviceData , driverData )
if err != nil {
continue
}
driverDate = driverData . DriverDate
driverVersion = driverData . DriverVersion
}
}
}
if driverVersion == 0 {
2019-08-02 12:53:02 +00:00
err = fmt . Errorf ( "No driver for device %q installed" , hardwareID )
return
2019-02-07 03:18:27 +00:00
}
2019-08-27 16:54:49 +00:00
defer func ( ) {
if err != nil {
// The interface failed to install, or the interface ID was unobtainable. Clean-up.
removeDeviceParams := setupapi . RemoveDeviceParams {
ClassInstallHeader : * setupapi . MakeClassInstallHeader ( setupapi . DIF_REMOVE ) ,
Scope : setupapi . DI_REMOVEDEVICE_GLOBAL ,
}
// Set class installer parameters for DIF_REMOVE.
if devInfoList . SetClassInstallParams ( deviceData , & removeDeviceParams . ClassInstallHeader , uint32 ( unsafe . Sizeof ( removeDeviceParams ) ) ) == nil {
// Call appropriate class installer.
if devInfoList . CallClassInstaller ( setupapi . DIF_REMOVE , deviceData ) == nil {
rebootRequired = rebootRequired || checkReboot ( devInfoList , deviceData )
}
}
wintun = nil
}
} ( )
2019-02-07 03:18:27 +00:00
// Call appropriate class installer.
err = devInfoList . CallClassInstaller ( setupapi . DIF_REGISTERDEVICE , deviceData )
if err != nil {
2019-08-02 12:53:02 +00:00
err = fmt . Errorf ( "SetupDiCallClassInstaller(DIF_REGISTERDEVICE) failed: %v" , err )
return
2019-02-07 03:18:27 +00:00
}
2019-03-08 08:45:59 +00:00
// Register device co-installers if any. (Ignore errors)
2019-02-07 03:18:27 +00:00
devInfoList . CallClassInstaller ( setupapi . DIF_REGISTER_COINSTALLERS , deviceData )
2019-08-19 07:11:21 +00:00
var netDevRegKey registry . Key
2019-08-02 13:48:41 +00:00
const pollTimeout = time . Millisecond * 50
for i := 0 ; i < int ( waitForRegistryTimeout / pollTimeout ) ; i ++ {
if i != 0 {
time . Sleep ( pollTimeout )
}
2019-08-19 07:11:21 +00:00
netDevRegKey , err = devInfoList . OpenDevRegKey ( deviceData , setupapi . DICS_FLAG_GLOBAL , 0 , setupapi . DIREG_DRV , registry . SET_VALUE | registry . QUERY_VALUE | registry . NOTIFY )
2019-08-02 13:48:41 +00:00
if err == nil {
break
2019-06-09 17:20:17 +00:00
}
2019-08-02 13:48:41 +00:00
}
if err != nil {
err = fmt . Errorf ( "SetupDiOpenDevRegKey failed: %v" , err )
return
}
2019-08-19 07:11:21 +00:00
defer netDevRegKey . Close ( )
2019-08-02 13:48:41 +00:00
if requestedGUID != nil {
2019-08-19 07:11:21 +00:00
err = netDevRegKey . SetStringValue ( "NetSetupAnticipatedInstanceId" , requestedGUID . String ( ) )
2019-06-09 17:20:17 +00:00
if err != nil {
2019-08-02 12:53:02 +00:00
err = fmt . Errorf ( "SetStringValue(NetSetupAnticipatedInstanceId) failed: %v" , err )
return
2019-06-09 17:20:17 +00:00
}
}
2019-03-08 08:45:59 +00:00
// Install interfaces if any. (Ignore errors)
2019-02-07 03:18:27 +00:00
devInfoList . CallClassInstaller ( setupapi . DIF_INSTALLINTERFACES , deviceData )
// Install the device.
err = devInfoList . CallClassInstaller ( setupapi . DIF_INSTALLDEVICE , deviceData )
2019-03-08 08:45:59 +00:00
if err != nil {
2019-05-10 16:01:47 +00:00
err = fmt . Errorf ( "SetupDiCallClassInstaller(DIF_INSTALLDEVICE) failed: %v" , err )
2019-08-02 12:53:02 +00:00
return
2019-03-08 08:45:59 +00:00
}
2019-08-02 13:37:08 +00:00
rebootRequired = checkReboot ( devInfoList , deviceData )
2019-02-07 03:18:27 +00:00
2019-08-20 13:48:08 +00:00
err = devInfoList . SetDeviceRegistryPropertyString ( deviceData , setupapi . SPDRP_DEVICEDESC , deviceTypeName )
if err != nil {
err = fmt . Errorf ( "SetDeviceRegistryPropertyString(SPDRP_DEVICEDESC) failed: %v" , err )
return
}
2019-08-02 12:53:02 +00:00
// DIF_INSTALLDEVICE returns almost immediately, while the device installation
// continues in the background. It might take a while, before all registry
// keys and values are populated.
2019-08-19 07:11:21 +00:00
_ , err = registryEx . GetStringValueWait ( netDevRegKey , "NetCfgInstanceId" , waitForRegistryTimeout )
2019-08-02 12:53:02 +00:00
if err != nil {
err = fmt . Errorf ( "GetStringValueWait(NetCfgInstanceId) failed: %v" , err )
return
2019-02-07 03:18:27 +00:00
}
2019-08-19 07:11:21 +00:00
_ , err = registryEx . GetIntegerValueWait ( netDevRegKey , "NetLuidIndex" , waitForRegistryTimeout )
2019-08-02 12:53:02 +00:00
if err != nil {
err = fmt . Errorf ( "GetIntegerValueWait(NetLuidIndex) failed: %v" , err )
return
}
2019-08-19 07:11:21 +00:00
_ , err = registryEx . GetIntegerValueWait ( netDevRegKey , "*IfType" , waitForRegistryTimeout )
2019-08-02 12:53:02 +00:00
if err != nil {
err = fmt . Errorf ( "GetIntegerValueWait(*IfType) failed: %v" , err )
return
2019-05-09 08:11:15 +00:00
}
2019-08-02 12:53:02 +00:00
// Get network interface.
2019-08-29 18:13:16 +00:00
wintun , err = makeWintun ( devInfoList , deviceData , pool )
2019-08-02 12:53:02 +00:00
if err != nil {
err = fmt . Errorf ( "makeWintun failed: %v" , err )
return
2019-05-09 08:11:15 +00:00
}
2019-08-02 12:53:02 +00:00
// Wait for TCP/IP adapter registry key to emerge and populate.
2019-08-19 07:11:21 +00:00
tcpipAdapterRegKey , err := registryEx . OpenKeyWait (
2019-08-02 12:53:02 +00:00
registry . LOCAL_MACHINE ,
wintun . tcpipAdapterRegKeyName ( ) , registry . QUERY_VALUE | registry . NOTIFY ,
waitForRegistryTimeout )
if err != nil {
err = fmt . Errorf ( "OpenKeyWait(HKLM\\%s) failed: %v" , wintun . tcpipAdapterRegKeyName ( ) , err )
return
}
2019-08-19 07:11:21 +00:00
defer tcpipAdapterRegKey . Close ( )
_ , err = registryEx . GetStringValueWait ( tcpipAdapterRegKey , "IpConfig" , waitForRegistryTimeout )
2019-08-02 12:53:02 +00:00
if err != nil {
err = fmt . Errorf ( "GetStringValueWait(IpConfig) failed: %v" , err )
return
2019-05-09 08:11:15 +00:00
}
2019-08-02 12:53:02 +00:00
tcpipInterfaceRegKeyName , err := wintun . tcpipInterfaceRegKeyName ( )
if err != nil {
err = fmt . Errorf ( "tcpipInterfaceRegKeyName failed: %v" , err )
return
2019-02-07 03:18:27 +00:00
}
2019-08-02 12:53:02 +00:00
// Wait for TCP/IP interface registry key to emerge.
2019-08-19 07:11:21 +00:00
tcpipInterfaceRegKey , err := registryEx . OpenKeyWait (
2019-08-02 12:53:02 +00:00
registry . LOCAL_MACHINE ,
2019-08-22 06:52:59 +00:00
tcpipInterfaceRegKeyName , registry . QUERY_VALUE | registry . SET_VALUE ,
2019-08-02 12:53:02 +00:00
waitForRegistryTimeout )
if err != nil {
err = fmt . Errorf ( "OpenKeyWait(HKLM\\%s) failed: %v" , tcpipInterfaceRegKeyName , err )
return
2019-02-07 03:18:27 +00:00
}
2019-08-19 07:11:21 +00:00
defer tcpipInterfaceRegKey . Close ( )
2019-08-02 12:53:02 +00:00
// Disable dead gateway detection on our interface.
2019-08-19 07:11:21 +00:00
tcpipInterfaceRegKey . SetDWordValue ( "EnableDeadGWDetect" , 0 )
2019-02-07 03:18:27 +00:00
2019-08-29 23:42:28 +00:00
err = wintun . SetName ( ifname )
if err != nil {
err = fmt . Errorf ( "Unable to set name of Wintun interface: %v" , err )
return
}
2019-08-02 12:53:02 +00:00
return
2019-02-07 03:18:27 +00:00
}
2019-06-10 09:02:18 +00:00
// DeleteInterface deletes a Wintun interface. This function succeeds
// if the interface was not found. It returns a bool indicating whether
// a reboot is required.
2019-08-29 18:20:40 +00:00
func ( wintun * Interface ) DeleteInterface ( ) ( rebootRequired bool , err error ) {
2019-06-10 09:02:18 +00:00
devInfoList , deviceData , err := wintun . deviceData ( )
2019-06-03 12:16:34 +00:00
if err == windows . ERROR_OBJECT_NOT_FOUND {
return false , nil
}
2019-02-07 03:18:27 +00:00
if err != nil {
2019-06-03 12:16:34 +00:00
return false , err
2019-02-07 03:18:27 +00:00
}
defer devInfoList . Close ( )
2019-06-03 12:16:34 +00:00
// Remove the device.
removeDeviceParams := setupapi . RemoveDeviceParams {
ClassInstallHeader : * setupapi . MakeClassInstallHeader ( setupapi . DIF_REMOVE ) ,
Scope : setupapi . DI_REMOVEDEVICE_GLOBAL ,
}
2019-02-07 03:18:27 +00:00
2019-06-03 12:16:34 +00:00
// Set class installer parameters for DIF_REMOVE.
err = devInfoList . SetClassInstallParams ( deviceData , & removeDeviceParams . ClassInstallHeader , uint32 ( unsafe . Sizeof ( removeDeviceParams ) ) )
if err != nil {
return false , fmt . Errorf ( "SetupDiSetClassInstallParams failed: %v" , err )
}
2019-02-07 03:18:27 +00:00
2019-06-03 12:16:34 +00:00
// Call appropriate class installer.
err = devInfoList . CallClassInstaller ( setupapi . DIF_REMOVE , deviceData )
if err != nil {
return false , fmt . Errorf ( "SetupDiCallClassInstaller failed: %v" , err )
2019-02-07 03:18:27 +00:00
}
2019-08-02 13:37:08 +00:00
return checkReboot ( devInfoList , deviceData ) , nil
2019-02-07 03:18:27 +00:00
}
2019-08-28 09:39:01 +00:00
// DeleteMatchingInterfaces deletes all Wintun interfaces, which match
// given criteria, and returns which ones it deleted, whether a reboot
// is required after, and which errors occurred during the process.
2019-08-29 18:20:40 +00:00
func ( pool Pool ) DeleteMatchingInterfaces ( matches func ( wintun * Interface ) bool ) ( deviceInstancesDeleted [ ] uint32 , rebootRequired bool , errors [ ] error ) {
2019-08-29 23:42:28 +00:00
mutex , err := pool . takeNameMutex ( )
if err != nil {
errors = append ( errors , err )
return
}
defer func ( ) {
windows . ReleaseMutex ( mutex )
windows . CloseHandle ( mutex )
} ( )
2019-06-10 09:20:49 +00:00
devInfoList , err := setupapi . SetupDiGetClassDevsEx ( & deviceClassNetGUID , "" , 0 , setupapi . DIGCF_PRESENT , setupapi . DevInfo ( 0 ) , "" )
if err != nil {
return nil , false , [ ] error { fmt . Errorf ( "SetupDiGetClassDevsEx(%v) failed: %v" , deviceClassNetGUID , err . Error ( ) ) }
}
defer devInfoList . Close ( )
for i := 0 ; ; i ++ {
deviceData , err := devInfoList . EnumDeviceInfo ( i )
if err != nil {
if err == windows . ERROR_NO_MORE_ITEMS {
break
}
continue
}
err = devInfoList . BuildDriverInfoList ( deviceData , setupapi . SPDIT_COMPATDRIVER )
if err != nil {
continue
}
defer devInfoList . DestroyDriverInfoList ( deviceData , setupapi . SPDIT_COMPATDRIVER )
isWintun := false
for j := 0 ; ; j ++ {
driverData , err := devInfoList . EnumDriverInfo ( deviceData , setupapi . SPDIT_COMPATDRIVER , j )
if err != nil {
if err == windows . ERROR_NO_MORE_ITEMS {
break
}
continue
}
driverDetailData , err := devInfoList . DriverInfoDetail ( deviceData , driverData )
if err != nil {
continue
}
if driverDetailData . IsCompatible ( hardwareID ) {
isWintun = true
break
}
}
if ! isWintun {
continue
}
2019-08-28 14:08:07 +00:00
2019-08-29 16:00:44 +00:00
isMember , err := pool . isMember ( devInfoList , deviceData )
2019-08-28 09:39:01 +00:00
if err != nil {
2019-08-29 16:00:44 +00:00
errors = append ( errors , err )
2019-08-28 14:08:07 +00:00
continue
}
2019-08-29 16:00:44 +00:00
if ! isMember {
2019-08-28 09:39:01 +00:00
continue
}
2019-08-28 14:08:07 +00:00
2019-08-29 18:13:16 +00:00
wintun , err := makeWintun ( devInfoList , deviceData , pool )
2019-08-28 09:39:01 +00:00
if err != nil {
2019-08-29 23:42:28 +00:00
errors = append ( errors , fmt . Errorf ( "Unable to make Wintun interface object: %v" , err ) )
2019-08-28 09:39:01 +00:00
continue
}
if ! matches ( wintun ) {
continue
}
2019-06-10 09:20:49 +00:00
err = setQuietInstall ( devInfoList , deviceData )
if err != nil {
errors = append ( errors , err )
continue
}
inst := deviceData . DevInst
removeDeviceParams := setupapi . RemoveDeviceParams {
ClassInstallHeader : * setupapi . MakeClassInstallHeader ( setupapi . DIF_REMOVE ) ,
Scope : setupapi . DI_REMOVEDEVICE_GLOBAL ,
}
err = devInfoList . SetClassInstallParams ( deviceData , & removeDeviceParams . ClassInstallHeader , uint32 ( unsafe . Sizeof ( removeDeviceParams ) ) )
if err != nil {
errors = append ( errors , err )
continue
}
err = devInfoList . CallClassInstaller ( setupapi . DIF_REMOVE , deviceData )
if err != nil {
errors = append ( errors , err )
continue
}
2019-08-02 13:37:08 +00:00
rebootRequired = rebootRequired || checkReboot ( devInfoList , deviceData )
2019-06-10 09:20:49 +00:00
deviceInstancesDeleted = append ( deviceInstancesDeleted , inst )
}
return
}
2019-08-29 16:00:44 +00:00
// isMember checks if SPDRP_DEVICEDESC or SPDRP_FRIENDLYNAME match device type name.
func ( pool Pool ) isMember ( deviceInfoSet setupapi . DevInfo , deviceInfoData * setupapi . DevInfoData ) ( bool , error ) {
deviceDescVal , err := deviceInfoSet . DeviceRegistryProperty ( deviceInfoData , setupapi . SPDRP_DEVICEDESC )
if err != nil {
return false , fmt . Errorf ( "DeviceRegistryPropertyString(SPDRP_DEVICEDESC) failed: %v" , err )
}
deviceDesc , _ := deviceDescVal . ( string )
friendlyNameVal , err := deviceInfoSet . DeviceRegistryProperty ( deviceInfoData , setupapi . SPDRP_FRIENDLYNAME )
if err != nil {
return false , fmt . Errorf ( "DeviceRegistryPropertyString(SPDRP_FRIENDLYNAME) failed: %v" , err )
}
friendlyName , _ := friendlyNameVal . ( string )
2019-08-29 18:20:40 +00:00
deviceTypeName := pool . deviceTypeName ( )
2019-08-29 16:00:44 +00:00
return friendlyName == deviceTypeName || deviceDesc == deviceTypeName ||
removeNumberedSuffix ( friendlyName ) == deviceTypeName || removeNumberedSuffix ( deviceDesc ) == deviceTypeName , nil
}
2019-02-07 17:24:28 +00:00
// checkReboot checks device install parameters if a system reboot is required.
2019-08-02 13:37:08 +00:00
func checkReboot ( deviceInfoSet setupapi . DevInfo , deviceInfoData * setupapi . DevInfoData ) bool {
2019-05-22 17:31:52 +00:00
devInstallParams , err := deviceInfoSet . DeviceInstallParams ( deviceInfoData )
2019-02-07 03:18:27 +00:00
if err != nil {
2019-08-02 13:37:08 +00:00
return false
2019-02-07 03:18:27 +00:00
}
2019-08-02 13:37:08 +00:00
return ( devInstallParams . Flags & ( setupapi . DI_NEEDREBOOT | setupapi . DI_NEEDRESTART ) ) != 0
2019-06-04 11:57:36 +00:00
}
// setQuietInstall sets device install parameters for a quiet installation
func setQuietInstall ( deviceInfoSet setupapi . DevInfo , deviceInfoData * setupapi . DevInfoData ) error {
devInstallParams , err := deviceInfoSet . DeviceInstallParams ( deviceInfoData )
if err != nil {
return err
2019-02-07 03:18:27 +00:00
}
2019-06-04 11:57:36 +00:00
devInstallParams . Flags |= setupapi . DI_QUIETINSTALL
return deviceInfoSet . SetDeviceInstallParams ( deviceInfoData , devInstallParams )
2019-02-07 03:18:27 +00:00
}
2019-08-29 18:20:40 +00:00
// deviceTypeName returns pool-specific device type name.
func ( pool Pool ) deviceTypeName ( ) string {
2019-08-29 16:00:44 +00:00
return fmt . Sprintf ( "%s Tunnel" , pool )
}
2019-08-29 18:20:40 +00:00
// Name returns the name of the Wintun interface.
func ( wintun * Interface ) Name ( ) ( string , error ) {
2019-08-21 06:40:44 +00:00
return nci . ConnectionName ( & wintun . cfgInstanceID )
2019-05-02 21:53:15 +00:00
}
2019-08-29 18:20:40 +00:00
// SetName sets name of the Wintun interface.
func ( wintun * Interface ) SetName ( ifname string ) error {
2019-08-22 06:52:59 +00:00
const maxSuffix = 1000
availableIfname := ifname
for i := 0 ; ; i ++ {
err := nci . SetConnectionName ( & wintun . cfgInstanceID , availableIfname )
2019-08-27 16:09:16 +00:00
if err == windows . ERROR_DUP_NAME {
duplicateGuid , err2 := iphlpapi . InterfaceGUIDFromAlias ( availableIfname )
if err2 == nil {
2019-08-28 14:08:07 +00:00
for j := 0 ; j < maxSuffix ; j ++ {
2019-08-27 16:09:16 +00:00
proposal := fmt . Sprintf ( "%s %d" , ifname , j + 1 )
if proposal == availableIfname {
continue
}
err2 = nci . SetConnectionName ( duplicateGuid , proposal )
if err2 == windows . ERROR_DUP_NAME {
continue
}
if err2 == nil {
err = nci . SetConnectionName ( & wintun . cfgInstanceID , availableIfname )
if err == nil {
2019-08-29 01:31:20 +00:00
break
2019-08-27 16:09:16 +00:00
}
}
break
}
}
}
2019-08-29 01:31:20 +00:00
if err == nil {
break
}
2019-08-27 16:09:16 +00:00
2019-08-22 06:52:59 +00:00
if i > maxSuffix || err != windows . ERROR_DUP_NAME {
return fmt . Errorf ( "NciSetConnectionName failed: %v" , err )
}
availableIfname = fmt . Sprintf ( "%s %d" , ifname , i + 1 )
2019-08-19 07:20:23 +00:00
}
2019-08-20 13:48:08 +00:00
2019-08-21 06:40:44 +00:00
// TODO: This should use NetSetup2 so that it doesn't get unset.
2019-08-19 07:20:23 +00:00
deviceRegKey , err := registry . OpenKey ( registry . LOCAL_MACHINE , wintun . deviceRegKeyName ( ) , registry . SET_VALUE )
if err != nil {
return fmt . Errorf ( "Device-level registry key open failed: %v" , err )
}
defer deviceRegKey . Close ( )
2019-08-29 18:20:40 +00:00
err = deviceRegKey . SetStringValue ( "FriendlyName" , wintun . pool . deviceTypeName ( ) )
2019-08-19 07:20:23 +00:00
if err != nil {
2019-08-21 06:40:44 +00:00
return fmt . Errorf ( "SetStringValue(FriendlyName) failed: %v" , err )
2019-08-19 07:20:23 +00:00
}
return nil
2019-02-07 03:18:27 +00:00
}
2019-06-10 09:02:18 +00:00
// tcpipAdapterRegKeyName returns the adapter-specific TCP/IP network registry key name.
2019-08-29 18:20:40 +00:00
func ( wintun * Interface ) tcpipAdapterRegKeyName ( ) string {
2019-06-06 20:28:13 +00:00
return fmt . Sprintf ( "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Adapters\\%v" , wintun . cfgInstanceID )
2019-05-09 08:11:15 +00:00
}
2019-10-08 07:58:58 +00:00
// deviceRegKeyName returns the device-level registry key name.
2019-08-29 18:20:40 +00:00
func ( wintun * Interface ) deviceRegKeyName ( ) string {
2019-08-19 07:01:53 +00:00
return fmt . Sprintf ( "SYSTEM\\CurrentControlSet\\Enum\\%v" , wintun . devInstanceID )
}
2019-10-08 07:58:58 +00:00
// Version returns the version of the Wintun driver and NDIS system currently loaded.
func ( wintun * Interface ) Version ( ) ( driverVersion string , ndisVersion string , err error ) {
key , err := registry . OpenKey ( registry . LOCAL_MACHINE , "SYSTEM\\CurrentControlSet\\Services\\Wintun" , registry . QUERY_VALUE )
if err != nil {
return
}
defer key . Close ( )
driverMajor , _ , err := key . GetIntegerValue ( "DriverMajorVersion" )
if err != nil {
return
}
driverMinor , _ , err := key . GetIntegerValue ( "DriverMinorVersion" )
if err != nil {
return
}
ndisMajor , _ , err := key . GetIntegerValue ( "NdisMajorVersion" )
if err != nil {
return
}
ndisMinor , _ , err := key . GetIntegerValue ( "NdisMinorVersion" )
if err != nil {
return
}
driverVersion = fmt . Sprintf ( "%d.%d" , driverMajor , driverMinor )
ndisVersion = fmt . Sprintf ( "%d.%d" , ndisMajor , ndisMinor )
return
}
2019-06-10 09:02:18 +00:00
// tcpipInterfaceRegKeyName returns the interface-specific TCP/IP network registry key name.
2019-08-29 18:20:40 +00:00
func ( wintun * Interface ) tcpipInterfaceRegKeyName ( ) ( path string , err error ) {
2019-05-17 12:26:46 +00:00
key , err := registry . OpenKey ( registry . LOCAL_MACHINE , wintun . tcpipAdapterRegKeyName ( ) , registry . QUERY_VALUE )
2019-02-07 03:18:27 +00:00
if err != nil {
2019-05-10 16:01:47 +00:00
return "" , fmt . Errorf ( "Error opening adapter-specific TCP/IP network registry key: %v" , err )
2019-02-07 03:18:27 +00:00
}
2019-05-10 16:01:47 +00:00
paths , _ , err := key . GetStringsValue ( "IpConfig" )
key . Close ( )
2019-02-07 03:18:27 +00:00
if err != nil {
2019-05-10 16:01:47 +00:00
return "" , fmt . Errorf ( "Error reading IpConfig registry key: %v" , err )
2019-02-07 03:18:27 +00:00
}
2019-05-10 16:01:47 +00:00
if len ( paths ) == 0 {
return "" , errors . New ( "No TCP/IP interfaces found on adapter" )
}
return fmt . Sprintf ( "SYSTEM\\CurrentControlSet\\Services\\%s" , paths [ 0 ] ) , nil
2019-02-07 03:18:27 +00:00
}
2019-06-03 12:16:34 +00:00
// deviceData returns TUN device info list handle and interface device info
2019-06-10 09:02:18 +00:00
// data. The device info list handle must be closed after use. In case the
// device is not found, windows.ERROR_OBJECT_NOT_FOUND is returned.
2019-08-29 18:20:40 +00:00
func ( wintun * Interface ) deviceData ( ) ( setupapi . DevInfo , * setupapi . DevInfoData , error ) {
2019-06-03 12:16:34 +00:00
// Create a list of network devices.
2019-06-10 09:02:18 +00:00
devInfoList , err := setupapi . SetupDiGetClassDevsEx ( & deviceClassNetGUID , "" , 0 , setupapi . DIGCF_PRESENT , setupapi . DevInfo ( 0 ) , "" )
2019-06-03 12:16:34 +00:00
if err != nil {
2019-06-06 20:28:13 +00:00
return 0 , nil , fmt . Errorf ( "SetupDiGetClassDevsEx(%v) failed: %v" , deviceClassNetGUID , err . Error ( ) )
2019-06-03 12:16:34 +00:00
}
for index := 0 ; ; index ++ {
deviceData , err := devInfoList . EnumDeviceInfo ( index )
if err != nil {
2019-06-10 09:10:49 +00:00
if err == windows . ERROR_NO_MORE_ITEMS {
2019-06-03 12:16:34 +00:00
break
}
continue
}
// Get interface ID.
2019-06-06 21:00:15 +00:00
// TODO: Store some ID in the Wintun object such that this call isn't required.
2019-08-29 18:13:16 +00:00
wintun2 , err := makeWintun ( devInfoList , deviceData , wintun . pool )
2019-06-03 12:16:34 +00:00
if err != nil {
continue
}
if wintun . cfgInstanceID == wintun2 . cfgInstanceID {
2019-06-10 09:02:18 +00:00
err = setQuietInstall ( devInfoList , deviceData )
if err != nil {
devInfoList . Close ( )
return 0 , nil , fmt . Errorf ( "Setting quiet installation failed: %v" , err )
2019-06-03 12:16:34 +00:00
}
return devInfoList , deviceData , nil
}
}
devInfoList . Close ( )
return 0 , nil , windows . ERROR_OBJECT_NOT_FOUND
}
2019-08-29 18:47:16 +00:00
// handle returns a handle to the interface device object.
func ( wintun * Interface ) handle ( ) ( windows . Handle , error ) {
2019-07-23 12:58:46 +00:00
interfaces , err := setupapi . CM_Get_Device_Interface_List ( wintun . devInstanceID , & deviceInterfaceNetGUID , setupapi . CM_GET_DEVICE_INTERFACE_LIST_PRESENT )
if err != nil {
2019-08-28 14:39:26 +00:00
return windows . InvalidHandle , fmt . Errorf ( "Error listing NDIS interfaces: %v" , err )
2019-07-23 12:58:46 +00:00
}
handle , err := windows . CreateFile ( windows . StringToUTF16Ptr ( interfaces [ 0 ] ) , windows . GENERIC_READ | windows . GENERIC_WRITE , windows . FILE_SHARE_READ | windows . FILE_SHARE_WRITE | windows . FILE_SHARE_DELETE , nil , windows . OPEN_EXISTING , 0 , 0 )
2019-07-19 11:51:56 +00:00
if err != nil {
2019-08-28 14:39:26 +00:00
return windows . InvalidHandle , fmt . Errorf ( "Error opening NDIS device: %v" , err )
2019-07-19 11:51:56 +00:00
}
return handle , nil
2019-07-18 08:25:12 +00:00
}
2019-05-17 12:26:46 +00:00
// GUID returns the GUID of the interface.
2019-08-29 18:20:40 +00:00
func ( wintun * Interface ) GUID ( ) windows . GUID {
2019-05-17 12:26:46 +00:00
return wintun . cfgInstanceID
}
// LUID returns the LUID of the interface.
2019-08-29 18:20:40 +00:00
func ( wintun * Interface ) LUID ( ) uint64 {
2019-05-17 12:26:46 +00:00
return ( ( uint64 ( wintun . luidIndex ) & ( ( 1 << 24 ) - 1 ) ) << 24 ) | ( ( uint64 ( wintun . ifType ) & ( ( 1 << 16 ) - 1 ) ) << 48 )
2019-02-07 17:24:28 +00:00
}