Support new config method.

Refined code.
This commit is contained in:
lucus 2017-01-09 18:55:36 +09:00
parent cde943e7d7
commit 933e95c058
3 changed files with 81 additions and 27 deletions

12
if_windows.go Normal file
View file

@ -0,0 +1,12 @@
// +build windows
package water
import "errors"
func newDev(config Config) (ifce *Interface, err error) {
if config.DeviceType != TAP && config.DeviceType != TUN {
return nil, errors.New("unknown device type")
}
return openDev(config)
}

View file

@ -1,3 +1,5 @@
// +build windows
package water package water
// PlatformSpecificParams defines parameters in Config that are specific to // PlatformSpecificParams defines parameters in Config that are specific to
@ -9,10 +11,22 @@ type PlatformSpecificParams struct {
// use the default ComponentId. The default ComponentId is set to tap0901, // use the default ComponentId. The default ComponentId is set to tap0901,
// the one used by OpenVPN. // the one used by OpenVPN.
ComponentID string ComponentID string
// Network is required when creating a TUN interface. The library will call
// net.ParseCIDR() to parse this string into LocalIP, RemoteNetaddr,
// RemoteNetmask. The underlying driver will need those to generate ARP
// response to Windows kernel, to emulate an TUN interface.
// Please note that it cannot perceive the IP changes caused by DHCP, user
// configuration to the adapter and etc,. If IP changed, please reconfigure
// the adapter using syscall, just like openDev().
// For detail, please refer
// https://github.com/OpenVPN/tap-windows6/blob/master/src/device.c#L431
// and https://github.com/songgao/water/pull/13#issuecomment-270341777
Network string
} }
func defaultPlatformSpecificParams() PlatformSpecificParams { func defaultPlatformSpecificParams() PlatformSpecificParams {
return PlatformSpecificParams{ return PlatformSpecificParams{
ComponentId: "tap0901", ComponentID: "tap0901",
Network: "192.168.1.10/24",
} }
} }

View file

