diff --git a/if_windows.go b/if_windows.go new file mode 100644 index 0000000..3ef1f8e --- /dev/null +++ b/if_windows.go @@ -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) +} diff --git a/params_windows.go b/params_windows.go index 2287c85..89016ce 100644 --- a/params_windows.go +++ b/params_windows.go @@ -1,3 +1,5 @@ +// +build windows + package water // 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, // the one used by OpenVPN. 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 { return PlatformSpecificParams{ - ComponentId: "tap0901", + ComponentID: "tap0901", + Network: "192.168.1.10/24", } } diff --git a/syscalls_windows.go b/syscalls_windows.go index 514f0b8..5dc4013 100644 --- a/syscalls_windows.go +++ b/syscalls_windows.go @@ -33,9 +33,6 @@ var ( tap_ioctl_config_tun = tap_control_code(10, 0) // w32 api 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, nResetEvent, 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. -func getdeviceid() (string, error) { +func getdeviceid(componentID string) (deviceid string, err error) { // TAP driver key location regkey := `SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}` k, err := registry.OpenKey(registry.LOCAL_MACHINE, regkey, registry.READ) @@ -167,7 +164,7 @@ func getdeviceid() (string, error) { key.Close() continue } - if val == componentId { + if val == componentID { val, _, err = key.GetStringValue("NetCfgInstanceId") if err != nil { key.Close() @@ -178,14 +175,49 @@ func getdeviceid() (string, error) { } 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. -func openDev(isTAP bool) (ifce *Interface, err error) { - // ifName won't work +func openDev(config Config) (ifce *Interface, err error) { // find the device in registry. - deviceid, err := getdeviceid() + deviceid, err := getdeviceid(config.PlatformSpecificParams.ComponentID) if err != nil { return nil, err } @@ -209,7 +241,6 @@ func openDev(isTAP bool) (ifce *Interface, err error) { return nil, err } 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 mac := make([]byte, 6) @@ -228,21 +259,17 @@ func openDev(isTAP bool) (ifce *Interface, err error) { return } 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. - code := []byte{0x01, 0x00, 0x00, 0x00} - err = syscall.DeviceIoControl(file, tap_ioctl_set_media_status, &code[0], uint32(4), &rdbbuf[0], uint32(len(rdbbuf)), &bytesReturned, nil) - if err != nil { - return + if err := setStatus(file, true); err != nil { + return nil, err } //TUN - if !isTAP { - code2 := []byte{0x0a, 0x03, 0x00, 0x01, 0x0a, 0x03, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00} - err = syscall.DeviceIoControl(file, tap_ioctl_config_tun, &code2[0], uint32(12), &rdbbuf[0], uint32(len(rdbbuf)), &bytesReturned, nil) - if err != nil { - return + if config.DeviceType == TUN { + if err := setTUN(file, config.PlatformSpecificParams.Network); err != nil { + return nil, err } } @@ -259,16 +286,17 @@ func openDev(isTAP bool) (ifce *Interface, err error) { } } - err = errIfceNameNotFound - return + return nil, errIfceNameNotFound } func newTAP(ifName string) (ifce *Interface, err error) { - // ifName won't work - return openDev(true) + config := defaultConfig() + config.DeviceType = TAP + return openDev(config) } func newTUN(ifName string) (ifce *Interface, err error) { - // ifName won't work - return openDev(false) + config := defaultConfig() + config.DeviceType = TUN + return openDev(config) }