diff --git a/Driver.cpp b/Driver.cpp index 6462f1e..81be0d6 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -26,6 +26,7 @@ #include #include "bufferpool.h" +#include "control.h" #include "driver.h" #include "trace.h" #include "peer.h" @@ -49,6 +50,9 @@ OvpnEvtDriverUnload(_In_ WDFDRIVER driver) { UNREFERENCED_PARAMETER(driver); + LOG_ENTER(); + LOG_EXIT(); + TraceLoggingUnregister(g_hOvpnEtwProvider); // tail call optimization incorrectly eliminates TraceLoggingUnregister() call @@ -56,6 +60,17 @@ OvpnEvtDriverUnload(_In_ WDFDRIVER driver) __nop(); } +EVT_WDF_OBJECT_CONTEXT_CLEANUP OvpnEvtDriverCleanup; + +_Use_decl_annotations_ +VOID OvpnEvtDriverCleanup(_In_ WDFOBJECT driver) +{ + UNREFERENCED_PARAMETER(driver); + + LOG_ENTER(); + LOG_EXIT(); +} + EXTERN_C DRIVER_INITIALIZE DriverEntry; #ifdef ALLOC_PRAGMA @@ -80,11 +95,13 @@ DriverEntry(_In_ PDRIVER_OBJECT driverObject, _In_ PUNICODE_STRING registryPath) WDF_OBJECT_ATTRIBUTES driverAttrs; WDF_OBJECT_ATTRIBUTES_INIT(&driverAttrs); WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&driverAttrs, OVPN_DRIVER); + driverAttrs.EvtCleanupCallback = OvpnEvtDriverCleanup; + WDFDRIVER wdfDriver; WDF_DRIVER_CONFIG driverConfig; WDF_DRIVER_CONFIG_INIT(&driverConfig, OvpnEvtDeviceAdd); driverConfig.EvtDriverUnload = OvpnEvtDriverUnload; - GOTO_IF_NOT_NT_SUCCESS(done, status, WdfDriverCreate(driverObject, registryPath, &driverAttrs, &driverConfig, WDF_NO_HANDLE)); + GOTO_IF_NOT_NT_SUCCESS(done, status, WdfDriverCreate(driverObject, registryPath, &driverAttrs, &driverConfig, &wdfDriver)); // Register the WSK application wskClientNpi.ClientContext = NULL; @@ -212,26 +229,6 @@ OvpnEvtIoWrite(WDFQUEUE queue, WDFREQUEST request, size_t length) ExReleaseSpinLockShared(&device->SpinLock, kiqrl); } -NTSTATUS -OvpnGetVersion(WDFREQUEST request, _Out_ ULONG_PTR* bytesReturned) -{ - *bytesReturned = 0; - - NTSTATUS status; - POVPN_VERSION version = NULL; - GOTO_IF_NOT_NT_SUCCESS(done, status, WdfRequestRetrieveOutputBuffer(request, sizeof(OVPN_VERSION), (PVOID*)&version, NULL)); - - version->Major = OVPN_DCO_VERSION_MAJOR; - version->Minor = OVPN_DCO_VERSION_MINOR; - version->Patch = OVPN_DCO_VERSION_PATCH; - - *bytesReturned = sizeof(OVPN_VERSION); - -done: - return status; -} - - EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL OvpnEvtIoDeviceControl; _Use_decl_annotations_ @@ -336,6 +333,16 @@ VOID OvpnEvtDeviceCleanup(WDFOBJECT obj) { device->Adapter = WDF_NO_HANDLE; ExReleaseSpinLockExclusive(&device->SpinLock, irql); + // delete control device if there are no devices left + POVPN_DRIVER driverCtx = OvpnGetDriverContext(WdfGetDriver()); + LONG deviceCount = InterlockedDecrement(&driverCtx->DeviceCount); + LOG_INFO("Device count", TraceLoggingValue(deviceCount, "deviceCount")); + if ((deviceCount == 0) && (driverCtx->ControlDevice != NULL)) { + LOG_INFO("Delete control device"); + WdfObjectDelete(driverCtx->ControlDevice); + driverCtx->ControlDevice = NULL; + } + LOG_EXIT(); } @@ -433,6 +440,17 @@ OvpnEvtDeviceAdd(WDFDRIVER wdfDriver, PWDFDEVICE_INIT deviceInit) { WDFDEVICE wdfDevice; GOTO_IF_NOT_NT_SUCCESS(done, status, WdfDeviceCreate(&deviceInit, &objAttributes, &wdfDevice)); + POVPN_DRIVER driverCtx = OvpnGetDriverContext(WdfGetDriver()); + InterlockedIncrement(&driverCtx->DeviceCount); + + LOG_INFO("Device count", TraceLoggingValue(driverCtx->DeviceCount, "count")); + + if (driverCtx->DeviceCount == 1) + { + // create non-exclusive control device to get the version information + GOTO_IF_NOT_NT_SUCCESS(done, status, OvpnCreateControlDevice(wdfDriver)); + } + // this will fail if one device has already been created but that's ok, since // openvpn2/3 accesses devices via Device Interface GUID, and symlink is used only by test client. LOG_IF_NOT_NT_SUCCESS(WdfDeviceCreateSymbolicLink(wdfDevice, &symLink)); diff --git a/Driver.h b/Driver.h index 62476f5..d16a081 100644 --- a/Driver.h +++ b/Driver.h @@ -47,6 +47,8 @@ EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL OvpnEvtIoDeviceControl; typedef struct _OVPN_DRIVER { WSK_PROVIDER_NPI WskProviderNpi; WSK_REGISTRATION WskRegistration; + WDFDEVICE ControlDevice; + LONG DeviceCount; } OVPN_DRIVER, * POVPN_DRIVER; WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(OVPN_DRIVER, OvpnGetDriverContext) diff --git a/PropertySheet.props b/PropertySheet.props index 026767d..f839915 100644 --- a/PropertySheet.props +++ b/PropertySheet.props @@ -3,8 +3,8 @@ 1 - 2 - 1 + 3 + 0 diff --git a/control.cpp b/control.cpp new file mode 100644 index 0000000..32f30a5 --- /dev/null +++ b/control.cpp @@ -0,0 +1,128 @@ +/* + * ovpn-dco-win OpenVPN protocol accelerator for Windows + * + * Copyright (C) 2024- OpenVPN Inc + * + * Author: Lev Stipakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "control.h" +#include "Driver.h" +#include "uapi\ovpn-dco.h" +#include "trace.h" + +_Use_decl_annotations_ +NTSTATUS +OvpnGetVersion(WDFREQUEST request, ULONG_PTR* bytesReturned) +{ + LOG_ENTER(); + + *bytesReturned = 0; + + NTSTATUS status; + POVPN_VERSION version = NULL; + GOTO_IF_NOT_NT_SUCCESS(done, status, WdfRequestRetrieveOutputBuffer(request, sizeof(OVPN_VERSION), (PVOID*)&version, NULL)); + + version->Major = OVPN_DCO_VERSION_MAJOR; + version->Minor = OVPN_DCO_VERSION_MINOR; + version->Patch = OVPN_DCO_VERSION_PATCH; + + LOG_INFO("Version", TraceLoggingValue(version->Major, "Major"), TraceLoggingValue(version->Minor, "Minor"), TraceLoggingValue(version->Patch, "Patch")); + + *bytesReturned = sizeof(OVPN_VERSION); + +done: + LOG_EXIT(); + + return status; +} + +EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL OvpnEvtControlDeviceIOControl; + +VOID +OvpnEvtControlDeviceIOControl(WDFQUEUE queue, WDFREQUEST request, size_t outputBufferLength, size_t inputBufferLength, ULONG ioControlCode) +{ + UNREFERENCED_PARAMETER(queue); + UNREFERENCED_PARAMETER(inputBufferLength); + UNREFERENCED_PARAMETER(outputBufferLength); + + NTSTATUS status = STATUS_SUCCESS; + ULONG_PTR bytesReturned = 0; + + switch (ioControlCode) + { + case OVPN_IOCTL_GET_VERSION: + status = OvpnGetVersion(request, &bytesReturned); + break; + + default: + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + WdfRequestCompleteWithInformation(request, status, bytesReturned); +} + +NTSTATUS +OvpnCreateControlDevice(WDFDRIVER wdfDriver) +{ + LOG_ENTER(); + + DECLARE_CONST_UNICODE_STRING(symLink, L"\\DosDevices\\ovpn-dco-ver"); // this will be used by CreateFile + DECLARE_CONST_UNICODE_STRING(deviceName, L"\\Device\\ovpn-dco-ver"); // this is required tp create symlink + + // allocate control device initialization structure + PWDFDEVICE_INIT deviceInit = WdfControlDeviceInitAllocate(wdfDriver, &SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_R_RES_R); + if (deviceInit == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + // create the control device + WDF_OBJECT_ATTRIBUTES deviceAttributes; + WDF_OBJECT_ATTRIBUTES_INIT(&deviceAttributes); + WDFDEVICE controlDevice; + NTSTATUS status; + + GOTO_IF_NOT_NT_SUCCESS(done, status, WdfDeviceInitAssignName(deviceInit, &deviceName)); + GOTO_IF_NOT_NT_SUCCESS(done, status, WdfDeviceCreate(&deviceInit, &deviceAttributes, &controlDevice)); + + POVPN_DRIVER driverCtx = OvpnGetDriverContext(WdfGetDriver()); + driverCtx->ControlDevice = controlDevice; + + // symlink for control device + GOTO_IF_NOT_NT_SUCCESS(done, status, WdfDeviceCreateSymbolicLink(controlDevice, &symLink)); + + // queue to handle IO + WDF_IO_QUEUE_CONFIG queueConfig; + WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queueConfig, WdfIoQueueDispatchParallel); + queueConfig.EvtIoDeviceControl = OvpnEvtControlDeviceIOControl; + WDFQUEUE queue; + GOTO_IF_NOT_NT_SUCCESS(done, status, WdfIoQueueCreate(controlDevice, &queueConfig, WDF_NO_OBJECT_ATTRIBUTES, &queue)); + + // Complete the control device initialization + WdfControlFinishInitializing(controlDevice); + + done: + if (deviceInit) + { + WdfDeviceInitFree(deviceInit); + } + + LOG_EXIT(); + + return status; +} diff --git a/control.h b/control.h new file mode 100644 index 0000000..8827ed2 --- /dev/null +++ b/control.h @@ -0,0 +1,31 @@ +/* + * ovpn-dco-win OpenVPN protocol accelerator for Windows + * + * Copyright (C) 2024- OpenVPN Inc + * + * Author: Lev Stipakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#pragma once + +#include +#include + +NTSTATUS +OvpnGetVersion(WDFREQUEST request, _Out_ ULONG_PTR* bytesReturned); + +NTSTATUS +OvpnCreateControlDevice(WDFDRIVER wdfDriver); diff --git a/ovpn-dco-win.vcxproj b/ovpn-dco-win.vcxproj index 3fc5a88..d3000a6 100644 --- a/ovpn-dco-win.vcxproj +++ b/ovpn-dco-win.vcxproj @@ -69,6 +69,7 @@ + @@ -82,6 +83,7 @@ + @@ -440,7 +442,7 @@ OVPN_DCO_VERSION_MAJOR=$(OVPN_DCO_VERSION_MAJOR);OVPN_DCO_VERSION_MINOR=$(OVPN_DCO_VERSION_MINOR);OVPN_DCO_VERSION_PATCH=$(OVPN_DCO_VERSION_PATCH);OVPN_DCO_VERSION_STR=$(OVPN_DCO_VERSION_MAJOR).$(OVPN_DCO_VERSION_MINOR).$(OVPN_DCO_VERSION_PATCH);NETADAPTER_VERSION_MAJOR=$(NETADAPTER_VERSION_MAJOR);NETADAPTER_VERSION_MINOR=$(NETADAPTER_VERSION_MINOR);%(PreprocessorDefinitions) - uuid.lib;Netio.lib;cng.lib;%(AdditionalDependencies) + uuid.lib;Netio.lib;cng.lib;wdmsec.lib%(AdditionalDependencies) @@ -456,7 +458,7 @@ OVPN_DCO_VERSION_MAJOR=$(OVPN_DCO_VERSION_MAJOR);OVPN_DCO_VERSION_MINOR=$(OVPN_DCO_VERSION_MINOR);OVPN_DCO_VERSION_PATCH=$(OVPN_DCO_VERSION_PATCH);OVPN_DCO_VERSION_STR=$(OVPN_DCO_VERSION_MAJOR).$(OVPN_DCO_VERSION_MINOR).$(OVPN_DCO_VERSION_PATCH);NETADAPTER_VERSION_MAJOR=$(NETADAPTER_VERSION_MAJOR);NETADAPTER_VERSION_MINOR=$(NETADAPTER_VERSION_MINOR);%(PreprocessorDefinitions) - uuid.lib;Netio.lib;cng.lib;%(AdditionalDependencies) + uuid.lib;Netio.lib;cng.lib;wdmsec.lib%(AdditionalDependencies) @@ -478,7 +480,7 @@ $(ProjectDir)$(Platform)\$(ConfigurationName)\ovpn-dco.DVL.XML - uuid.lib;Netio.lib;cng.lib;%(AdditionalDependencies) + uuid.lib;Netio.lib;cng.lib;wdmsec.lib%(AdditionalDependencies) /Brepro %(AdditionalOptions) DebugFull false @@ -505,7 +507,7 @@ $(ProjectDir)$(Platform)\$(ConfigurationName)\ovpn-dco.DVL.XML - uuid.lib;Netio.lib;cng.lib;%(AdditionalDependencies) + uuid.lib;Netio.lib;cng.lib;wdmsec.lib%(AdditionalDependencies) /Brepro %(AdditionalOptions) DebugFull false @@ -526,7 +528,7 @@ OVPN_DCO_VERSION_MAJOR=$(OVPN_DCO_VERSION_MAJOR);OVPN_DCO_VERSION_MINOR=$(OVPN_DCO_VERSION_MINOR);OVPN_DCO_VERSION_PATCH=$(OVPN_DCO_VERSION_PATCH);OVPN_DCO_VERSION_STR=$(OVPN_DCO_VERSION_MAJOR).$(OVPN_DCO_VERSION_MINOR).$(OVPN_DCO_VERSION_PATCH);NETADAPTER_VERSION_MAJOR=$(NETADAPTER_VERSION_MAJOR);NETADAPTER_VERSION_MINOR=$(NETADAPTER_VERSION_MINOR);%(PreprocessorDefinitions) - uuid.lib;Netio.lib;cng.lib;%(AdditionalDependencies) + uuid.lib;Netio.lib;cng.lib;wdmsec.lib%(AdditionalDependencies) @@ -550,7 +552,7 @@ OVPN_DCO_VERSION_MAJOR=$(OVPN_DCO_VERSION_MAJOR);OVPN_DCO_VERSION_MINOR=$(OVPN_DCO_VERSION_MINOR);OVPN_DCO_VERSION_PATCH=$(OVPN_DCO_VERSION_PATCH);OVPN_DCO_VERSION_STR=$(OVPN_DCO_VERSION_MAJOR).$(OVPN_DCO_VERSION_MINOR).$(OVPN_DCO_VERSION_PATCH);NETADAPTER_VERSION_MAJOR=$(NETADAPTER_VERSION_MAJOR);NETADAPTER_VERSION_MINOR=$(NETADAPTER_VERSION_MINOR);%(PreprocessorDefinitions) - uuid.lib;Netio.lib;cng.lib;%(AdditionalDependencies) + uuid.lib;Netio.lib;cng.lib;wdmsec.lib%(AdditionalDependencies) @@ -580,7 +582,7 @@ $(ProjectDir)$(Platform)\$(ConfigurationName)\ovpn-dco.DVL.XML - uuid.lib;Netio.lib;cng.lib;%(AdditionalDependencies) + uuid.lib;Netio.lib;cng.lib;wdmsec.lib%(AdditionalDependencies) /Brepro %(AdditionalOptions) DebugFull false @@ -607,7 +609,7 @@ $(ProjectDir)$(Platform)\$(ConfigurationName)\ovpn-dco.DVL.XML - uuid.lib;Netio.lib;cng.lib;%(AdditionalDependencies) + uuid.lib;Netio.lib;cng.lib;wdmsec.lib%(AdditionalDependencies) /Brepro %(AdditionalOptions) DebugFull false @@ -628,7 +630,7 @@ OVPN_DCO_VERSION_MAJOR=$(OVPN_DCO_VERSION_MAJOR);OVPN_DCO_VERSION_MINOR=$(OVPN_DCO_VERSION_MINOR);OVPN_DCO_VERSION_PATCH=$(OVPN_DCO_VERSION_PATCH);OVPN_DCO_VERSION_STR=$(OVPN_DCO_VERSION_MAJOR).$(OVPN_DCO_VERSION_MINOR).$(OVPN_DCO_VERSION_PATCH);NETADAPTER_VERSION_MAJOR=$(NETADAPTER_VERSION_MAJOR);NETADAPTER_VERSION_MINOR=$(NETADAPTER_VERSION_MINOR);%(PreprocessorDefinitions) - uuid.lib;Netio.lib;cng.lib;%(AdditionalDependencies) + uuid.lib;Netio.lib;cng.lib;wdmsec.lib%(AdditionalDependencies) @@ -644,7 +646,7 @@ OVPN_DCO_VERSION_MAJOR=$(OVPN_DCO_VERSION_MAJOR);OVPN_DCO_VERSION_MINOR=$(OVPN_DCO_VERSION_MINOR);OVPN_DCO_VERSION_PATCH=$(OVPN_DCO_VERSION_PATCH);OVPN_DCO_VERSION_STR=$(OVPN_DCO_VERSION_MAJOR).$(OVPN_DCO_VERSION_MINOR).$(OVPN_DCO_VERSION_PATCH);NETADAPTER_VERSION_MAJOR=$(NETADAPTER_VERSION_MAJOR);NETADAPTER_VERSION_MINOR=$(NETADAPTER_VERSION_MINOR);%(PreprocessorDefinitions) - uuid.lib;Netio.lib;cng.lib;%(AdditionalDependencies) + uuid.lib;Netio.lib;cng.lib;wdmsec.lib%(AdditionalDependencies) @@ -661,7 +663,7 @@ false - uuid.lib;Netio.lib;cng.lib;%(AdditionalDependencies) + uuid.lib;Netio.lib;cng.lib;wdmsec.lib%(AdditionalDependencies) /Brepro %(AdditionalOptions) DebugFull false @@ -683,7 +685,7 @@ false - uuid.lib;Netio.lib;cng.lib;%(AdditionalDependencies) + uuid.lib;Netio.lib;cng.lib;wdmsec.lib%(AdditionalDependencies) /Brepro %(AdditionalOptions) DebugFull false @@ -710,7 +712,7 @@ $(ProjectDir)$(Platform)\$(ConfigurationName)\ovpn-dco.DVL.XML - uuid.lib;Netio.lib;cng.lib;%(AdditionalDependencies) + uuid.lib;Netio.lib;cng.lib;wdmsec.lib%(AdditionalDependencies) /Brepro %(AdditionalOptions) DebugFull false @@ -737,7 +739,7 @@ $(ProjectDir)$(Platform)\$(ConfigurationName)\ovpn-dco.DVL.XML - uuid.lib;Netio.lib;cng.lib;%(AdditionalDependencies) + uuid.lib;Netio.lib;cng.lib;wdmsec.lib%(AdditionalDependencies) /Brepro %(AdditionalOptions) DebugFull false @@ -758,7 +760,7 @@ OVPN_DCO_VERSION_MAJOR=$(OVPN_DCO_VERSION_MAJOR);OVPN_DCO_VERSION_MINOR=$(OVPN_DCO_VERSION_MINOR);OVPN_DCO_VERSION_PATCH=$(OVPN_DCO_VERSION_PATCH);OVPN_DCO_VERSION_STR=$(OVPN_DCO_VERSION_MAJOR).$(OVPN_DCO_VERSION_MINOR).$(OVPN_DCO_VERSION_PATCH);NETADAPTER_VERSION_MAJOR=$(NETADAPTER_VERSION_MAJOR);NETADAPTER_VERSION_MINOR=$(NETADAPTER_VERSION_MINOR);%(PreprocessorDefinitions) - uuid.lib;Netio.lib;cng.lib;%(AdditionalDependencies) + uuid.lib;Netio.lib;cng.lib;wdmsec.lib%(AdditionalDependencies) @@ -774,7 +776,7 @@ OVPN_DCO_VERSION_MAJOR=$(OVPN_DCO_VERSION_MAJOR);OVPN_DCO_VERSION_MINOR=$(OVPN_DCO_VERSION_MINOR);OVPN_DCO_VERSION_PATCH=$(OVPN_DCO_VERSION_PATCH);OVPN_DCO_VERSION_STR=$(OVPN_DCO_VERSION_MAJOR).$(OVPN_DCO_VERSION_MINOR).$(OVPN_DCO_VERSION_PATCH);NETADAPTER_VERSION_MAJOR=$(NETADAPTER_VERSION_MAJOR);NETADAPTER_VERSION_MINOR=$(NETADAPTER_VERSION_MINOR);%(PreprocessorDefinitions) - uuid.lib;Netio.lib;cng.lib;%(AdditionalDependencies) + uuid.lib;Netio.lib;cng.lib;wdmsec.lib%(AdditionalDependencies) diff --git a/ovpn-dco-win.vcxproj.filters b/ovpn-dco-win.vcxproj.filters index a4603a6..7d0f4cf 100644 --- a/ovpn-dco-win.vcxproj.filters +++ b/ovpn-dco-win.vcxproj.filters @@ -67,6 +67,9 @@ Header Files + + Header Files + @@ -102,6 +105,9 @@ Source Files + + Source Files +