@ -33,9 +33,6 @@ var (
tap_ioctl_config_tun = tap_control_code(10, 0) tap_ioctl_config_tun = tap_control_code(10, 0)
// w32 api // w32 api
file_device_unknown = uint32(0x00000022) file_device_unknown = uint32(0x00000022)
// Driver maker specified ComponentId
// ComponentId is defined here: https://github.com/OpenVPN/tap-windows6/blob/master/version.m4#L5
componentId = "tap0901"
nCreateEvent, nCreateEvent,
nResetEvent, nResetEvent,
nGetOverlappedResult uintptr nGetOverlappedResult uintptr
@ -143,7 +140,7 @@ func tap_control_code(request, method uint32) uint32 {
} }
// getdeviceid finds out a TAP device from registry, it *may* requires privileged right to prevent some weird issue. // getdeviceid finds out a TAP device from registry, it *may* requires privileged right to prevent some weird issue.
func getdeviceid() (string, error) { func getdeviceid(componentID string) (deviceid string, err error) {
// TAP driver key location // TAP driver key location
regkey := `SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}` regkey := `SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}`
k, err := registry.OpenKey(registry.LOCAL_MACHINE, regkey, registry.READ) k, err := registry.OpenKey(registry.LOCAL_MACHINE, regkey, registry.READ)
@ -167,7 +164,7 @@ func getdeviceid() (string, error) {
key.Close() key.Close()
continue continue
} }
if val == componentId { if val == componentID {
val, _, err = key.GetStringValue("NetCfgInstanceId") val, _, err = key.GetStringValue("NetCfgInstanceId")
if err != nil { if err != nil {
key.Close() key.Close()
@ -178,14 +175,49 @@ func getdeviceid() (string, error) {
} }
key.Close() key.Close()
} }
return "", fmt.Errorf("Failed to find the tap device in registry with specified ComponentId(%s), TAP driver may be not installed", componentId) return "", fmt.Errorf("Failed to find the tap device in registry with specified ComponentId(%s), TAP driver may be not installed", componentID)
}
// setStatus is used to bring up or bring down the interface
func setStatus(fd syscall.Handle, status bool) error {
var bytesReturned uint32
rdbbuf := make([]byte, syscall.MAXIMUM_REPARSE_DATA_BUFFER_SIZE)
code := []byte{0x00, 0x00, 0x00, 0x00}
if status {
code[0] = 0x01
}
return syscall.DeviceIoControl(fd, tap_ioctl_set_media_status, &code[0], uint32(4), &rdbbuf[0], uint32(len(rdbbuf)), &bytesReturned, nil)
}
// setTUN is used to configure the IP address in the underlying driver when using TUN
func setTUN(fd syscall.Handle, network string) error {
var bytesReturned uint32
rdbbuf := make([]byte, syscall.MAXIMUM_REPARSE_DATA_BUFFER_SIZE)
localIP, remoteNet, err := net.ParseCIDR(network)
if err != nil {
return fmt.Errorf("Failed to parse network CIDR in config, %v", err)
}
if localIP.To4() == nil {
return fmt.Errorf("Provided network(%s) is not a valid IPv4 address", network)
}
code2 := make([]byte, 0, 12)
code2 = append(code2, localIP.To4()[:4]...)
code2 = append(code2, remoteNet.IP.To4()[:4]...)
code2 = append(code2, remoteNet.Mask[:4]...)
if len(code2) != 12 {
return fmt.Errorf("Provided network(%s) is not valid", network)
}
if err := syscall.DeviceIoControl(fd, tap_ioctl_config_tun, &code2[0], uint32(12), &rdbbuf[0], uint32(len(rdbbuf)), &bytesReturned, nil); err != nil {
return err
}
return nil
} }
// openDev find and open an interface. // openDev find and open an interface.
func openDev(isTAP bool) (ifce *Interface, err error) { func openDev(config Config) (ifce *Interface, err error) {
// ifName won't work
// find the device in registry. // find the device in registry.
deviceid, err := getdeviceid() deviceid, err := getdeviceid(config.PlatformSpecificParams.ComponentID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -209,7 +241,6 @@ func openDev(isTAP bool) (ifce *Interface, err error) {
return nil, err return nil, err
} }
var bytesReturned uint32 var bytesReturned uint32
rdbbuf := make([]byte, syscall.MAXIMUM_REPARSE_DATA_BUFFER_SIZE)
// find the mac address of tap device, use this to find the name of interface // find the mac address of tap device, use this to find the name of interface
mac := make([]byte, 6) mac := make([]byte, 6)
@ -228,21 +259,17 @@ func openDev(isTAP bool) (ifce *Interface, err error) {
return return
} }
fd := &wfile{fd: file, ro: ro, wo: wo} fd := &wfile{fd: file, ro: ro, wo: wo}
ifce = &Interface{isTAP: isTAP, ReadWriteCloser: fd} ifce = &Interface{isTAP: (config.DeviceType == TAP), ReadWriteCloser: fd}
// bring up device. // bring up device.
code := []byte{0x01, 0x00, 0x00, 0x00} if err := setStatus(file, true); err != nil {
err = syscall.DeviceIoControl(file, tap_ioctl_set_media_status, &code[0], uint32(4), &rdbbuf[0], uint32(len(rdbbuf)), &bytesReturned, nil) return nil, err
if err != nil {
return
} }
//TUN //TUN
if !isTAP { if config.DeviceType == TUN {
code2 := []byte{0x0a, 0x03, 0x00, 0x01, 0x0a, 0x03, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00} if err := setTUN(file, config.PlatformSpecificParams.Network); err != nil {
err = syscall.DeviceIoControl(file, tap_ioctl_config_tun, &code2[0], uint32(12), &rdbbuf[0], uint32(len(rdbbuf)), &bytesReturned, nil) return nil, err
if err != nil {
return
} }
} }
@ -259,16 +286,17 @@ func openDev(isTAP bool) (ifce *Interface, err error) {
} }
} }
err = errIfceNameNotFound return nil, errIfceNameNotFound
return
} }
func newTAP(ifName string) (ifce *Interface, err error) { func newTAP(ifName string) (ifce *Interface, err error) {
// ifName won't work config := defaultConfig()
return openDev(true) config.DeviceType = TAP
return openDev(config)
} }
func newTUN(ifName string) (ifce *Interface, err error) { func newTUN(ifName string) (ifce *Interface, err error) {
// ifName won't work config := defaultConfig()
return openDev(false) config.DeviceType = TUN
return openDev(config)
} }