diff --git a/tun/wintun/iphlpapi/conversion_windows.go b/tun/wintun/iphlpapi/conversion_windows.go new file mode 100644 index 0000000..a19e961 --- /dev/null +++ b/tun/wintun/iphlpapi/conversion_windows.go @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +package iphlpapi + +import "golang.org/x/sys/windows" + +//sys convertInterfaceLUIDToGUID(interfaceLUID *uint64, interfaceGUID *windows.GUID) (ret error) = iphlpapi.ConvertInterfaceLuidToGuid +//sys convertInterfaceAliasToLUID(interfaceAlias *uint16, interfaceLUID *uint64) (ret error) = iphlpapi.ConvertInterfaceAliasToLuid + +func InterfaceGUIDFromAlias(alias string) (*windows.GUID, error) { + var luid uint64 + var guid windows.GUID + err := convertInterfaceAliasToLUID(windows.StringToUTF16Ptr(alias), &luid) + if err != nil { + return nil, err + } + err = convertInterfaceLUIDToGUID(&luid, &guid) + if err != nil { + return nil, err + } + return &guid, nil +} diff --git a/tun/wintun/iphlpapi/mksyscall.go b/tun/wintun/iphlpapi/mksyscall.go new file mode 100644 index 0000000..f2639ea --- /dev/null +++ b/tun/wintun/iphlpapi/mksyscall.go @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +package iphlpapi + +//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go conversion_windows.go diff --git a/tun/wintun/iphlpapi/zsyscall_windows.go b/tun/wintun/iphlpapi/zsyscall_windows.go new file mode 100644 index 0000000..dc14294 --- /dev/null +++ b/tun/wintun/iphlpapi/zsyscall_windows.go @@ -0,0 +1,60 @@ +// Code generated by 'go generate'; DO NOT EDIT. + +package iphlpapi + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return nil + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll") + + procConvertInterfaceLuidToGuid = modiphlpapi.NewProc("ConvertInterfaceLuidToGuid") + procConvertInterfaceAliasToLuid = modiphlpapi.NewProc("ConvertInterfaceAliasToLuid") +) + +func convertInterfaceLUIDToGUID(interfaceLUID *uint64, interfaceGUID *windows.GUID) (ret error) { + r0, _, _ := syscall.Syscall(procConvertInterfaceLuidToGuid.Addr(), 2, uintptr(unsafe.Pointer(interfaceLUID)), uintptr(unsafe.Pointer(interfaceGUID)), 0) + if r0 != 0 { + ret = syscall.Errno(r0) + } + return +} + +func convertInterfaceAliasToLUID(interfaceAlias *uint16, interfaceLUID *uint64) (ret error) { + r0, _, _ := syscall.Syscall(procConvertInterfaceAliasToLuid.Addr(), 2, uintptr(unsafe.Pointer(interfaceAlias)), uintptr(unsafe.Pointer(interfaceLUID)), 0) + if r0 != 0 { + ret = syscall.Errno(r0) + } + return +} diff --git a/tun/wintun/wintun_windows.go b/tun/wintun/wintun_windows.go index 81da251..f5d42eb 100644 --- a/tun/wintun/wintun_windows.go +++ b/tun/wintun/wintun_windows.go @@ -15,6 +15,7 @@ import ( "golang.org/x/sys/windows" "golang.org/x/sys/windows/registry" + "golang.zx2c4.com/wireguard/tun/wintun/iphlpapi" "golang.zx2c4.com/wireguard/tun/wintun/nci" registryEx "golang.zx2c4.com/wireguard/tun/wintun/registry" "golang.zx2c4.com/wireguard/tun/wintun/setupapi" @@ -528,11 +529,35 @@ func (wintun *Wintun) InterfaceName() (string, error) { func (wintun *Wintun) SetInterfaceName(ifname string) error { const maxSuffix = 1000 availableIfname := ifname +setloop: for i := 0; ; i++ { err := nci.SetConnectionName(&wintun.cfgInstanceID, availableIfname) if err == nil { break } + if err == windows.ERROR_DUP_NAME { + duplicateGuid, err2 := iphlpapi.InterfaceGUIDFromAlias(availableIfname) + if err2 == nil { + for j := 0; j < 1000; j++ { + 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 { + break setloop + } + } + break + } + } + } + if i > maxSuffix || err != windows.ERROR_DUP_NAME { return fmt.Errorf("NciSetConnectionName failed: %v", err) }