├── .github └── workflows │ └── msbuild.yml ├── .gitignore ├── COPYRIGHT.MIT ├── Driver.cpp ├── Driver.h ├── PropertySheet.props ├── README.md ├── adapter.cpp ├── adapter.h ├── bufferpool.cpp ├── bufferpool.h ├── build_all.bat ├── build_all_nosdv.bat ├── clean.bat ├── control.cpp ├── control.h ├── crypto.cpp ├── crypto.h ├── gui ├── CMakeLists.txt ├── data64.key └── gui.cpp ├── makecabs.bat ├── msm ├── .gitignore ├── build-release-msm.ps1 ├── build.ps1 ├── dllmain.c ├── exports.def ├── installer.vcxproj ├── installer.vcxproj.filters ├── log.c ├── log.h ├── msi.c ├── ovpn-dco.wxs └── sampleinstaller.wxs ├── mss.cpp ├── mss.h ├── netringiterator.h ├── notifyqueue.cpp ├── notifyqueue.h ├── ovpn-dco-autologger.reg ├── ovpn-dco-win.ddf ├── ovpn-dco-win.sln ├── ovpn-dco-win.vcxproj ├── ovpn-dco-win.vcxproj.filters ├── ovpn-dco-win.wprp ├── ovpn-dco.inf ├── ovpn-dco.rc ├── peer.cpp ├── peer.h ├── pktid.cpp ├── pktid.h ├── rxqueue.cpp ├── rxqueue.h ├── socket.cpp ├── socket.h ├── timer.cpp ├── timer.h ├── trace.h ├── trie.cpp ├── trie.h ├── txqueue.cpp ├── txqueue.h └── uapi └── ovpn-dco.h /.github/workflows/msbuild.yml: -------------------------------------------------------------------------------- 1 | name: MSBuild 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | SOLUTION_FILE_PATH: . 7 | 8 | jobs: 9 | build: 10 | runs-on: windows-2019 11 | 12 | strategy: 13 | matrix: 14 | arch: [x64, x86, ARM64] 15 | build_conf: [Debug, Release, Debug-Win11, Release-Win11] 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | with: 20 | submodules: true 21 | 22 | - name: Add MSBuild to PATH 23 | uses: microsoft/setup-msbuild@v1 24 | 25 | - name: Restore artifacts, or run vcpkg, build and cache artifacts 26 | uses: lukka/run-vcpkg@v7.4 27 | with: 28 | vcpkgGitCommitId: 'cafd398be781144787573ca78390e951673c7055' 29 | vcpkgArguments: 'asio' 30 | vcpkgTriplet: '${{ matrix.arch }}-windows' 31 | 32 | - name: Install Wix 3.14 33 | run: | 34 | Invoke-WebRequest -Uri "https://build.openvpn.net/downloads/temp/wix314-toolset.zip" -OutFile wix.zip 35 | Expand-Archive -Path .\wix.zip -DestinationPath wix 36 | Move-Item '.\wix\WiX Toolset v3.14\bin' .\wix 37 | 38 | - name: vcpkg integrate install 39 | shell: cmd 40 | run: | 41 | cd vcpkg && vcpkg.exe integrate install 42 | 43 | - name: Build 44 | working-directory: ${{env.GITHUB_WORKSPACE}} 45 | run: | 46 | msbuild /m /p:Configuration=${{ matrix.build_conf }} /p:Platform="${{ matrix.arch }}" ${{env.SOLUTION_FILE_PATH}} 47 | 48 | - name: Build GUI 49 | working-directory: gui 50 | shell: cmd 51 | run: | 52 | mkdir build&& cd build&& cmake ..&& cmake --build . --config Release 53 | cp ..\data64.key Release 54 | 55 | - name: Build MSM 56 | working-directory: msm 57 | if: ${{ matrix.build_conf == 'Release' }} 58 | run: | 59 | New-Item -Path dist\${{ matrix.arch }}\win10 -type directory -Force 60 | New-Item -Path dist\${{ matrix.arch }}\win11 -type directory -Force 61 | Copy-Item -Path ..\${{ matrix.arch }}\${{ matrix.build_conf }}\ovpn-dco\* -Destination dist\${{ matrix.arch }}\win10\ -Recurse 62 | Copy-Item -Path ..\${{ matrix.arch }}\${{ matrix.build_conf }}\ovpn-dco\* -Destination dist\${{ matrix.arch }}\win11\ -Recurse 63 | .\build.ps1 -Arch ${{ matrix.arch }} -Wix ..\wix 64 | 65 | - uses: actions/upload-artifact@v4 66 | with: 67 | name: ovpn-dco_${{ matrix.arch }}_${{ matrix.build_conf }} 68 | path: | 69 | ${{ matrix.arch }}\${{matrix.build_conf}}\ovpn-dco\ovpn-dco.* 70 | msm\${{ matrix.arch }}\${{matrix.build_conf}}\installer.dll 71 | msm\*.msi 72 | msm\*.msm 73 | gui\build\Release\*.* 74 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # These are some examples of commonly ignored file patterns. 2 | # You should customize this list as applicable to your project. 3 | # Learn more about .gitignore: 4 | # https://www.atlassian.com/git/tutorials/saving-changes/gitignore 5 | 6 | # Node artifact files 7 | node_modules/ 8 | dist/ 9 | 10 | # Compiled Java class files 11 | *.class 12 | 13 | # Compiled Python bytecode 14 | *.py[cod] 15 | 16 | # Log files 17 | *.log 18 | 19 | # Package files 20 | *.jar 21 | 22 | # Maven 23 | target/ 24 | dist/ 25 | 26 | # JetBrains IDE 27 | .idea/ 28 | 29 | # Unit test reports 30 | TEST*.xml 31 | 32 | # Generated by MacOS 33 | .DS_Store 34 | 35 | # Generated by Windows 36 | Thumbs.db 37 | 38 | # Applications 39 | *.app 40 | *.exe 41 | *.war 42 | 43 | # Large media files 44 | *.mp4 45 | *.tiff 46 | *.avi 47 | *.flv 48 | *.mov 49 | *.wmv 50 | 51 | # Visual Studio 52 | .vs 53 | Debug 54 | Release 55 | x64 56 | x86 57 | Win32 58 | sdv 59 | ARM64 60 | amd64 61 | obj 62 | vc*.pdb 63 | 64 | # CodeQL 65 | codeql_db 66 | *.sarif 67 | codeql.build.bat 68 | 69 | # SDV 70 | smvstats.txt 71 | *.DVL.XML 72 | 73 | *.cab 74 | 75 | *.vcxproj.user 76 | 77 | **/out 78 | **/build 79 | **/.vscode 80 | **/signed 81 | msm/tmp -------------------------------------------------------------------------------- /COPYRIGHT.MIT: -------------------------------------------------------------------------------- 1 | Copyright 2021 OpenVPN, Inc 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Driver.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ovpn-dco-win OpenVPN protocol accelerator for Windows 3 | * 4 | * Copyright (C) 2020-2021 OpenVPN Inc 5 | * 6 | * Author: Lev Stipakov 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License version 2 10 | * as published by the Free Software Foundation. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along 18 | * with this program; if not, write to the Free Software Foundation, Inc., 19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | */ 21 | 22 | #pragma once 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "adapter.h" 32 | #include "bufferpool.h" 33 | #include "crypto.h" 34 | #include "notifyqueue.h" 35 | #include "socket.h" 36 | #include "trie.h" 37 | #include "uapi\ovpn-dco.h" 38 | 39 | extern "C" { 40 | DRIVER_INITIALIZE DriverEntry; 41 | } 42 | 43 | EVT_WDF_DRIVER_DEVICE_ADD OvpnEvtDeviceAdd; 44 | 45 | EVT_WDF_IO_QUEUE_IO_READ OvpnEvtIoRead; 46 | EVT_WDF_IO_QUEUE_IO_WRITE OvpnEvtIoWrite; 47 | EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL OvpnEvtIoDeviceControl; 48 | 49 | typedef struct _OVPN_DRIVER { 50 | WSK_PROVIDER_NPI WskProviderNpi; 51 | WSK_REGISTRATION WskRegistration; 52 | WDFDEVICE ControlDevice; 53 | LONG DeviceCount; 54 | } OVPN_DRIVER, * POVPN_DRIVER; 55 | WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(OVPN_DRIVER, OvpnGetDriverContext) 56 | 57 | struct OVPN_DEVICE { 58 | EX_SPIN_LOCK SpinLock; 59 | 60 | // WDF handle associated with this context 61 | WDFDEVICE WdfDevice; 62 | 63 | WDFQUEUE PendingReadsQueue; 64 | WDFQUEUE PendingWritesQueue; 65 | WDFQUEUE PendingNotificationRequestsQueue; 66 | 67 | // NEW_PEER request may be enqueued here if TCP connect doesn't finish immediatelly 68 | WDFQUEUE PendingNewPeerQueue; 69 | 70 | // buffer queue for received decrypted data channel packets 71 | OVPN_BUFFER_QUEUE DataRxBufferQueue; 72 | 73 | // buffer queue for received control channel packets 74 | OVPN_BUFFER_QUEUE ControlRxBufferQueue; 75 | 76 | // pool for OVPN_RX_BUFFER entries 77 | OVPN_RX_BUFFER_POOL RxBufferPool; 78 | 79 | // buffer pool for encrypted data channel and control channel packets to be sent 80 | OVPN_TX_BUFFER_POOL TxBufferPool; 81 | 82 | // queue to store pending userspace notifications 83 | NotifyQueue PendingNotificationsQueue; 84 | 85 | OVPN_STATS Stats; 86 | 87 | BCRYPT_ALG_HANDLE AesAlgHandle; 88 | BCRYPT_ALG_HANDLE ChachaAlgHandle; 89 | 90 | _Guarded_by_(SpinLock) 91 | OvpnSocket Socket; 92 | 93 | _Guarded_by_(SpinLock) 94 | NETADAPTER Adapter; 95 | 96 | _Guarded_by_(SpinLock) 97 | RTL_GENERIC_TABLE Peers; 98 | 99 | _Guarded_by_(SpinLock) 100 | RTL_GENERIC_TABLE PeersByVpn4; 101 | 102 | _Guarded_by_(SpinLock) 103 | RTL_GENERIC_TABLE PeersByVpn6; 104 | 105 | OVPN_MODE Mode; 106 | 107 | IPTrie IRoutesIPV4; 108 | IPTrie IRoutesIPV6; 109 | }; 110 | 111 | typedef OVPN_DEVICE * POVPN_DEVICE; 112 | 113 | WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(OVPN_DEVICE, OvpnGetDeviceContext) 114 | -------------------------------------------------------------------------------- /PropertySheet.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 2 6 | 5 7 | 6 8 | 9 | 10 | 11 | 12 | OVPN_DCO_VERSION_STR=$(OVPN_DCO_VERSION_MAJOR).$(OVPN_DCO_VERSION_MINOR).$(OVPN_DCO_VERSION_PATCH);%(PreprocessorDefinitions) 13 | 14 | 15 | 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);%(PreprocessorDefinitions) 16 | 17 | 18 | 19 | 20 | $(OVPN_DCO_VERSION_MAJOR) 21 | true 22 | 23 | 24 | $(OVPN_DCO_VERSION_MINOR) 25 | true 26 | 27 | 28 | $(OVPN_DCO_VERSION_PATCH) 29 | true 30 | 31 | 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ovpn-dco-win 2 | 3 | ![Github Actions](https://github.com/openvpn/ovpn-dco-win/actions/workflows/msbuild.yml/badge.svg) 4 | 5 | ### Intro 6 | 7 | ovpn-dco-win stands for "OpenVPN Data Channel Offload for Windows". It is a modern Windows driver, which functions as virtual network adapter and implements required functionality to handle the OpenVPN data channel. When using ovpn-dco-win, the OpenVPN software doesn't send data traffic back and forth between user and kernel space (for encryption, decryption and routing), but operations on payload take place in Windows kernel. The driver is being developed using modern frameworks - WDF and NetAdapterCx. Because of that, the code is easier to read and maintain comparison to existing NDIS miniport drivers. Speed-wise the new driver performs significantly better comparison to tap-windows6, so it should eliminate the bottleneck which hampers the performance of OpenVPN on Windows. 8 | 9 | ovpn-dco-win is a default driver starting from OpenVPN 2.6. 10 | 11 | ### Installation 12 | 13 | You can just install the latest [OpenVPN 2.6 release](https://openvpn.net/community-downloads/), which includes signed driver. 14 | 15 | 16 | Alternatively you can get releases from [GitHub](https://github.com/OpenVPN/ovpn-dco-win/releases). 17 | 18 | You can use devcon tool (available as part of WDK) to install the driver: 19 | 20 | ``` 21 | devcon install ovpn-dco.inf ovpn-dco 22 | ``` 23 | 24 | 25 | ### ovpn-dco-cli 26 | 27 | The project includes ovpn-dco-cli command line tool, which works as development test bed, reference client and API usage example. With that you can setup VPN tunnel between two Windows hosts or 28 | between Windows and Linux host using ./ovpn-cli tool from ovpn-dco Linux project. 29 | 30 | To set up Windows <-> Windows tunnel, on first host run: 31 | 32 | ``` 33 | ovpn-dco-cli.exe udp i4 0.0.0.0 1194 192.168.100.200 1194 10.8.0.2 255.255.255.0 10.8.0.1 data64.key 0 34 | ``` 35 | 36 | where "0.0.0.0 1194" local IP address/port to bind the socket, "192.168.100.200 1194" remote address/port, "10.8.0.2" is a local VPN IP. 37 | 38 | On the second Windows host run: 39 | 40 | ``` 41 | ovpn-dco-cli.exe udp i4 0.0.0.0 1194 192.168.100.100 1194 10.8.0.1 255.255.255.0 10.8.0.1 data64.key 1 42 | ``` 43 | 44 | Note that remote IP, VPN IP and key direction (last 0/1 digit) are different. 45 | 46 | To set up tunnel between Windows and Linux, run on the second (Linux) host: 47 | 48 | ``` 49 | # ip link add dev ovpn0 type ovpn-dco 50 | # ip link set ovpn0 up 51 | # ip link set mtu 1428 dev ovpn0 52 | 53 | # ip addr add dev ovpn0 10.8.0.1/24 54 | 55 | # tests/ovpn-cli ovpn0 new_peer 1194 0 192.168.100.100 1194 10.8.0.2 56 | # tests/ovpn-cli ovpn0 new_key 0 aes 1 tests/data64.key 57 | ``` 58 | 59 | where 192.168.100.150 is a Linux host IP address, 192.168.100.100 is a Windows host IP address. 60 | 61 | After you've established tunnel, you should be able to ping hosts (10.8.0.1 <-> 10.8.0.1) and run iperf tests (iperf3 -s 10.8.0.2 on the first host, iperf3 -c 10.8.0.2 on the second) via VPN tunnel. 62 | 63 | Please note that using ovpn-dco-cli tool in production is a very bad idea, because it doesn't do any key negotiation and use a static key (data64.key) instead. 64 | 65 | 66 | ### API Usage 67 | 68 | To use the driver, client needs to get file handle by calling CreateFile. One can either use symbolic link or device interface to get the file path. Here is example from ovpn-dco tool (see below): 69 | 70 | ``` 71 | HANDLE h = CreateFileA("\\\\.\\ovpn-dco", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, NULL); 72 | ``` 73 | 74 | After getting handle, client uses DeviceIOControl and Read/WriteFile calls to set up connection and send/receive control packets. See uapi.h for the list of IOCTL commands and ovpn-dco-cli.cpp on how to use those. 75 | 76 | * First, client needs to initialize peer with OVPN_IOCTL_NEW_PEER command. Client passes OVPN_NEW_PEER structure, which contains local/remote IP address/port (either IPv4 or IPv6) and transport protocol (TCP or UDP). 77 | 78 | * After initializing peer, client passed cipher algorithm (supported are "none" and "AES-GCM"), crypto keys and peer-id as part of OVPN_CRYPTO_DATA structure using OVPN_IOCTL_NEW_KEY command. Description of openvpn crypto is beyond the scope of this document. 79 | 80 | * After setting up crypto, client may set keepalive parameters (OVPN_SET_PEER struct) for the session via OVPN_IOCTL_SET_PEER command. Keepalive interval defines how often to send special keepalive packet in the absence of outgoing data traffic. Keepalive timeout defines when to notify userspace (by completing read request with an error) in the absence of incoming data traffic. 81 | 82 | * To start VPN session, client sends OVPN_IOCTL_START_VPN command. This signals driver to start network adapter. 83 | 84 | * After starting VPN session, client sets up network adapter (IP address, netmask, gateway) and routing. 85 | 86 | To send and receive control channel packets, client uses Read/WriteFile calls. It is recommended to use overlapped IO by passing FILE_FLAG_OVERLAPPED to CreateFile call and OVERLAPPED structure to Read/WriteFile calls. ovpn-dco-cli uses ASIO, which abstracts those low-level details. 87 | 88 | 89 | ### OpenVPN support 90 | 91 | ovpn-dco-win driver is used by default OpenVPN starting from 2.6 release. 92 | 93 | OpenVPN3 also supports ovpn-dco-win in the latest master branch. 94 | 95 | ### Logging 96 | 97 | Logging is performed via TraceLogging API, which is based on ETW. To see logs on a target machine: 98 | 99 | 1. Run `traceview.exe` as administrator 100 | 2. File -> Create New Log Session 101 | 3. Manually Entered Control GUID -> `4970F9cf-2c0c-4f11-b1cc-e3a1e9958833` -> OK 102 | 4. Choose Source Of Decoding Information -> Auto -> OK 103 | 5. Press "Next" -> "Finish" 104 | 105 | To collect logs for analysis: 106 | 107 | 1. Run administrator command prompt 108 | 2. Run `wpr -start ovpn-dco-win.wprp` (wprp file is in driver source tree) 109 | 3. Interact with the driver 110 | 4. To stop log collection, run `wpr -stop ovpn-dco-win.etl` 111 | 112 | The etl file could be opened, for example, by Windows Performance Analyzer (`wpa.exe`). 113 | 114 | To see logs in attached debbuger (windbg), use `tracelog.exe` in administrator command prompt: 115 | 116 | * `tracelog -start MyTrace -guid #4970F9cf-2c0c-4f11-b1cc-e3a1e9958833 -rt -kd` 117 | 118 | If you experience boot issues, you might want to enable AutoLogger session. Run `ovpn-dco-autologger.reg` file, which will create neccessary registry keys, and then reboot. 119 | 120 | Driver logs will be stored in `%SystemRoot%\System32\LogFiles\WMI\ovpn-dco.etl`. 121 | 122 | ### Reproducible builds 123 | 124 | Release builds of ovpn-dco-win are reproducible, meaning that when you use the same build environment, you should get the exact same ovpn-dco.sys binary. That way, you can verify that a driver released in binary form is indeed compiled from the source code in this repository. This is useful because Microsoft's driver signing requirements make it difficult to run self-compiled drivers. 125 | 126 | Released drivers are built with Windows 11 EWDK (Enterprise Windows Driver Kit). Despite the name, it also works on Windows 10. 127 | You can download it here: https://docs.microsoft.com/en-us/windows-hardware/drivers/download-the-wdk 128 | 129 | If you have obtained a ovpn-dco.sys file, you can verify it as follows: 130 | 131 | 1. Remove the signature from the ovpn-dco.sys file you received: `signtool remove /s ovpn-dco.sys` 132 | 2. Clone this repository and check out the version that your file is supposed to be. 133 | 3. Mount the Windows 11 EWDK iso image. 134 | 4. In Powershell, navigate to the virtual drive and run `LaunchBuildEnv.cmd`. 135 | 5. Navigate to the ovpn-dco-win project directory. 136 | 6. Build the driver: `msbuild /p:platform= /p:configuration=release /p:signmode=off ovpn-dco-win.vcxproj /t:Build` where `` is `Win32`, `x64`, `ARM` or `ARM64`. 137 | 7. Check that `/Release/ovpn-dco.sys` and the file from step 1 have the same SHA256 hash. 138 | 139 | 140 | ### Limitations 141 | 142 | * Minimum supported Windows version is Windows 10 20H1. 143 | * Supported cipher are AES-128(-192-256)-GCM and ChaCha20-Poly1305 (starting from Windows 11 / Server 2022) 144 | 145 | 146 | ### Questions 147 | 148 | Contact Lev Stipakov [lev@openvpn.net](mailto:lev@openvpn.net) (lev__ on #openvpn-devel) 149 | -------------------------------------------------------------------------------- /adapter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * ovpn-dco-win OpenVPN protocol accelerator for Windows 3 | * 4 | * Copyright (C) 2020-2021 OpenVPN Inc 5 | * 6 | * Author: Lev Stipakov 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License version 2 10 | * as published by the Free Software Foundation. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along 18 | * with this program; if not, write to the Free Software Foundation, Inc., 19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | */ 21 | 22 | #include "adapter.h" 23 | #include "txqueue.h" 24 | #include "rxqueue.h" 25 | #include "trace.h" 26 | #include "driver.h" 27 | 28 | // maximum link speed for send and recv in bps 29 | #define OVPN_MEDIA_MAX_SPEED 1'000'000'000 30 | 31 | #if (NETADAPTER_VERSION_MAJOR >= 2) && (NETADAPTER_VERSION_MINOR >= 1) 32 | 33 | EVT_NET_ADAPTER_OFFLOAD_SET_RX_CHECKSUM OvpnEvtAdapterOffloadSetRxChecksum; 34 | 35 | _Use_decl_annotations_ 36 | VOID 37 | OvpnEvtAdapterOffloadSetRxChecksum(NETADAPTER netAdapter, NETOFFLOAD offload) 38 | { 39 | UNREFERENCED_PARAMETER(netAdapter); 40 | 41 | LOG_INFO("Checksum offload settings changed", 42 | TraceLoggingValue(NetOffloadIsRxChecksumIPv4Enabled(offload), "ipv4"), 43 | TraceLoggingValue(NetOffloadIsRxChecksumTcpEnabled(offload), "tcp"), 44 | TraceLoggingValue(NetOffloadIsRxChecksumUdpEnabled(offload), "udp")); 45 | 46 | // we don't really care about those settings, since we always report tunnel checksums as valid 47 | } 48 | 49 | static 50 | VOID 51 | OvpnAdapterSetOffloadCapabilities(_In_ POVPN_ADAPTER adapter) 52 | { 53 | // Configure the hardware's Rx checksum offload capabilities 54 | NET_ADAPTER_OFFLOAD_RX_CHECKSUM_CAPABILITIES rxChecksumOffloadCapabilities; 55 | 56 | NET_ADAPTER_OFFLOAD_RX_CHECKSUM_CAPABILITIES_INIT(&rxChecksumOffloadCapabilities, OvpnEvtAdapterOffloadSetRxChecksum); 57 | 58 | // Set the current Rx checksum offload capabilities and register the callback for future changes in active capabilities 59 | NetAdapterOffloadSetRxChecksumCapabilities(adapter->NetAdapter, &rxChecksumOffloadCapabilities); 60 | } 61 | 62 | #endif 63 | 64 | static 65 | VOID 66 | OvpnAdapterSetDatapathCapabilities(_In_ POVPN_ADAPTER adapter) 67 | { 68 | NET_ADAPTER_TX_CAPABILITIES txCapabilities; 69 | NET_ADAPTER_TX_CAPABILITIES_INIT(&txCapabilities, 1); 70 | 71 | NET_ADAPTER_RX_CAPABILITIES rxCapabilities; 72 | NET_ADAPTER_RX_CAPABILITIES_INIT_SYSTEM_MANAGED(&rxCapabilities, 65536, 1); 73 | 74 | NetAdapterSetDataPathCapabilities(adapter->NetAdapter, &txCapabilities, &rxCapabilities); 75 | } 76 | 77 | static 78 | VOID 79 | OvpnAdapterSetLinkLayerCapabilities(_In_ POVPN_ADAPTER adapter) 80 | { 81 | ULONG64 maxXmitLinkSpeed = OVPN_MEDIA_MAX_SPEED; 82 | ULONG64 maxRcvLinkSpeed = OVPN_MEDIA_MAX_SPEED; 83 | 84 | NET_ADAPTER_LINK_LAYER_CAPABILITIES linkLayerCapabilities; 85 | NET_ADAPTER_LINK_LAYER_CAPABILITIES_INIT(&linkLayerCapabilities, 86 | maxXmitLinkSpeed, 87 | maxRcvLinkSpeed); 88 | 89 | NetAdapterSetLinkLayerCapabilities(adapter->NetAdapter, &linkLayerCapabilities); 90 | NetAdapterSetLinkLayerMtuSize(adapter->NetAdapter, OVPN_DCO_MTU_MAX); 91 | } 92 | 93 | _Use_decl_annotations_ 94 | VOID 95 | OvpnAdapterSetLinkState(POVPN_ADAPTER adapter, NET_IF_MEDIA_CONNECT_STATE state) 96 | { 97 | NET_ADAPTER_LINK_STATE linkState; 98 | NET_ADAPTER_LINK_STATE_INIT(&linkState, 99 | OVPN_MEDIA_MAX_SPEED, 100 | state, 101 | NET_IF_MEDIA_DUPLEX_STATE::MediaDuplexStateFull, 102 | NET_ADAPTER_PAUSE_FUNCTION_TYPE::NetAdapterPauseFunctionTypeUnsupported, 103 | NET_ADAPTER_AUTO_NEGOTIATION_FLAGS::NetAdapterAutoNegotiationFlagNone); 104 | NetAdapterSetLinkState(adapter->NetAdapter, &linkState); 105 | } 106 | 107 | EVT_NET_ADAPTER_CREATE_TXQUEUE OvpnEvtAdapterCreateTxQueue; 108 | 109 | _Use_decl_annotations_ 110 | NTSTATUS 111 | OvpnEvtAdapterCreateTxQueue(NETADAPTER netAdapter, _Inout_ NETTXQUEUE_INIT* txQueueInit) 112 | { 113 | WDF_OBJECT_ATTRIBUTES txAttributes; 114 | NET_PACKET_QUEUE_CONFIG txConfig; 115 | 116 | NET_PACKET_QUEUE_CONFIG_INIT(&txConfig, OvpnEvtTxQueueAdvance, OvpnEvtTxQueueSetNotificationEnabled, OvpnEvtTxQueueCancel); 117 | 118 | NETPACKETQUEUE txQueue; 119 | WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&txAttributes, OVPN_TXQUEUE); 120 | NTSTATUS status; 121 | GOTO_IF_NOT_NT_SUCCESS(done, status, NetTxQueueCreate(txQueueInit, &txAttributes, &txConfig, &txQueue)); 122 | 123 | OvpnTxQueueInitialize(txQueue, OvpnGetAdapterContext(netAdapter)); 124 | 125 | done: 126 | return status; 127 | } 128 | 129 | EVT_NET_ADAPTER_CREATE_RXQUEUE OvpnEvtAdapterCreateRxQueue; 130 | 131 | _Use_decl_annotations_ 132 | NTSTATUS 133 | OvpnEvtAdapterCreateRxQueue(NETADAPTER netAdapter, NETRXQUEUE_INIT* rxQueueInit) 134 | { 135 | LOG_ENTER(); 136 | 137 | WDF_OBJECT_ATTRIBUTES rxAttributes; 138 | NET_PACKET_QUEUE_CONFIG rxConfig; 139 | 140 | WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&rxAttributes, OVPN_RXQUEUE); 141 | rxAttributes.EvtDestroyCallback = OvpnEvtRxQueueDestroy; 142 | 143 | NET_PACKET_QUEUE_CONFIG_INIT(&rxConfig, OvpnEvtRxQueueAdvance, OvpnEvtRxQueueSetNotificationEnabled, OvpnEvtRxQueueCancel); 144 | rxConfig.EvtStart = OvpnEvtRxQueueStart; 145 | rxConfig.EvtStop = OvpnEvtRxQueueStop; 146 | 147 | NETPACKETQUEUE netPacketQueue; 148 | NTSTATUS status; 149 | GOTO_IF_NOT_NT_SUCCESS(done, status, NetRxQueueCreate(rxQueueInit, &rxAttributes, &rxConfig, &netPacketQueue)); 150 | 151 | POVPN_ADAPTER adapterContext = OvpnGetAdapterContext(netAdapter); 152 | OvpnRxQueueInitialize(netPacketQueue, adapterContext); 153 | 154 | done: 155 | LOG_EXIT(); 156 | 157 | return status; 158 | } 159 | 160 | _Use_decl_annotations_ 161 | NTSTATUS 162 | OvpnAdapterCreate(OVPN_DEVICE * device) { 163 | NTSTATUS status = STATUS_SUCCESS; 164 | 165 | LOG_ENTER(); 166 | 167 | NETADAPTER_INIT* adapterInit = NetAdapterInitAllocate(device->WdfDevice); 168 | if (adapterInit == NULL) { 169 | LOG_ERROR("NetAdapterInitAllocate() failed"); 170 | return STATUS_MEMORY_NOT_ALLOCATED; 171 | } 172 | 173 | NET_ADAPTER_DATAPATH_CALLBACKS datapathCallbacks; 174 | NET_ADAPTER_DATAPATH_CALLBACKS_INIT(&datapathCallbacks, OvpnEvtAdapterCreateTxQueue, OvpnEvtAdapterCreateRxQueue); 175 | NetAdapterInitSetDatapathCallbacks(adapterInit, &datapathCallbacks); 176 | 177 | WDF_OBJECT_ATTRIBUTES adapterAttributes; 178 | WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&adapterAttributes, OVPN_ADAPTER); 179 | 180 | NETADAPTER netAdapter; 181 | GOTO_IF_NOT_NT_SUCCESS(createfailure, status, NetAdapterCreate(adapterInit, &adapterAttributes, &netAdapter)); 182 | 183 | POVPN_ADAPTER adapter = OvpnGetAdapterContext(netAdapter); 184 | adapter->NetAdapter = netAdapter; 185 | adapter->WdfDevice = device->WdfDevice; 186 | 187 | NetAdapterInitFree(adapterInit); 188 | adapterInit = NULL; 189 | 190 | OvpnAdapterSetDatapathCapabilities(adapter); 191 | OvpnAdapterSetLinkLayerCapabilities(adapter); 192 | 193 | #if (NETADAPTER_VERSION_MAJOR >= 2) && (NETADAPTER_VERSION_MINOR >= 1) 194 | OvpnAdapterSetOffloadCapabilities(adapter); 195 | #endif 196 | 197 | OvpnAdapterSetLinkState(adapter, MediaConnectStateDisconnected); 198 | 199 | status = NetAdapterStart(adapter->NetAdapter); 200 | 201 | KIRQL irql = ExAcquireSpinLockExclusive(&device->SpinLock); 202 | device->Adapter = netAdapter; 203 | ExReleaseSpinLockExclusive(&device->SpinLock, irql); 204 | 205 | goto done; 206 | 207 | createfailure: 208 | NetAdapterInitFree(adapterInit); 209 | adapterInit = NULL; 210 | 211 | done: 212 | LOG_EXIT(); 213 | 214 | return status; 215 | } 216 | 217 | VOID OvpnAdapterNotifyRx(NETADAPTER netAdapter) 218 | { 219 | if (netAdapter == WDF_NO_HANDLE) { 220 | LOG_ERROR("Adapter not initialized"); 221 | return; 222 | } 223 | 224 | NETPACKETQUEUE rxQueue = OvpnGetAdapterContext(netAdapter)->RxQueue; 225 | if (rxQueue == WDF_NO_HANDLE) { 226 | LOG_WARN("rxQueue not initialized"); 227 | return; 228 | } 229 | 230 | POVPN_RXQUEUE queueContext = OvpnGetRxQueueContext(rxQueue); 231 | if (InterlockedExchange(&queueContext->NotificationEnabled, FALSE) == TRUE) 232 | NetRxQueueNotifyMoreReceivedPacketsAvailable(rxQueue); 233 | } 234 | -------------------------------------------------------------------------------- /adapter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ovpn-dco-win OpenVPN protocol accelerator for Windows 3 | * 4 | * Copyright (C) 2020-2021 OpenVPN Inc 5 | * 6 | * Author: Lev Stipakov 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License version 2 10 | * as published by the Free Software Foundation. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along 18 | * with this program; if not, write to the Free Software Foundation, Inc., 19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | */ 21 | 22 | #pragma once 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #define OVPN_DCO_MTU_MAX 1500 29 | 30 | // Context for NETADAPTER 31 | struct OVPN_ADAPTER 32 | { 33 | // WDF handles associated with this context 34 | NETADAPTER NetAdapter; 35 | WDFDEVICE WdfDevice; 36 | 37 | // Handle to Rx Queue 38 | NETPACKETQUEUE RxQueue; 39 | }; 40 | 41 | typedef OVPN_ADAPTER * POVPN_ADAPTER; 42 | 43 | WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(OVPN_ADAPTER, OvpnGetAdapterContext); 44 | 45 | struct OVPN_DEVICE; 46 | 47 | _Must_inspect_result_ 48 | _IRQL_requires_(PASSIVE_LEVEL) 49 | _IRQL_requires_same_ 50 | NTSTATUS 51 | OvpnAdapterCreate(OVPN_DEVICE* device); 52 | 53 | // notify NetAdapter (if it is ready) that more packets are available 54 | VOID 55 | OvpnAdapterNotifyRx(NETADAPTER netAdapter); 56 | 57 | VOID 58 | OvpnAdapterSetLinkState(_In_ POVPN_ADAPTER adapter, NET_IF_MEDIA_CONNECT_STATE state); 59 | 60 | #define OVPN_PAYLOAD_BACKFILL 26 // 2 + 4 + 4 + 16 -> tcp packet size + data_v2 + pktid + auth-tag; -------------------------------------------------------------------------------- /bufferpool.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * ovpn-dco-win OpenVPN protocol accelerator for Windows 3 | * 4 | * Copyright (C) 2020-2021 OpenVPN Inc 5 | * 6 | * Author: Lev Stipakov 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License version 2 10 | * as published by the Free Software Foundation. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along 18 | * with this program; if not, write to the Free Software Foundation, Inc., 19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | */ 21 | 22 | #pragma once 23 | 24 | #include 25 | 26 | #include "adapter.h" 27 | #include "bufferpool.h" 28 | #include "trace.h" 29 | 30 | #define OVPN_BUFFER_HEADROOM 30 // we prepend TCP packet size (2 bytes) and max crypto overhead (28 bytes) 31 | 32 | // good enough limit for in-flight packets 33 | constexpr auto MAX_POOL_SIZE = 100'000; 34 | 35 | struct OVPN_BUFFER_POOL_IMPL 36 | { 37 | LIST_ENTRY ListHead; 38 | KSPIN_LOCK Lock; 39 | UINT32 ItemSize; 40 | LONG PoolSize; 41 | VOID* Context; 42 | CHAR* Tag; 43 | }; 44 | 45 | struct OVPN_BUFFER_QUEUE_IMPL 46 | { 47 | LIST_ENTRY ListHead; 48 | KSPIN_LOCK Lock; 49 | }; 50 | 51 | NTSTATUS 52 | OvpnBufferQueueCreate(OVPN_BUFFER_QUEUE* handle) 53 | { 54 | OVPN_BUFFER_QUEUE_IMPL* queue = (OVPN_BUFFER_QUEUE_IMPL*)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(OVPN_BUFFER_QUEUE_IMPL), 'ovpn'); 55 | if (!queue) 56 | return STATUS_MEMORY_NOT_ALLOCATED; 57 | 58 | InitializeListHead(&queue->ListHead); 59 | KeInitializeSpinLock(&queue->Lock); 60 | 61 | *handle = (OVPN_BUFFER_QUEUE)queue; 62 | return STATUS_SUCCESS; 63 | } 64 | 65 | VOID 66 | OvpnBufferQueueEnqueue(OVPN_BUFFER_QUEUE handle, PLIST_ENTRY listEntry) 67 | { 68 | OVPN_BUFFER_QUEUE_IMPL* queue = (OVPN_BUFFER_QUEUE_IMPL*)handle; 69 | 70 | ExInterlockedInsertTailList(&queue->ListHead, listEntry, &queue->Lock); 71 | } 72 | 73 | VOID 74 | OvpnBufferQueueEnqueueHead(OVPN_BUFFER_QUEUE handle, PLIST_ENTRY listEntry) 75 | { 76 | OVPN_BUFFER_QUEUE_IMPL* queue = (OVPN_BUFFER_QUEUE_IMPL*)handle; 77 | 78 | ExInterlockedInsertHeadList(&queue->ListHead, listEntry, &queue->Lock); 79 | } 80 | 81 | LIST_ENTRY* 82 | OvpnBufferQueueDequeue(OVPN_BUFFER_QUEUE handle) 83 | { 84 | LIST_ENTRY* entry = NULL; 85 | OVPN_BUFFER_QUEUE_IMPL* queue = (OVPN_BUFFER_QUEUE_IMPL*)handle; 86 | 87 | entry = ExInterlockedRemoveHeadList(&queue->ListHead, &queue->Lock); 88 | 89 | return entry; 90 | } 91 | 92 | static 93 | NTSTATUS 94 | OvpnBufferPoolCreate(OVPN_BUFFER_POOL* handle, UINT32 itemSize, CHAR* tag, VOID* ctx) 95 | { 96 | NTSTATUS status = STATUS_SUCCESS; 97 | *handle = NULL; 98 | OVPN_BUFFER_POOL_IMPL* pool = NULL; 99 | 100 | pool = (OVPN_BUFFER_POOL_IMPL*)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(OVPN_BUFFER_POOL_IMPL), 'ovpn'); 101 | if (!pool) { 102 | status = STATUS_MEMORY_NOT_ALLOCATED; 103 | goto error; 104 | } 105 | 106 | InitializeListHead(&pool->ListHead); 107 | KeInitializeSpinLock(&pool->Lock); 108 | 109 | *handle = (OVPN_BUFFER_POOL)pool; 110 | 111 | pool->ItemSize = itemSize; 112 | pool->Tag = tag; 113 | pool->Context = ctx; 114 | 115 | goto done; 116 | 117 | error: 118 | if (pool) { 119 | ExFreePoolWithTag(pool, 'ovpn'); 120 | pool = NULL; 121 | } 122 | 123 | done: 124 | return status; 125 | } 126 | 127 | _Use_decl_annotations_ 128 | NTSTATUS 129 | OvpnTxBufferPoolCreate(OVPN_TX_BUFFER_POOL* handle, VOID* ctx) 130 | { 131 | return OvpnBufferPoolCreate((OVPN_BUFFER_POOL*)handle, sizeof(OVPN_TX_BUFFER) + OVPN_DCO_MTU_MAX + OVPN_BUFFER_HEADROOM, "tx", ctx); 132 | } 133 | 134 | VOID* 135 | OvpnTxBufferPoolGetContext(OVPN_TX_BUFFER_POOL handle) 136 | { 137 | OVPN_BUFFER_POOL_IMPL* pool = (OVPN_BUFFER_POOL_IMPL*)handle; 138 | return pool->Context; 139 | } 140 | 141 | template 142 | static 143 | VOID 144 | OvpnBufferPoolGet(OVPN_BUFFER_POOL handle, POOL_ENTRY** entry) { 145 | OVPN_BUFFER_POOL_IMPL* pool = (OVPN_BUFFER_POOL_IMPL*)handle; 146 | 147 | LIST_ENTRY* slist_entry = ExInterlockedRemoveHeadList(&pool->ListHead, &pool->Lock); 148 | if (slist_entry) { 149 | *entry = CONTAINING_RECORD(slist_entry, POOL_ENTRY, PoolListEntry); 150 | } else { 151 | if (pool->PoolSize > MAX_POOL_SIZE) 152 | { 153 | *entry = NULL; 154 | LOG_ERROR("Pool size is too large", TraceLoggingValue(pool->Tag, "tag"), TraceLoggingValue(pool->PoolSize, "size")); 155 | return; 156 | } 157 | *entry = (POOL_ENTRY*)ExAllocatePool2(POOL_FLAG_NON_PAGED, pool->ItemSize, 'ovpn'); 158 | if (*entry) 159 | { 160 | InterlockedIncrement(&pool->PoolSize); 161 | if ((pool->PoolSize % 256) == 0) { 162 | LOG_INFO("Pool size", TraceLoggingValue(pool->Tag, "tag"), TraceLoggingValue(pool->PoolSize, "size")); 163 | } 164 | } 165 | } 166 | } 167 | 168 | _Use_decl_annotations_ 169 | NTSTATUS 170 | OvpnTxBufferPoolGet(OVPN_TX_BUFFER_POOL handle, OVPN_TX_BUFFER** buffer) 171 | { 172 | OvpnBufferPoolGet((OVPN_BUFFER_POOL)handle, buffer); 173 | if (*buffer == NULL) 174 | return STATUS_INSUFFICIENT_RESOURCES; 175 | 176 | (*buffer)->Pool = handle; 177 | 178 | (*buffer)->Mdl = IoAllocateMdl(*buffer, ((OVPN_BUFFER_POOL_IMPL*)handle)->ItemSize, FALSE, FALSE, NULL); 179 | if (((*buffer)->Mdl) == NULL) 180 | { 181 | OvpnTxBufferPoolPut(*buffer); 182 | *buffer = NULL; 183 | return STATUS_INSUFFICIENT_RESOURCES; 184 | } 185 | 186 | MmBuildMdlForNonPagedPool((*buffer)->Mdl); 187 | 188 | (*buffer)->Data = (*buffer)->Head + OVPN_BUFFER_HEADROOM; 189 | (*buffer)->Tail = (*buffer)->Data; 190 | 191 | (*buffer)->Len = 0; 192 | 193 | RtlZeroMemory(&(*buffer)->WskBufList, sizeof(WSK_BUF_LIST)); 194 | 195 | (*buffer)->IoQueue = WDF_NO_HANDLE; 196 | 197 | return STATUS_SUCCESS; 198 | } 199 | 200 | _Use_decl_annotations_ 201 | NTSTATUS 202 | OvpnRxBufferPoolGet(OVPN_RX_BUFFER_POOL handle, OVPN_RX_BUFFER** buffer) 203 | { 204 | OvpnBufferPoolGet((OVPN_BUFFER_POOL)handle, buffer); 205 | if (*buffer == NULL) 206 | return STATUS_INSUFFICIENT_RESOURCES; 207 | 208 | (*buffer)->Data = (*buffer)->Head; 209 | (*buffer)->Tail = (*buffer)->Data; 210 | (*buffer)->Pool = handle; 211 | (*buffer)->Len = 0; 212 | 213 | return STATUS_SUCCESS; 214 | } 215 | 216 | template 217 | static 218 | VOID 219 | OvpnBufferPoolPut(POOL_ENTRY* pool_entry) 220 | { 221 | OVPN_BUFFER_POOL_IMPL* pool = (OVPN_BUFFER_POOL_IMPL*)pool_entry->Pool; 222 | 223 | ExInterlockedInsertTailList(&pool->ListHead, &(pool_entry->PoolListEntry), &pool->Lock); 224 | } 225 | 226 | _Use_decl_annotations_ 227 | VOID 228 | OvpnTxBufferPoolPut(OVPN_TX_BUFFER* buffer) 229 | { 230 | if (buffer->Mdl) 231 | IoFreeMdl(buffer->Mdl); 232 | 233 | OvpnBufferPoolPut(buffer); 234 | } 235 | 236 | _Use_decl_annotations_ 237 | VOID 238 | OvpnRxBufferPoolPut(_In_ OVPN_RX_BUFFER* buffer) 239 | { 240 | OvpnBufferPoolPut(buffer); 241 | } 242 | 243 | 244 | template 245 | static 246 | VOID 247 | OvpnBufferPoolDelete(OVPN_BUFFER_POOL handle) 248 | { 249 | if (handle == NULL) 250 | return; 251 | 252 | OVPN_BUFFER_POOL_IMPL* pool = (OVPN_BUFFER_POOL_IMPL*)handle; 253 | 254 | LIST_ENTRY* list_entry = NULL; 255 | while ((list_entry = ExInterlockedRemoveHeadList(&pool->ListHead, &pool->Lock)) != NULL) { 256 | POOL_ENTRY* entry = CONTAINING_RECORD(list_entry, POOL_ENTRY, PoolListEntry); 257 | ExFreePoolWithTag(entry, 'ovpn'); 258 | } 259 | 260 | ExFreePoolWithTag(pool, 'ovpn'); 261 | } 262 | 263 | VOID 264 | OvpnRxBufferPoolDelete(OVPN_BUFFER_POOL handle) 265 | { 266 | OvpnBufferPoolDelete(handle); 267 | } 268 | 269 | VOID 270 | OvpnTxBufferPoolDelete(OVPN_BUFFER_POOL handle) 271 | { 272 | OvpnBufferPoolDelete(handle); 273 | } 274 | 275 | _Use_decl_annotations_ 276 | UCHAR* 277 | OvpnTxBufferPush(OVPN_TX_BUFFER* buffer, SIZE_T len) 278 | { 279 | buffer->Data -= len; 280 | buffer->Len += len; 281 | 282 | return buffer->Data; 283 | } 284 | 285 | _Use_decl_annotations_ 286 | NTSTATUS 287 | OvpnRxBufferPoolCreate(OVPN_RX_BUFFER_POOL* handle) 288 | { 289 | return OvpnBufferPoolCreate((OVPN_BUFFER_POOL*)handle, sizeof(OVPN_RX_BUFFER) + OVPN_SOCKET_RX_PACKET_BUFFER_SIZE, "rx", NULL); 290 | } 291 | 292 | VOID 293 | OvpnBufferQueueDelete(OVPN_BUFFER_QUEUE handle) 294 | { 295 | if (handle == NULL) 296 | return; 297 | 298 | OVPN_BUFFER_QUEUE_IMPL* queue = (OVPN_BUFFER_QUEUE_IMPL*)handle; 299 | 300 | ExFreePoolWithTag(queue, 'ovpn'); 301 | } 302 | -------------------------------------------------------------------------------- /bufferpool.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ovpn-dco-win OpenVPN protocol accelerator for Windows 3 | * 4 | * Copyright (C) 2020-2021 OpenVPN Inc 5 | * 6 | * Author: Lev Stipakov 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License version 2 10 | * as published by the Free Software Foundation. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along 18 | * with this program; if not, write to the Free Software Foundation, Inc., 19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | */ 21 | 22 | #pragma once 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #define OVPN_SOCKET_RX_PACKET_BUFFER_SIZE 2048 30 | 31 | DECLARE_HANDLE(OVPN_BUFFER_POOL); 32 | DECLARE_HANDLE(OVPN_TX_BUFFER_POOL); 33 | DECLARE_HANDLE(OVPN_RX_BUFFER_POOL); 34 | 35 | DECLARE_HANDLE(OVPN_BUFFER_QUEUE); 36 | 37 | struct OVPN_TX_BUFFER 38 | { 39 | WSK_BUF_LIST WskBufList; 40 | 41 | // points to the beginning of data 42 | PUCHAR Data; 43 | 44 | // points to the end of data 45 | PUCHAR Tail; 46 | 47 | // data length 48 | SIZE_T Len; 49 | 50 | // describes MDL for buffer, used by Winsock Kernel 51 | PMDL Mdl; 52 | 53 | OVPN_TX_BUFFER_POOL Pool; 54 | 55 | LIST_ENTRY PoolListEntry; 56 | 57 | // used when sending from EvtIoWrite 58 | WDFQUEUE IoQueue; 59 | 60 | #pragma warning(suppress:4200) //nonstandard extension used: zero-sized array in struct/union 61 | UCHAR Head[]; 62 | }; 63 | 64 | struct OVPN_RX_BUFFER 65 | { 66 | // points to the beginning of data 67 | PUCHAR Data; 68 | 69 | // points to the end of data 70 | PUCHAR Tail; 71 | 72 | // data length 73 | SIZE_T Len; 74 | 75 | LIST_ENTRY PoolListEntry; 76 | 77 | LIST_ENTRY QueueListEntry; 78 | 79 | OVPN_RX_BUFFER_POOL Pool; 80 | 81 | #pragma warning(suppress:4200) //nonstandard extension used: zero-sized array in struct/union 82 | UCHAR Head[]; 83 | }; 84 | 85 | template 86 | UCHAR* 87 | OvpnBufferPut(_In_ BUF* buf, SIZE_T len) 88 | { 89 | UCHAR* tmp = buf->Tail; 90 | buf->Tail += len; 91 | buf->Len += len; 92 | 93 | return tmp; 94 | } 95 | 96 | template 97 | VOID 98 | OvpnBufferTrim(_In_ BUF* buf, SIZE_T len) 99 | { 100 | buf->Len = len; 101 | buf->Tail = buf->Data + len; 102 | } 103 | 104 | template 105 | VOID 106 | OvpnBufferPull(_In_ BUF* buf, SIZE_T len) 107 | { 108 | buf->Len -= len; 109 | buf->Data += len; 110 | } 111 | 112 | UCHAR* 113 | OvpnTxBufferPush(_In_ OVPN_TX_BUFFER* work, SIZE_T len); 114 | 115 | _Must_inspect_result_ 116 | NTSTATUS 117 | OvpnTxBufferPoolCreate(OVPN_TX_BUFFER_POOL* handle, VOID* ctx); 118 | 119 | VOID* 120 | OvpnTxBufferPoolGetContext(OVPN_TX_BUFFER_POOL handle); 121 | 122 | _Must_inspect_result_ 123 | NTSTATUS 124 | OvpnTxBufferPoolGet(_In_ OVPN_TX_BUFFER_POOL handle, _Outptr_ OVPN_TX_BUFFER** buffer); 125 | 126 | VOID 127 | OvpnTxBufferPoolPut(_In_ OVPN_TX_BUFFER* buffer); 128 | 129 | _Must_inspect_result_ 130 | NTSTATUS 131 | OvpnRxBufferPoolGet(_In_ OVPN_RX_BUFFER_POOL handle, _Outptr_ OVPN_RX_BUFFER** buffer); 132 | 133 | VOID 134 | OvpnRxBufferPoolPut(_In_ OVPN_RX_BUFFER* buffer); 135 | 136 | _Must_inspect_result_ 137 | NTSTATUS 138 | OvpnRxBufferPoolCreate(OVPN_RX_BUFFER_POOL* handle); 139 | 140 | NTSTATUS 141 | OvpnBufferQueueCreate(OVPN_BUFFER_QUEUE* handle); 142 | 143 | VOID 144 | OvpnBufferQueueEnqueue(OVPN_BUFFER_QUEUE handle, PLIST_ENTRY listEntry); 145 | 146 | VOID 147 | OvpnBufferQueueEnqueueHead(OVPN_BUFFER_QUEUE handle, PLIST_ENTRY listEntry); 148 | 149 | LIST_ENTRY* 150 | OvpnBufferQueueDequeue(OVPN_BUFFER_QUEUE handle); 151 | 152 | VOID 153 | OvpnRxBufferPoolDelete(OVPN_BUFFER_POOL handle); 154 | 155 | VOID 156 | OvpnTxBufferPoolDelete(OVPN_BUFFER_POOL handle); 157 | 158 | VOID 159 | OvpnBufferQueueDelete(OVPN_BUFFER_QUEUE handle); -------------------------------------------------------------------------------- /build_all.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal 3 | 4 | if "%CODEQL_HOME%"=="" set CODEQL_HOME=c:\codeql-home 5 | set CODEQL_BIN=%CODEQL_HOME%\codeql\codeql.cmd 6 | 7 | if "%EWDK11_DIR%"=="" set EWDK11_DIR=c:\ewdk11 8 | 9 | call "%EWDK11_DIR%\BuildEnv\SetupBuildEnv.cmd" 10 | 11 | set SOLUTION_FILE=ovpn-dco-win.sln 12 | set DRIVER_PROJECT_FILE=ovpn-dco-win.vcxproj 13 | 14 | for %%C in ( Release Debug ) do ( 15 | for %%P in ( x64 x86 arm64 ) do ( 16 | echo Building %SOLUTION_FILE%, configuration %%C, platform %%P 17 | call :runbuild %SOLUTION_FILE% %%C %%P 18 | ) 19 | ) 20 | 21 | if not "%BUILD_DISABLE_SDV%"=="" ( 22 | echo Skipping SDV build because BUILD_DISABLE_SDV is set 23 | goto :end 24 | ) 25 | 26 | for %%P in ( x64 x86 arm64 ) do ( 27 | call :runsdv %DRIVER_PROJECT_FILE% "Release" %%P 28 | call :runql %DRIVER_PROJECT_FILE% "Release" %%P 29 | call :runca %DRIVER_PROJECT_FILE% "Release" %%P 30 | call :rundvl %DRIVER_PROJECT_FILE% "Release" %%P 31 | ) 32 | 33 | :end: 34 | 35 | if "%BUILD_FAILED%"=="1" exit /B 1 36 | exit /B 0 37 | 38 | :runbuild: 39 | :: %1 - build file (as "ovpn-dco-win.sln") 40 | :: %2 - configuration (as "Release") 41 | :: %3 - platform (as x64) 42 | msbuild.exe "%~1" /p:Configuration="%~2" /P:Platform=%3 43 | goto :eof 44 | 45 | :runsdv 46 | echo Running SDV for %DRIVER_PROJECT_FILE%, configuration "%~2", platform %3 47 | msbuild.exe "%~1" /t:clean /p:Configuration="%~2" /P:Platform=%3 48 | 49 | IF ERRORLEVEL 1 ( 50 | set BUILD_FAILED=1 51 | ) 52 | 53 | msbuild.exe "%~1" /t:sdv /p:inputs="/clean" /p:Configuration="%~2" /P:Platform=%3 54 | 55 | IF ERRORLEVEL 1 ( 56 | set BUILD_FAILED=1 57 | ) 58 | 59 | msbuild.exe "%~1" /t:sdv /p:inputs="/check /devenv" /p:Configuration="%~2" /P:Platform=%3 60 | 61 | IF ERRORLEVEL 1 ( 62 | set BUILD_FAILED=1 63 | ) 64 | 65 | goto :eof 66 | 67 | :runql 68 | 69 | echo Running CodeQL for %DRIVER_PROJECT_FILE%, configuration "%~2", platform %3 70 | echo "Removing previously created rules database" 71 | rmdir /s/q codeql_db 72 | 73 | echo call "%EWDK11_DIR%\BuildEnv\SetupBuildEnv.cmd" > %~dp1\codeql.build.bat 74 | echo msbuild.exe "%~dp1\%~1" /t:rebuild /p:Configuration="%~2" /P:Platform=%3 >> %~dp1\codeql.build.bat 75 | 76 | call %CODEQL_BIN% database create -l=cpp -s=%~dp1 -c "%~dp1\codeql.build.bat" %~dp1\codeql_db -j 0 77 | 78 | IF ERRORLEVEL 1 ( 79 | set CODEQL_FAILED=1 80 | set BUILD_FAILED=1 81 | ) 82 | 83 | IF "%CODEQL_FAILED%" NEQ "1" ( 84 | call %CODEQL_BIN% database analyze %~dp1\codeql_db windows_driver_recommended.qls --format=sarifv2.1.0 --output=%~dp1\%DRIVER_PROJECT_FILE%.sarif -j 0 85 | ) 86 | 87 | IF ERRORLEVEL 1 ( 88 | set BUILD_FAILED=1 89 | ) 90 | 91 | goto :eof 92 | 93 | :runca 94 | echo Running Code Analysis for %DRIVER_PROJECT_FILE%, configuration "%~2", platform %3 95 | msbuild.exe "%~1" /p:Configuration="%~2" /P:Platform=%3 /P:RunCodeAnalysisOnce=True 96 | 97 | IF ERRORLEVEL 1 ( 98 | set BUILD_FAILED=1 99 | ) 100 | 101 | goto :eof 102 | 103 | :rundvl 104 | echo Creating Driver Verification Log for %DRIVER_PROJECT_FILE%, configuration "%~2", platform %3 105 | msbuild.exe "%~1" /t:dvl /p:Configuration="%~2" /P:Platform=%3 106 | 107 | IF ERRORLEVEL 1 ( 108 | set BUILD_FAILED=1 109 | ) 110 | 111 | goto :eof -------------------------------------------------------------------------------- /build_all_nosdv.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal 3 | SET BUILD_DISABLE_SDV=Yes 4 | call build_all.bat 5 | -------------------------------------------------------------------------------- /clean.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal 3 | 4 | call :rmdir .\Win32 5 | call :rmdir .\x64 6 | call :rmdir .\ARM64 7 | call :rmdir .\Debug 8 | call :rmdir .\Release 9 | 10 | call :rmdir .\ovpn-cli\x64 11 | call :rmdir .\ovpn-cli\ARM64 12 | call :rmdir .\ovpn-cli\Debug 13 | call :rmdir .\ovpn-cli\Release 14 | 15 | call :rmdir .\sdv 16 | call :rmdir .\sdv.temp 17 | call :rmdir .\codeql_db 18 | call :rmfiles *.dvl.xml *.sarif codeql.build.bat 19 | call :rmfiles *.log 20 | goto :eof 21 | 22 | :rmdir 23 | if exist "%~1" rmdir "%~1" /s /q 24 | goto :eof 25 | 26 | :rmfiles 27 | if "%~1"=="" goto :eof 28 | if exist "%~1" del /f "%~1" 29 | shift 30 | goto rmfiles 31 | -------------------------------------------------------------------------------- /control.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * ovpn-dco-win OpenVPN protocol accelerator for Windows 3 | * 4 | * Copyright (C) 2024- OpenVPN Inc 5 | * 6 | * Author: Lev Stipakov 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License version 2 10 | * as published by the Free Software Foundation. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along 18 | * with this program; if not, write to the Free Software Foundation, Inc., 19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | */ 21 | 22 | #include "control.h" 23 | #include "Driver.h" 24 | #include "uapi\ovpn-dco.h" 25 | #include "trace.h" 26 | 27 | _Use_decl_annotations_ 28 | NTSTATUS 29 | OvpnGetVersion(WDFREQUEST request, ULONG_PTR* bytesReturned) 30 | { 31 | LOG_ENTER(); 32 | 33 | *bytesReturned = 0; 34 | 35 | NTSTATUS status; 36 | POVPN_VERSION version = NULL; 37 | GOTO_IF_NOT_NT_SUCCESS(done, status, WdfRequestRetrieveOutputBuffer(request, sizeof(OVPN_VERSION), (PVOID*)&version, NULL)); 38 | 39 | version->Major = OVPN_DCO_VERSION_MAJOR; 40 | version->Minor = OVPN_DCO_VERSION_MINOR; 41 | version->Patch = OVPN_DCO_VERSION_PATCH; 42 | 43 | LOG_INFO("Version", TraceLoggingValue(version->Major, "Major"), TraceLoggingValue(version->Minor, "Minor"), TraceLoggingValue(version->Patch, "Patch")); 44 | 45 | *bytesReturned = sizeof(OVPN_VERSION); 46 | 47 | done: 48 | LOG_EXIT(); 49 | 50 | return status; 51 | } 52 | 53 | EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL OvpnEvtControlDeviceIOControl; 54 | 55 | VOID 56 | OvpnEvtControlDeviceIOControl(WDFQUEUE queue, WDFREQUEST request, size_t outputBufferLength, size_t inputBufferLength, ULONG ioControlCode) 57 | { 58 | UNREFERENCED_PARAMETER(queue); 59 | UNREFERENCED_PARAMETER(inputBufferLength); 60 | UNREFERENCED_PARAMETER(outputBufferLength); 61 | 62 | NTSTATUS status = STATUS_SUCCESS; 63 | ULONG_PTR bytesReturned = 0; 64 | 65 | switch (ioControlCode) 66 | { 67 | case OVPN_IOCTL_GET_VERSION: 68 | status = OvpnGetVersion(request, &bytesReturned); 69 | break; 70 | 71 | default: 72 | status = STATUS_INVALID_DEVICE_REQUEST; 73 | break; 74 | } 75 | 76 | WdfRequestCompleteWithInformation(request, status, bytesReturned); 77 | } 78 | 79 | NTSTATUS 80 | OvpnCreateControlDevice(WDFDRIVER wdfDriver) 81 | { 82 | LOG_ENTER(); 83 | 84 | DECLARE_CONST_UNICODE_STRING(symLink, L"\\DosDevices\\ovpn-dco-ver"); // this will be used by CreateFile 85 | DECLARE_CONST_UNICODE_STRING(deviceName, L"\\Device\\ovpn-dco-ver"); // this is required tp create symlink 86 | 87 | // allocate control device initialization structure 88 | PWDFDEVICE_INIT deviceInit = WdfControlDeviceInitAllocate(wdfDriver, &SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_R_RES_R); 89 | if (deviceInit == NULL) 90 | { 91 | return STATUS_INSUFFICIENT_RESOURCES; 92 | } 93 | 94 | // create the control device 95 | WDF_OBJECT_ATTRIBUTES deviceAttributes; 96 | WDF_OBJECT_ATTRIBUTES_INIT(&deviceAttributes); 97 | WDFDEVICE controlDevice; 98 | NTSTATUS status; 99 | 100 | GOTO_IF_NOT_NT_SUCCESS(done, status, WdfDeviceInitAssignName(deviceInit, &deviceName)); 101 | GOTO_IF_NOT_NT_SUCCESS(done, status, WdfDeviceCreate(&deviceInit, &deviceAttributes, &controlDevice)); 102 | 103 | POVPN_DRIVER driverCtx = OvpnGetDriverContext(WdfGetDriver()); 104 | driverCtx->ControlDevice = controlDevice; 105 | 106 | // symlink for control device 107 | GOTO_IF_NOT_NT_SUCCESS(done, status, WdfDeviceCreateSymbolicLink(controlDevice, &symLink)); 108 | 109 | // queue to handle IO 110 | WDF_IO_QUEUE_CONFIG queueConfig; 111 | WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queueConfig, WdfIoQueueDispatchParallel); 112 | queueConfig.EvtIoDeviceControl = OvpnEvtControlDeviceIOControl; 113 | WDFQUEUE queue; 114 | GOTO_IF_NOT_NT_SUCCESS(done, status, WdfIoQueueCreate(controlDevice, &queueConfig, WDF_NO_OBJECT_ATTRIBUTES, &queue)); 115 | 116 | // Complete the control device initialization 117 | WdfControlFinishInitializing(controlDevice); 118 | 119 | done: 120 | if (deviceInit) 121 | { 122 | WdfDeviceInitFree(deviceInit); 123 | } 124 | 125 | LOG_EXIT(); 126 | 127 | return status; 128 | } 129 | -------------------------------------------------------------------------------- /control.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ovpn-dco-win OpenVPN protocol accelerator for Windows 3 | * 4 | * Copyright (C) 2024- OpenVPN Inc 5 | * 6 | * Author: Lev Stipakov 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License version 2 10 | * as published by the Free Software Foundation. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along 18 | * with this program; if not, write to the Free Software Foundation, Inc., 19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | */ 21 | 22 | #pragma once 23 | 24 | #include 25 | #include 26 | 27 | NTSTATUS 28 | OvpnGetVersion(WDFREQUEST request, _Out_ ULONG_PTR* bytesReturned); 29 | 30 | NTSTATUS 31 | OvpnCreateControlDevice(WDFDRIVER wdfDriver); 32 | -------------------------------------------------------------------------------- /crypto.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * ovpn-dco-win OpenVPN protocol accelerator for Windows 3 | * 4 | * Copyright (C) 2020-2021 OpenVPN Inc 5 | * 6 | * Author: Lev Stipakov 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License version 2 10 | * as published by the Free Software Foundation. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along 18 | * with this program; if not, write to the Free Software Foundation, Inc., 19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | */ 21 | 22 | #include 23 | #include 24 | 25 | #include "crypto.h" 26 | #include "trace.h" 27 | #include "pktid.h" 28 | #include "socket.h" 29 | 30 | UINT 31 | OvpnCryptoOpCompose(UINT opcode, UINT keyId) 32 | { 33 | return (opcode << OVPN_OPCODE_SHIFT) | keyId; 34 | } 35 | 36 | static 37 | UINT 38 | OvpnProtoOp32Compose(UINT opcode, UINT keyId, UINT opPeerId) 39 | { 40 | UINT op8 = OvpnCryptoOpCompose(opcode, keyId); 41 | 42 | if (opcode == OVPN_OP_DATA_V2) 43 | return (op8 << 24) | (opPeerId & 0x00FFFFFF); 44 | 45 | return op8; 46 | } 47 | 48 | OVPN_CRYPTO_DECRYPT OvpnCryptoDecryptNone; 49 | 50 | _Use_decl_annotations_ 51 | NTSTATUS OvpnCryptoDecryptNone(OvpnCryptoKeySlot* keySlot, UCHAR* bufIn, SIZE_T len, UCHAR* bufOut, INT32 cryptoOptions) 52 | { 53 | UNREFERENCED_PARAMETER(keySlot); 54 | 55 | BOOLEAN pktId64bit = cryptoOptions & CRYPTO_OPTIONS_64BIT_PKTID; 56 | BOOLEAN cryptoOverhead = OVPN_DATA_V2_LEN + pktId64bit ? 8 : 4; 57 | 58 | if (len < cryptoOverhead) { 59 | LOG_WARN("Packet too short", TraceLoggingValue(len, "len")); 60 | return STATUS_DATA_ERROR; 61 | } 62 | 63 | RtlCopyMemory(bufOut, bufIn, len); 64 | 65 | return STATUS_SUCCESS; 66 | } 67 | 68 | OVPN_CRYPTO_ENCRYPT OvpnCryptoEncryptNone; 69 | 70 | _Use_decl_annotations_ 71 | NTSTATUS 72 | OvpnCryptoEncryptNone(OvpnCryptoKeySlot* keySlot, UCHAR* buf, SIZE_T len, INT32 cryptoOptions) 73 | { 74 | UNREFERENCED_PARAMETER(keySlot); 75 | UNREFERENCED_PARAMETER(len); 76 | UNREFERENCED_PARAMETER(cryptoOptions); 77 | 78 | // prepend with opcode, key-id and peer-id 79 | UINT32 op = OvpnProtoOp32Compose(OVPN_OP_DATA_V2, 0, 0); 80 | op = RtlUlongByteSwap(op); 81 | *(UINT32*)(buf) = op; 82 | 83 | // prepend with pktid 84 | static ULONG pktid; 85 | ULONG pktidNetwork = RtlUlongByteSwap(pktid++); 86 | *(UINT32*)(buf + OVPN_DATA_V2_LEN) = pktidNetwork; 87 | 88 | return STATUS_SUCCESS; 89 | } 90 | 91 | _Use_decl_annotations_ 92 | NTSTATUS 93 | OvpnCryptoInitAlgHandles(BCRYPT_ALG_HANDLE* aesAlgHandle, BCRYPT_ALG_HANDLE* chachaAlgHandle) 94 | { 95 | NTSTATUS status; 96 | GOTO_IF_NOT_NT_SUCCESS(done, status, BCryptOpenAlgorithmProvider(aesAlgHandle, BCRYPT_AES_ALGORITHM, NULL, BCRYPT_PROV_DISPATCH)); 97 | GOTO_IF_NOT_NT_SUCCESS(done, status, BCryptSetProperty(*aesAlgHandle, BCRYPT_CHAINING_MODE, (PUCHAR)BCRYPT_CHAIN_MODE_GCM, sizeof(BCRYPT_CHAIN_MODE_GCM), 0)); 98 | 99 | // available starting from Windows 11 100 | LOG_IF_NOT_NT_SUCCESS(BCryptOpenAlgorithmProvider(chachaAlgHandle, BCRYPT_CHACHA20_POLY1305_ALGORITHM, NULL, BCRYPT_PROV_DISPATCH)); 101 | done: 102 | return status; 103 | } 104 | 105 | _Use_decl_annotations_ 106 | VOID 107 | OvpnCryptoUninitAlgHandles(_In_ BCRYPT_ALG_HANDLE aesAlgHandle, BCRYPT_ALG_HANDLE chachaAlgHandle) 108 | { 109 | if (aesAlgHandle) { 110 | LOG_IF_NOT_NT_SUCCESS(BCryptCloseAlgorithmProvider(aesAlgHandle, 0)); 111 | } 112 | 113 | if (chachaAlgHandle) { 114 | LOG_IF_NOT_NT_SUCCESS(BCryptCloseAlgorithmProvider(chachaAlgHandle, 0)); 115 | } 116 | } 117 | 118 | #define GET_SYSTEM_ADDRESS_MDL(buf, mdl) { \ 119 | buf = (PUCHAR)MmGetSystemAddressForMdlSafe(mdl, LowPagePriority | MdlMappingNoExecute); \ 120 | if (buf == NULL) { \ 121 | LOG_ERROR("MmGetSystemAddressForMdlSafe() returned NULL"); \ 122 | return STATUS_DATA_ERROR; \ 123 | } \ 124 | } 125 | 126 | static 127 | NTSTATUS 128 | OvpnCryptoAEADDoWork(BOOLEAN encrypt, OvpnCryptoKeySlot* keySlot, UCHAR *bufIn, SIZE_T len, UCHAR* bufOut, INT32 cryptoOptions) 129 | { 130 | /* 131 | AEAD Nonce : 132 | 133 | [Packet ID] [HMAC keying material] 134 | [4/8 bytes] [8/4 bytes ] 135 | [AEAD nonce total : 12 bytes ] 136 | 137 | TLS wire protocol : 138 | 139 | Packet ID is 8 bytes long with CRYPTO_OPTIONS_64BIT_PKTID. 140 | 141 | [DATA_V2 opcode] [Packet ID] [AEAD Auth tag] [ciphertext] 142 | [4 bytes ] [4/8 bytes] [16 bytes ] 143 | [AEAD additional data(AD) ] 144 | 145 | With CRYPTO_OPTIONS_AEAD_TAG_END AEAD Auth tag is placed after ciphertext: 146 | 147 | [DATA_V2 opcode] [Packet ID] [ciphertext] [AEAD Auth tag] 148 | [4 bytes ] [4/8 bytes] [16 bytes ] 149 | [AEAD additional data(AD) ] 150 | */ 151 | 152 | NTSTATUS status = STATUS_SUCCESS; 153 | 154 | BOOLEAN pktId64bit = cryptoOptions & CRYPTO_OPTIONS_64BIT_PKTID; 155 | 156 | SIZE_T cryptoOverhead = OVPN_DATA_V2_LEN + AEAD_AUTH_TAG_LEN + (pktId64bit ? 8 : 4); 157 | 158 | if (len < cryptoOverhead) { 159 | LOG_WARN("Packet too short", TraceLoggingValue(len, "len")); 160 | return STATUS_DATA_ERROR; 161 | } 162 | 163 | UCHAR nonce[12]; 164 | if (encrypt) { 165 | // prepend with opcode, key-id and peer-id 166 | UINT32 op = OvpnProtoOp32Compose(OVPN_OP_DATA_V2, keySlot->KeyId, keySlot->PeerId); 167 | op = RtlUlongByteSwap(op); 168 | *reinterpret_cast(bufOut) = op; 169 | 170 | if (pktId64bit) 171 | { 172 | // calculate pktid 173 | UINT64 pktid; 174 | GOTO_IF_NOT_NT_SUCCESS(done, status, OvpnPktidXmitNext(&keySlot->PktidXmit, &pktid, true)); 175 | ULONG64 pktidNetwork = RtlUlonglongByteSwap(pktid); 176 | 177 | // calculate nonce, which is pktid + nonce_tail 178 | RtlCopyMemory(nonce, &pktidNetwork, 8); 179 | RtlCopyMemory(nonce + 8, keySlot->EncNonceTail, 4); 180 | 181 | // prepend with pktid 182 | *reinterpret_cast(bufOut + OVPN_DATA_V2_LEN) = pktidNetwork; 183 | } 184 | else 185 | { 186 | // calculate pktid 187 | UINT32 pktid; 188 | GOTO_IF_NOT_NT_SUCCESS(done, status, OvpnPktidXmitNext(&keySlot->PktidXmit, &pktid, false)); 189 | ULONG pktidNetwork = RtlUlongByteSwap(pktid); 190 | 191 | // calculate nonce, which is pktid + nonce_tail 192 | RtlCopyMemory(nonce, &pktidNetwork, 4); 193 | RtlCopyMemory(nonce + 4, keySlot->EncNonceTail, 8); 194 | 195 | // prepend with pktid 196 | *reinterpret_cast(bufOut + OVPN_DATA_V2_LEN) = pktidNetwork; 197 | } 198 | } 199 | else { 200 | ULONG64 pktId; 201 | 202 | RtlCopyMemory(nonce, bufIn + OVPN_DATA_V2_LEN, pktId64bit ? 8 : 4); 203 | RtlCopyMemory(nonce + (pktId64bit ? 8 : 4), &keySlot->DecNonceTail, pktId64bit ? 4 : 8); 204 | if (pktId64bit) 205 | { 206 | pktId = RtlUlonglongByteSwap(*reinterpret_cast(nonce)); 207 | } 208 | else 209 | { 210 | pktId = static_cast(RtlUlongByteSwap(*reinterpret_cast(nonce))); 211 | } 212 | 213 | status = OvpnPktidRecvVerify(&keySlot->PktidRecv, pktId); 214 | 215 | if (!NT_SUCCESS(status)) { 216 | LOG_ERROR("Invalid pktId", TraceLoggingUInt64(pktId, "pktId")); 217 | return STATUS_DATA_ERROR; 218 | } 219 | } 220 | 221 | // we prepended buf with crypto overhead 222 | len -= cryptoOverhead; 223 | 224 | BOOLEAN aeadTagEnd = cryptoOptions & CRYPTO_OPTIONS_AEAD_TAG_END; 225 | 226 | BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO authInfo; 227 | BCRYPT_INIT_AUTH_MODE_INFO(authInfo); 228 | authInfo.pbNonce = nonce; 229 | authInfo.cbNonce = sizeof(nonce); 230 | authInfo.pbTag = (encrypt ? bufOut : bufIn) + OVPN_DATA_V2_LEN + (pktId64bit ? 8 : 4) + (aeadTagEnd ? len : 0); 231 | authInfo.cbTag = AEAD_AUTH_TAG_LEN; 232 | authInfo.pbAuthData = (encrypt ? bufOut : bufIn); 233 | authInfo.cbAuthData = OVPN_DATA_V2_LEN + (pktId64bit ? 8 : 4); 234 | 235 | auto payloadOffset = OVPN_DATA_V2_LEN + (pktId64bit ? 8 : 4) + (aeadTagEnd ? 0 : AEAD_AUTH_TAG_LEN); 236 | bufOut += payloadOffset; 237 | bufIn += payloadOffset; 238 | 239 | // non-chaining mode 240 | ULONG bytesDone = 0; 241 | GOTO_IF_NOT_NT_SUCCESS(done, status, encrypt ? 242 | BCryptEncrypt(keySlot->EncKey, bufIn, (ULONG)len, &authInfo, NULL, 0, bufOut, (ULONG)len, &bytesDone, 0) : 243 | BCryptDecrypt(keySlot->DecKey, bufIn, (ULONG)len, &authInfo, NULL, 0, bufOut, (ULONG)len, &bytesDone, 0) 244 | ); 245 | 246 | done: 247 | return status; 248 | } 249 | 250 | OVPN_CRYPTO_DECRYPT OvpnCryptoDecryptAEAD; 251 | 252 | _Use_decl_annotations_ 253 | NTSTATUS 254 | OvpnCryptoDecryptAEAD(OvpnCryptoKeySlot* keySlot, UCHAR* bufIn, SIZE_T len, UCHAR* bufOut, INT32 cryptoOptions) 255 | { 256 | return OvpnCryptoAEADDoWork(FALSE, keySlot, bufIn, len, bufOut, cryptoOptions); 257 | } 258 | 259 | OVPN_CRYPTO_ENCRYPT OvpnCryptoEncryptAEAD; 260 | 261 | _Use_decl_annotations_ 262 | NTSTATUS 263 | OvpnCryptoEncryptAEAD(OvpnCryptoKeySlot* keySlot, UCHAR* buf, SIZE_T len, INT32 cryptoOptions) 264 | { 265 | return OvpnCryptoAEADDoWork(TRUE, keySlot, buf, len, buf, cryptoOptions); 266 | } 267 | 268 | _Use_decl_annotations_ 269 | NTSTATUS 270 | OvpnCryptoNewKey(OvpnCryptoContext* cryptoContext, POVPN_CRYPTO_DATA_V2 cryptoDataV2, BCRYPT_ALG_HANDLE algHandle) 271 | { 272 | OvpnCryptoKeySlot* keySlot = NULL; 273 | NTSTATUS status = STATUS_SUCCESS; 274 | 275 | POVPN_CRYPTO_DATA cryptoData = &cryptoDataV2->V1; 276 | 277 | if (cryptoData->KeySlot == OVPN_KEY_SLOT::OVPN_KEY_SLOT_PRIMARY) { 278 | keySlot = &cryptoContext->Primary; 279 | } 280 | else if (cryptoData->KeySlot == OVPN_KEY_SLOT::OVPN_KEY_SLOT_SECONDARY) { 281 | keySlot = &cryptoContext->Secondary; 282 | } 283 | else { 284 | LOG_ERROR("Invalid key slot", TraceLoggingValue((int)cryptoData->KeySlot, "keySlot")); 285 | return STATUS_INVALID_DEVICE_REQUEST; 286 | } 287 | 288 | if (cryptoDataV2->CryptoOptions & CRYPTO_OPTIONS_64BIT_PKTID) 289 | { 290 | cryptoContext->CryptoOptions |= CRYPTO_OPTIONS_64BIT_PKTID; 291 | } 292 | if (cryptoDataV2->CryptoOptions & CRYPTO_OPTIONS_AEAD_TAG_END) 293 | { 294 | cryptoContext->CryptoOptions |= CRYPTO_OPTIONS_AEAD_TAG_END; 295 | } 296 | 297 | if ((cryptoData->CipherAlg == OVPN_CIPHER_ALG_AES_GCM) || (cryptoData->CipherAlg == OVPN_CIPHER_ALG_CHACHA20_POLY1305)) { 298 | // destroy previous keys 299 | if (keySlot->EncKey) { 300 | BCryptDestroyKey(keySlot->EncKey); 301 | keySlot->EncKey = NULL; 302 | } 303 | 304 | if (keySlot->DecKey) { 305 | BCryptDestroyKey(keySlot->DecKey); 306 | keySlot->DecKey = NULL; 307 | } 308 | 309 | if ((cryptoData->Encrypt.KeyLen > 32) || (cryptoData->Decrypt.KeyLen > 32)) 310 | { 311 | status = STATUS_INVALID_DEVICE_REQUEST; 312 | LOG_ERROR("Incorrect encrypt or decrypt key length", TraceLoggingValue(cryptoData->Encrypt.KeyLen, "Encrypt.KeyLen"), 313 | TraceLoggingValue(cryptoData->Decrypt.KeyLen, "Decrypt.KeyLen")); 314 | goto done; 315 | } 316 | 317 | // generate keys from key materials 318 | GOTO_IF_NOT_NT_SUCCESS(done, status, BCryptGenerateSymmetricKey(algHandle, &keySlot->EncKey, NULL, 0, cryptoData->Encrypt.Key, cryptoData->Encrypt.KeyLen, 0)); 319 | GOTO_IF_NOT_NT_SUCCESS(done, status, BCryptGenerateSymmetricKey(algHandle, &keySlot->DecKey, NULL, 0, cryptoData->Decrypt.Key, cryptoData->Decrypt.KeyLen, 0)); 320 | 321 | // copy nonce tails 322 | RtlCopyMemory(keySlot->EncNonceTail, cryptoData->Encrypt.NonceTail, sizeof(cryptoData->Encrypt.NonceTail)); 323 | RtlCopyMemory(keySlot->DecNonceTail, cryptoData->Decrypt.NonceTail, sizeof(cryptoData->Decrypt.NonceTail)); 324 | 325 | cryptoContext->Encrypt = OvpnCryptoEncryptAEAD; 326 | cryptoContext->Decrypt = OvpnCryptoDecryptAEAD; 327 | 328 | keySlot->KeyId = cryptoData->KeyId; 329 | keySlot->PeerId = cryptoData->PeerId; 330 | 331 | LOG_INFO("New key", TraceLoggingValue(cryptoData->CipherAlg == OVPN_CIPHER_ALG_AES_GCM ? "aes-gcm" : "chacha20-poly1305", "alg"), 332 | TraceLoggingValue(cryptoData->KeyId, "KeyId"), TraceLoggingValue(cryptoData->PeerId, "PeerId")); 333 | } 334 | else if (cryptoData->CipherAlg == OVPN_CIPHER_ALG_NONE) { 335 | cryptoContext->Encrypt = OvpnCryptoEncryptNone; 336 | cryptoContext->Decrypt = OvpnCryptoDecryptNone; 337 | 338 | LOG_INFO("Using cipher none"); 339 | } 340 | else { 341 | status = STATUS_INVALID_DEVICE_REQUEST; 342 | LOG_ERROR("Unknown OVPN_CIPHER_ALG", TraceLoggingValue((int)cryptoData->CipherAlg, "CipherAlg")); 343 | goto done; 344 | } 345 | 346 | // reset pktid for a new key 347 | RtlZeroMemory(&keySlot->PktidXmit, sizeof(keySlot->PktidXmit)); 348 | RtlZeroMemory(&keySlot->PktidRecv, sizeof(keySlot->PktidRecv)); 349 | 350 | done: 351 | return status; 352 | } 353 | 354 | _Use_decl_annotations_ 355 | OvpnCryptoKeySlot* 356 | OvpnCryptoKeySlotFromKeyId(OvpnCryptoContext* cryptoContext, unsigned int keyId) 357 | { 358 | if (cryptoContext->Primary.KeyId == keyId) 359 | return &cryptoContext->Primary; 360 | else if (cryptoContext->Secondary.KeyId == keyId) { 361 | return &cryptoContext->Secondary; 362 | } 363 | 364 | LOG_ERROR("No KeySlot for KeyId", TraceLoggingValue(keyId, "KeyId")); 365 | 366 | return NULL; 367 | } 368 | 369 | _Use_decl_annotations_ 370 | VOID 371 | OvpnCryptoSwapKeys(OvpnCryptoContext* cryptoContext) 372 | { 373 | OvpnCryptoKeySlot keySlot; 374 | 375 | RtlCopyMemory(&keySlot, &cryptoContext->Primary, sizeof(keySlot)); 376 | RtlCopyMemory(&cryptoContext->Primary, &cryptoContext->Secondary, sizeof(keySlot)); 377 | RtlCopyMemory(&cryptoContext->Secondary, &keySlot, sizeof(keySlot)); 378 | 379 | LOG_INFO("Key swapped", TraceLoggingValue(cryptoContext->Primary.KeyId, "key1"), TraceLoggingValue(cryptoContext->Secondary.KeyId, "key2")); 380 | } 381 | 382 | _Use_decl_annotations_ 383 | VOID 384 | OvpnCryptoUninit(OvpnCryptoContext* cryptoContext) 385 | { 386 | if (cryptoContext->Primary.EncKey) { 387 | BCryptDestroyKey(cryptoContext->Primary.EncKey); 388 | } 389 | 390 | if (cryptoContext->Primary.DecKey) { 391 | BCryptDestroyKey(cryptoContext->Primary.DecKey); 392 | } 393 | 394 | if (cryptoContext->Secondary.EncKey) { 395 | BCryptDestroyKey(cryptoContext->Secondary.EncKey); 396 | } 397 | 398 | if (cryptoContext->Secondary.DecKey) { 399 | BCryptDestroyKey(cryptoContext->Secondary.DecKey); 400 | } 401 | 402 | RtlZeroMemory(cryptoContext, sizeof(OvpnCryptoContext)); 403 | } 404 | -------------------------------------------------------------------------------- /crypto.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ovpn-dco-win OpenVPN protocol accelerator for Windows 3 | * 4 | * Copyright (C) 2020-2021 OpenVPN Inc 5 | * Copyright (C) 2023 Rubicon Communications LLC (Netgate) 6 | * 7 | * Author: Lev Stipakov 8 | * 9 | * This program is free software; you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License version 2 11 | * as published by the Free Software Foundation. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License along 19 | * with this program; if not, write to the Free Software Foundation, Inc., 20 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | */ 22 | 23 | #pragma once 24 | 25 | #include 26 | #include 27 | 28 | #include "pktid.h" 29 | #include "uapi\ovpn-dco.h" 30 | #include "socket.h" 31 | 32 | #define OVPN_DATA_V2_LEN 4 33 | #define AEAD_AUTH_TAG_LEN 16 34 | 35 | // packet opcode (high 5 bits) and key-id (low 3 bits) are combined in one byte 36 | #define OVPN_OP_DATA_V2 9 37 | #define OVPN_KEY_ID_MASK 0x07 38 | #define OVPN_OPCODE_SHIFT 3 39 | #define OVPN_PEER_ID_MASK 0x00FFFFFF 40 | 41 | struct OvpnCryptoKeySlot 42 | { 43 | BCRYPT_KEY_HANDLE EncKey; 44 | BCRYPT_KEY_HANDLE DecKey; 45 | 46 | UCHAR EncNonceTail[8]; 47 | UCHAR DecNonceTail[8]; 48 | 49 | UCHAR KeyId; 50 | INT32 PeerId; 51 | 52 | OvpnPktidXmit PktidXmit; 53 | OvpnPktidRecv PktidRecv; 54 | }; 55 | 56 | _Function_class_(OVPN_CRYPTO_ENCRYPT) 57 | _IRQL_requires_max_(DISPATCH_LEVEL) 58 | _Must_inspect_result_ 59 | typedef 60 | NTSTATUS 61 | OVPN_CRYPTO_ENCRYPT(_In_ OvpnCryptoKeySlot* keySlot, _In_ UCHAR* buf, _In_ SIZE_T len, _In_ INT32 CryptoOptions); 62 | typedef OVPN_CRYPTO_ENCRYPT* POVPN_CRYPTO_ENCRYPT; 63 | 64 | _Function_class_(OVPN_CRYPTO_DECRYPT) 65 | _IRQL_requires_max_(DISPATCH_LEVEL) 66 | _Must_inspect_result_ 67 | typedef 68 | NTSTATUS 69 | OVPN_CRYPTO_DECRYPT(_In_ OvpnCryptoKeySlot* keySlot, _In_ UCHAR* bufIn, _In_ SIZE_T len, _In_ UCHAR* bufOut, _In_ INT32 CryptoOptions); 70 | typedef OVPN_CRYPTO_DECRYPT* POVPN_CRYPTO_DECRYPT; 71 | 72 | struct OvpnCryptoContext 73 | { 74 | OvpnCryptoKeySlot Primary; 75 | OvpnCryptoKeySlot Secondary; 76 | 77 | POVPN_CRYPTO_ENCRYPT Encrypt; 78 | POVPN_CRYPTO_DECRYPT Decrypt; 79 | 80 | INT32 CryptoOptions; 81 | }; 82 | 83 | _Must_inspect_result_ 84 | _IRQL_requires_(PASSIVE_LEVEL) 85 | NTSTATUS 86 | OvpnCryptoInitAlgHandles(_Outptr_ BCRYPT_ALG_HANDLE* aesAlgHandle, _Outptr_ BCRYPT_ALG_HANDLE* chachaAlgHandle); 87 | 88 | _IRQL_requires_(PASSIVE_LEVEL) 89 | VOID 90 | OvpnCryptoUninitAlgHandles(_In_ BCRYPT_ALG_HANDLE aesAlgHandle, BCRYPT_ALG_HANDLE chachaAlgHandle); 91 | 92 | VOID 93 | OvpnCryptoUninit(_In_ OvpnCryptoContext* cryptoContext); 94 | 95 | _Must_inspect_result_ 96 | NTSTATUS 97 | OvpnCryptoNewKey(_In_ OvpnCryptoContext* cryptoContext, _In_ POVPN_CRYPTO_DATA_V2 cryptoData, _In_opt_ BCRYPT_ALG_HANDLE algHandle); 98 | 99 | _Must_inspect_result_ 100 | OvpnCryptoKeySlot* 101 | OvpnCryptoKeySlotFromKeyId(_In_ OvpnCryptoContext* cryptoContext, unsigned int keyId); 102 | 103 | VOID 104 | OvpnCryptoSwapKeys(_In_ OvpnCryptoContext* cryptoContext); 105 | 106 | static inline 107 | UCHAR 108 | OvpnCryptoKeyIdExtract(UCHAR op) 109 | { 110 | return op & OVPN_KEY_ID_MASK; 111 | } 112 | 113 | static inline 114 | UCHAR OvpnCryptoOpcodeExtract(UCHAR op) 115 | { 116 | return op >> OVPN_OPCODE_SHIFT; 117 | } 118 | -------------------------------------------------------------------------------- /gui/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | project(gui) 4 | 5 | set(CMAKE_CXX_STANDARD 17) # Enable C++17 6 | set(CMAKE_CXX_STANDARD_REQUIRED YES) # Ensure the compiler strictly requires C++17 7 | 8 | add_executable (gui WIN32 "gui.cpp") 9 | 10 | add_definitions(-DUNICODE -D_UNICODE) 11 | target_link_libraries(gui PRIVATE user32 gdi32 Ws2_32 Crypt32) 12 | -------------------------------------------------------------------------------- /gui/data64.key: -------------------------------------------------------------------------------- 1 | jRqMACN7d7/aFQNT8S7jkrBD8uwrgHbG5OQZP2eu4R1Y7tfpS2bf5RHv06Vi163CGoaIiTX99R3B 2 | ia9ycAH8Wz1+9PWv51dnBLur9jbShlgZ2QHLtUc4a/gfT7zZwULXuuxdLnvR21DDeMBaTbkgbai9 3 | uvAa7ne1liIgGFzbv+Bas4HDVrygxIxuAnP5Qgc3648IJkZ0QEXPF+O9f0n5+QIvGCxkAUVx+5K6 4 | KIs+SoeWXnAopELmoGSjUpFtJbagXK82HfdqpuUxT2Tnuef0/14SzVE/vNleBNu2ZbyrSAaah8tE 5 | BofkPJUBFY+YQcfZNM5Dgrw3i+Bpmpq/gpdg5w== 6 | -------------------------------------------------------------------------------- /makecabs.bat: -------------------------------------------------------------------------------- 1 | rem This is supposed to be run under EWDK dev prompt 2 | 3 | msbuild /p:Configuration=Release /p:Platform="x86" 4 | msbuild /p:Configuration=Release-Win11 /p:Platform="x86" 5 | 6 | msbuild /p:Configuration=Release /p:Platform="x64" 7 | msbuild /p:Configuration=Release-Win11 /p:Platform="x64" 8 | 9 | msbuild /p:Configuration=Release /p:Platform="arm64" 10 | msbuild /p:Configuration=Release-Win11 /p:Platform="arm64" 11 | 12 | copy ovpn-dco-win.ddf x86\Release&& cd x86\Release&& makecab -f ovpn-dco-win.ddf&& copy disk1\ovpn-dco-win.cab ..\..\ovpn-dco-win-x86-win10.cab&& cd ..\.. 13 | copy ovpn-dco-win.ddf x86\Release-Win11&& cd x86\Release-Win11&& makecab -f ovpn-dco-win.ddf&& copy disk1\ovpn-dco-win.cab ..\..\ovpn-dco-win-x86-win11.cab&& cd ..\.. 14 | 15 | copy ovpn-dco-win.ddf x64\Release&& cd x64\Release&& makecab -f ovpn-dco-win.ddf&& copy disk1\ovpn-dco-win.cab ..\..\ovpn-dco-win-x64-win10.cab&& cd ..\.. 16 | copy ovpn-dco-win.ddf x64\Release-Win11&& cd x64\Release-Win11&& makecab -f ovpn-dco-win.ddf&& copy disk1\ovpn-dco-win.cab ..\..\ovpn-dco-win-x64-win11.cab&& cd ..\.. 17 | 18 | copy ovpn-dco-win.ddf ARM64\Release&& cd ARM64\Release&& makecab -f ovpn-dco-win.ddf&& copy disk1\ovpn-dco-win.cab ..\..\ovpn-dco-win-arm64-win10.cab&& cd ..\.. 19 | copy ovpn-dco-win.ddf ARM64\Release-Win11&& cd ARM64\Release-Win11&& makecab -f ovpn-dco-win.ddf&& copy disk1\ovpn-dco-win.cab ..\..\ovpn-dco-win-arm64-win11.cab&& cd ..\.. 20 | -------------------------------------------------------------------------------- /msm/.gitignore: -------------------------------------------------------------------------------- 1 | *.msi 2 | *.wixpdb 3 | *.wixobj 4 | *.msm 5 | dist\ 6 | tmp\ 7 | -------------------------------------------------------------------------------- /msm/build-release-msm.ps1: -------------------------------------------------------------------------------- 1 | param ( 2 | [string]$TAG = "0.8.3" 3 | ) 4 | 5 | Remove-Item -Path tmp -Recurse -ErrorAction SilentlyContinue 6 | Remove-Item -Path dist -Recurse -ErrorAction SilentlyContinue 7 | 8 | New-Item -Path tmp -type directory -Force 9 | 10 | foreach ($arch in $("amd64", "arm64", "x86")) { 11 | Invoke-WebRequest -Uri "https://github.com/OpenVPN/ovpn-dco-win/releases/download/$TAG/ovpn-dco-win-$TAG-$arch.zip" -OutFile tmp\$arch.zip 12 | Expand-Archive -Path tmp\$arch.zip -DestinationPath dist\$arch -Force 13 | } 14 | 15 | New-Item -Path dist -type directory -Force 16 | 17 | Rename-Item $PWD\dist\amd64 $PWD\dist\x64 18 | 19 | .\build.ps1 20 | -------------------------------------------------------------------------------- /msm/build.ps1: -------------------------------------------------------------------------------- 1 | param ( 2 | [ValidateSet("x86", "x64", "arm64", "All")] 3 | [string]$Arch = "All", 4 | 5 | [string]$Wix = "C:\Program Files (x86)\WiX Toolset v3.14\" 6 | ) 7 | 8 | $ProgramFilesDirs = @{ 9 | "x86"="ProgramFilesFolder" 10 | "x64"="ProgramFiles64Folder" 11 | "arm64"="ProgramFiles64Folder" 12 | } 13 | 14 | function Build-Arch($BuildArch) { 15 | & "$WIX\bin\candle.exe" ` 16 | -dPROGRAM_FILES_DIR="$($ProgramFilesDirs[$BuildArch])" ` 17 | -dPRODUCT_NAME=OpenVPN ovpn-dco.wxs ` 18 | -arch "$BuildArch" 19 | 20 | $plat = $BuildArch 21 | if ($BuildArch -eq "x64") { 22 | $plat = "amd64"; 23 | } 24 | 25 | & "$WIX\bin\light.exe" ` 26 | -sval ovpn-dco.wixobj ` 27 | -b arch="$BuildArch" ` 28 | -b ovpndco="dist\$BuildArch" ` 29 | -out ovpn-dco-"$plat".msm 30 | 31 | # build sample installer 32 | & "$WIX\bin\candle.exe" ` 33 | -dPROGRAM_FILES_DIR="$($ProgramFilesDirs[$BuildArch])" ` 34 | -dPRODUCT_NAME=OpenVPN sampleinstaller.wxs ` 35 | -dPRODUCT_PLATFORM="$plat" ` 36 | -arch "$BuildArch" 37 | 38 | & "$WIX\bin\light.exe" ` 39 | -sval sampleinstaller.wixobj ` 40 | -b arch="$BuildArch" ` 41 | -b ovpndco="dist\$BuildArch" ` 42 | -out sampleinstaller-"$plat".msi 43 | } 44 | 45 | if ($Arch -eq "All") { 46 | Build-Arch x86 47 | Build-Arch x64 48 | Build-Arch arm64 49 | } else { 50 | Build-Arch $Arch 51 | } 52 | -------------------------------------------------------------------------------- /msm/dllmain.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ovpn-dco-win OpenVPN protocol accelerator for Windows 3 | * 4 | * Copyright (C) 2023 OpenVPN Inc 5 | * Copyright (C) 2018-2023 Simon Rozman 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License version 2 9 | * as published by the Free Software Foundation. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | 21 | #include 22 | 23 | #include "log.h" 24 | 25 | DWORD ovpndco_thread_data_idx = TLS_OUT_OF_INDEXES; 26 | 27 | /** 28 | * DLL entry point 29 | */ 30 | BOOL WINAPI 31 | DllMain( 32 | _In_ HINSTANCE hinstDLL, 33 | _In_ DWORD dwReason, 34 | _In_ LPVOID lpReserved) 35 | { 36 | UNREFERENCED_PARAMETER(hinstDLL); 37 | UNREFERENCED_PARAMETER(lpReserved); 38 | 39 | switch (dwReason) 40 | { 41 | case DLL_PROCESS_ATTACH: 42 | /* Allocate thread local storage index. */ 43 | ovpndco_thread_data_idx = TlsAlloc(); 44 | if (ovpndco_thread_data_idx == TLS_OUT_OF_INDEXES) 45 | { 46 | return FALSE; 47 | } 48 | /* Fall through. */ 49 | 50 | case DLL_THREAD_ATTACH: 51 | { 52 | /* Create thread local storage data. */ 53 | struct ovpndco_thread_data* s = (struct ovpndco_thread_data*)calloc(1, sizeof(struct ovpndco_thread_data)); 54 | if (s == NULL) 55 | { 56 | return FALSE; 57 | } 58 | 59 | TlsSetValue(ovpndco_thread_data_idx, s); 60 | break; 61 | } 62 | 63 | case DLL_PROCESS_DETACH: 64 | if (ovpndco_thread_data_idx != TLS_OUT_OF_INDEXES) 65 | { 66 | /* Free thread local storage data and index. */ 67 | free(TlsGetValue(ovpndco_thread_data_idx)); 68 | TlsFree(ovpndco_thread_data_idx); 69 | } 70 | break; 71 | 72 | case DLL_THREAD_DETACH: 73 | /* Free thread local storage data. */ 74 | free(TlsGetValue(ovpndco_thread_data_idx)); 75 | break; 76 | } 77 | 78 | return TRUE; 79 | } 80 | 81 | -------------------------------------------------------------------------------- /msm/exports.def: -------------------------------------------------------------------------------- 1 | EXPORTS 2 | MsiEvaluate 3 | MsiProcess 4 | -------------------------------------------------------------------------------- /msm/installer.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | Debug 22 | arm64 23 | 24 | 25 | Release 26 | arm64 27 | 28 | 29 | 30 | 16.0 31 | Win32Proj 32 | {fda93664-ca76-44ef-89aa-625fbe9d64f6} 33 | installer 34 | 10.0.20348.0 35 | 36 | 37 | 38 | DynamicLibrary 39 | true 40 | v142 41 | Unicode 42 | 43 | 44 | DynamicLibrary 45 | false 46 | v142 47 | true 48 | Unicode 49 | 50 | 51 | DynamicLibrary 52 | true 53 | Unicode 54 | v142 55 | 56 | 57 | DynamicLibrary 58 | false 59 | v142 60 | true 61 | Unicode 62 | 63 | 64 | DynamicLibrary 65 | true 66 | v142 67 | Unicode 68 | 69 | 70 | DynamicLibrary 71 | false 72 | v142 73 | true 74 | Unicode 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | installer 102 | $(ProjectDir)$(Platform)\$(Configuration)\ 103 | 104 | 105 | installer 106 | $(ProjectDir)$(Platform)\$(Configuration)\ 107 | 108 | 109 | installer 110 | $(ProjectDir)x86\$(Configuration)\ 111 | 112 | 113 | installer 114 | $(ProjectDir)x86\$(Configuration)\ 115 | 116 | 117 | installer 118 | $(ProjectDir)$(Platform)\$(Configuration)\ 119 | 120 | 121 | installer 122 | $(ProjectDir)$(Platform)\$(Configuration)\ 123 | 124 | 125 | 126 | Level3 127 | true 128 | WIN32;_DEBUG;INSTALLER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 129 | true 130 | NotUsing 131 | pch.h 132 | MultiThreadedDebug 133 | 134 | 135 | Windows 136 | true 137 | false 138 | exports.def 139 | Setupapi.lib;newdev.lib;msi.lib;Shlwapi.lib;%(AdditionalDependencies) 140 | 141 | 142 | 143 | 144 | Level3 145 | true 146 | true 147 | true 148 | WIN32;NDEBUG;INSTALLER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 149 | true 150 | NotUsing 151 | pch.h 152 | MultiThreaded 153 | 154 | 155 | Windows 156 | true 157 | true 158 | true 159 | false 160 | exports.def 161 | Setupapi.lib;newdev.lib;msi.lib;Shlwapi.lib;%(AdditionalDependencies) 162 | 163 | 164 | 165 | 166 | Level3 167 | true 168 | _DEBUG;INSTALLER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 169 | true 170 | NotUsing 171 | pch.h 172 | MultiThreadedDebug 173 | 174 | 175 | Windows 176 | true 177 | false 178 | exports.def 179 | Setupapi.lib;newdev.lib;msi.lib;Shlwapi.lib;%(AdditionalDependencies) 180 | 181 | 182 | 183 | 184 | Level3 185 | true 186 | true 187 | true 188 | NDEBUG;INSTALLER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 189 | true 190 | NotUsing 191 | pch.h 192 | MultiThreaded 193 | 194 | 195 | Windows 196 | true 197 | true 198 | true 199 | false 200 | exports.def 201 | Setupapi.lib;newdev.lib;msi.lib;Shlwapi.lib;%(AdditionalDependencies) 202 | 203 | 204 | 205 | 206 | Level3 207 | true 208 | _DEBUG;INSTALLER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 209 | true 210 | NotUsing 211 | pch.h 212 | MultiThreadedDebug 213 | 214 | 215 | Windows 216 | true 217 | false 218 | exports.def 219 | Setupapi.lib;newdev.lib;msi.lib;Shlwapi.lib;%(AdditionalDependencies) 220 | 221 | 222 | 223 | 224 | Level3 225 | true 226 | true 227 | true 228 | NDEBUG;INSTALLER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 229 | true 230 | NotUsing 231 | pch.h 232 | MultiThreaded 233 | 234 | 235 | Windows 236 | true 237 | true 238 | true 239 | false 240 | exports.def 241 | Setupapi.lib;newdev.lib;msi.lib;Shlwapi.lib;%(AdditionalDependencies) 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | -------------------------------------------------------------------------------- /msm/installer.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | 23 | 24 | Source Files 25 | 26 | 27 | Source Files 28 | 29 | 30 | Source Files 31 | 32 | 33 | 34 | 35 | Header Files 36 | 37 | 38 | -------------------------------------------------------------------------------- /msm/log.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ovpn-dco-win OpenVPN protocol accelerator for Windows 3 | * 4 | * Copyright (C) 2023 OpenVPN Inc 5 | * Copyright (C) 2018-2023 Simon Rozman 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License version 2 9 | * as published by the Free Software Foundation. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | 28 | #include "log.h" 29 | 30 | void 31 | x_msg(const unsigned int flags, const char* format, ...) 32 | { 33 | va_list arglist; 34 | va_start(arglist, format); 35 | x_msg_va(flags, format, arglist); 36 | va_end(arglist); 37 | } 38 | 39 | void 40 | x_msg_va(const unsigned int flags, const char* format, va_list arglist) 41 | { 42 | /* Secure last error before it is overridden. */ 43 | DWORD dwResult = (flags & M_ERRNO) != 0 ? GetLastError() : ERROR_SUCCESS; 44 | 45 | struct ovpndco_thread_data* s = (struct ovpndco_thread_data*)TlsGetValue(ovpndco_thread_data_idx); 46 | if (s->hInstall == 0) 47 | { 48 | /* No MSI session, no fun. */ 49 | return; 50 | } 51 | 52 | /* Prepare the message record. The record will contain up to four fields. */ 53 | MSIHANDLE hRecordProg = MsiCreateRecord(4); 54 | 55 | { 56 | /* Field 2: The message string. */ 57 | char szBufStack[128]; 58 | int iResultLen = vsnprintf(szBufStack, _countof(szBufStack), format, arglist); 59 | if (iResultLen < _countof(szBufStack)) 60 | { 61 | /* Use from stack. */ 62 | MsiRecordSetStringA(hRecordProg, 2, szBufStack); 63 | } 64 | else 65 | { 66 | /* Allocate on heap and retry. */ 67 | char* szMessage = (char*)malloc(++iResultLen * sizeof(char)); 68 | if (szMessage != NULL) 69 | { 70 | vsnprintf(szMessage, iResultLen, format, arglist); 71 | MsiRecordSetStringA(hRecordProg, 2, szMessage); 72 | free(szMessage); 73 | } 74 | else 75 | { 76 | /* Use stack variant anyway, but make sure it's zero-terminated. */ 77 | szBufStack[_countof(szBufStack) - 1] = 0; 78 | MsiRecordSetStringA(hRecordProg, 2, szBufStack); 79 | } 80 | } 81 | } 82 | 83 | if ((flags & M_ERRNO) == 0) 84 | { 85 | /* Field 1: MSI Error Code */ 86 | MsiRecordSetInteger(hRecordProg, 1, ERROR_MSICA); 87 | } 88 | else 89 | { 90 | /* Field 1: MSI Error Code */ 91 | MsiRecordSetInteger(hRecordProg, 1, ERROR_MSICA_ERRNO); 92 | 93 | /* Field 3: The Windows error number. */ 94 | MsiRecordSetInteger(hRecordProg, 3, dwResult); 95 | 96 | /* Field 4: The Windows error description. */ 97 | LPTSTR szErrMessage = NULL; 98 | if (FormatMessage( 99 | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS, 100 | 0, 101 | dwResult, 102 | 0, 103 | (LPTSTR)&szErrMessage, 104 | 0, 105 | NULL) && szErrMessage) 106 | { 107 | /* Trim trailing whitespace. Set terminator after the last non-whitespace character. This prevents excessive trailing line breaks. */ 108 | for (size_t i = 0, i_last = 0;; i++) 109 | { 110 | if (szErrMessage[i]) 111 | { 112 | if (!_istspace(szErrMessage[i])) 113 | { 114 | i_last = i + 1; 115 | } 116 | } 117 | else 118 | { 119 | szErrMessage[i_last] = 0; 120 | break; 121 | } 122 | } 123 | MsiRecordSetString(hRecordProg, 4, szErrMessage); 124 | LocalFree(szErrMessage); 125 | } 126 | } 127 | 128 | MsiProcessMessage(s->hInstall, (flags & M_WARN) ? INSTALLMESSAGE_INFO : INSTALLMESSAGE_ERROR, hRecordProg); 129 | MsiCloseHandle(hRecordProg); 130 | } -------------------------------------------------------------------------------- /msm/log.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ovpn-dco-win OpenVPN protocol accelerator for Windows 3 | * 4 | * Copyright (C) 2023 OpenVPN Inc 5 | * Copyright (C) 2018-2023 Simon Rozman 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License version 2 9 | * as published by the Free Software Foundation. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | 21 | #pragma once 22 | 23 | #include 24 | #include 25 | 26 | #include 27 | 28 | /* 29 | * Error codes (next unused 2552L) 30 | */ 31 | #define ERROR_MSICA 2550L 32 | #define ERROR_MSICA_ERRNO 2551L 33 | 34 | #define M_FATAL (1<<4) /* exit program */ 35 | #define M_NONFATAL (1<<5) /* non-fatal error */ 36 | #define M_WARN (1<<6) /* call syslog with LOG_WARNING */ 37 | #define M_ERRNO (1<<8) /* show errno description */ 38 | 39 | 40 | /** 41 | * MSI session handle thread local storage index 42 | */ 43 | extern DWORD ovpndco_thread_data_idx; 44 | 45 | /** 46 | * Thread local storage data 47 | */ 48 | struct ovpndco_thread_data 49 | { 50 | MSIHANDLE hInstall; /** Handle to the installation session. */ 51 | }; 52 | 53 | /** 54 | * Set MSI session handle in thread local storage. 55 | */ 56 | #define OVPNDCO_SAVE_MSI_SESSION(handle) \ 57 | { \ 58 | struct ovpndco_thread_data *s = (struct ovpndco_thread_data *)TlsGetValue(ovpndco_thread_data_idx); \ 59 | s->hInstall = (handle); \ 60 | } 61 | 62 | #define EXIT_FATAL(flags) do { if ((flags) & M_FATAL) {_exit(1);}} while (false) 63 | 64 | void x_msg(const unsigned int flags, const char* format, ...); 65 | 66 | void x_msg_va(const unsigned int flags, const char* format, va_list arglist); 67 | 68 | #define msg(flags, ...) do { x_msg((flags), __VA_ARGS__); EXIT_FATAL(flags); } while (false) 69 | -------------------------------------------------------------------------------- /msm/msi.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ovpn-dco-win OpenVPN protocol accelerator for Windows 3 | * 4 | * Copyright (C) 2023 OpenVPN Inc 5 | * 6 | * Author: Lev Stipakov 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License version 2 10 | * as published by the Free Software Foundation. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along 18 | * with this program; if not, write to the Free Software Foundation, Inc., 19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | */ 21 | 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | 34 | #include "log.h" 35 | 36 | #define FILE_NEED_REBOOT L".ovpn_need_reboot" 37 | 38 | // this must match cbSize = sizeof(*drvDetails); 234 | if (!SetupDiGetDriverInfoDetailW(devInfoSet, NULL, &drvInfo, drvDetails, size, &size)) { 235 | msg(M_NONFATAL | M_ERRNO, "%s: SetupDiGetDriverInfoDetailW failed", __FUNCTION__); 236 | free(drvDetails); 237 | goto cleanupDriverInfoList; 238 | } 239 | if (wcscmp(hwid, drvDetails->HardwareID) == 0) { 240 | PathStripPathW(drvDetails->InfFileName); 241 | wcscpy_s(publishedName, len, drvDetails->InfFileName); 242 | free(drvDetails); 243 | res = TRUE; 244 | break; 245 | } 246 | free(drvDetails); 247 | } 248 | 249 | cleanupDriverInfoList: 250 | SetupDiDestroyDriverInfoList(devInfoSet, NULL, SPDIT_CLASSDRIVER); 251 | cleanupDeviceInfoSet: 252 | SetupDiDestroyDeviceInfoList(devInfoSet); 253 | 254 | return res; 255 | } 256 | 257 | static void 258 | DeleteDriver(_In_z_ LPCWSTR pathToTmp) 259 | { 260 | /* delete driver */ 261 | WCHAR publishedName[MAX_PATH] = { 0 }; 262 | if (GetPublishedDriverName(OVPN_DCO_HWID, publishedName, _countof(publishedName))) { 263 | if (!SetupUninstallOEMInfW(publishedName, 0, NULL)) { 264 | msg(M_NONFATAL | M_ERRNO, "%s: SetupUninstallOEMInfW(\"%ls\") failed", __FUNCTION__, publishedName); 265 | } 266 | } 267 | } 268 | 269 | UINT __stdcall 270 | MsiProcess(MSIHANDLE handle) 271 | { 272 | BOOL coInitialsed = SUCCEEDED(CoInitialize(NULL)); 273 | 274 | LPWSTR customData = NULL; 275 | UINT res = MsiGetString(handle, L"CustomActionData", &customData); 276 | if (res != ERROR_SUCCESS) { 277 | goto cleanup; 278 | } 279 | 280 | int i = 0; 281 | WCHAR action[ACTION_LEN] = { 0 }; 282 | WCHAR pathToInf[MAX_PATH] = { 0 }; 283 | WCHAR pathToTmp[MAX_PATH] = { 0 }; 284 | 285 | WCHAR* pos = NULL; 286 | WCHAR* token = wcstok_s(customData, L"|", &pos); 287 | /* action|path_to_inf_file|path_to_tmp_dir */ 288 | while (token) { 289 | switch (i++) { 290 | case 0: 291 | wcscpy_s(action, _countof(action), token); 292 | break; 293 | 294 | case 1: 295 | wcscpy_s(pathToInf, _countof(pathToInf), token); 296 | break; 297 | 298 | case 2: 299 | wcscpy_s(pathToTmp, _countof(pathToTmp), token); 300 | break; 301 | } 302 | token = wcstok_s(NULL, L"|", &pos); 303 | } 304 | 305 | if (wcscmp(action, ACTION_ADD_DRIVER) == 0) { 306 | AddDriver(pathToInf, pathToTmp); 307 | } 308 | else if (wcscmp(action, ACTION_DELETE_DRIVER) == 0) { 309 | DeleteDriver(pathToTmp); 310 | } 311 | 312 | cleanup: 313 | free(customData); 314 | 315 | if (coInitialsed) { 316 | CoUninitialize(); 317 | } 318 | 319 | return 0; 320 | } 321 | -------------------------------------------------------------------------------- /msm/ovpn-dco.wxs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | NOT NETADAPTERCX21 33 | 34 | 35 | 36 | NOT NETADAPTERCX21 37 | 38 | 39 | 40 | NOT NETADAPTERCX21 41 | 42 | 43 | 44 | 45 | 46 | NETADAPTERCX21 47 | 48 | 49 | 50 | NETADAPTERCX21 51 | 52 | 53 | 54 | NETADAPTERCX21 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /msm/sampleinstaller.wxs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 33 | 34 | WIN102004 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /mss.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * ovpn-dco-win OpenVPN protocol accelerator for Windows 3 | * 4 | * Copyright (C) 2020-2022 OpenVPN Inc 5 | * 6 | * Author: Lev Stipakov 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License version 2 10 | * as published by the Free Software Foundation. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along 18 | * with this program; if not, write to the Free Software Foundation, Inc., 19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | */ 21 | 22 | #pragma once 23 | 24 | #include 25 | #include 26 | 27 | #include "mss.h" 28 | #include "trace.h" 29 | 30 | BOOLEAN 31 | OvpnMssIsIPv4(_In_ UCHAR* buf, SIZE_T len) 32 | { 33 | if (len < sizeof(IPV4_HEADER)) 34 | return FALSE; 35 | 36 | IPV4_HEADER* hdr = (IPV4_HEADER*)buf; 37 | return hdr->Version == IPV4_VERSION; 38 | } 39 | 40 | BOOLEAN 41 | OvpnMssIsIPv6(_In_ UCHAR* buf, SIZE_T len) 42 | { 43 | if (len < sizeof(IPV6_HEADER)) 44 | return FALSE; 45 | 46 | IPV6_HEADER* hdr = (IPV6_HEADER*)buf; 47 | return hdr->Version == 6; 48 | } 49 | 50 | /* 51 | * The following macro is used to update an 52 | * internet checksum. "acc" is a 32-bit 53 | * accumulation of all the changes to the 54 | * checksum (adding in old 16-bit words and 55 | * subtracting out new words), and "cksum" 56 | * is the checksum value to be updated. 57 | */ 58 | #define ADJUST_CHECKSUM(acc, cksum) { \ 59 | int _acc = acc; \ 60 | _acc += (cksum); \ 61 | if (_acc < 0) { \ 62 | _acc = -_acc; \ 63 | _acc = (_acc >> 16) + (_acc & 0xffff); \ 64 | _acc += _acc >> 16; \ 65 | (cksum) = (UINT16) ~_acc; \ 66 | } else { \ 67 | _acc = (_acc >> 16) + (_acc & 0xffff); \ 68 | _acc += _acc >> 16; \ 69 | (cksum) = (UINT16) _acc; \ 70 | } \ 71 | } 72 | 73 | static 74 | VOID 75 | OvpnMssDoWork(_In_ UCHAR* buf, SIZE_T len, UINT16 maxmss) 76 | { 77 | if (len < sizeof(TCP_HDR)) 78 | return; 79 | 80 | TCP_HDR* th = (TCP_HDR*)buf; 81 | SIZE_T hlen = (SIZE_T)th->th_len << 2; 82 | 83 | // header without options or invalid header len 84 | if ((hlen <= sizeof(TCP_HDR)) || (hlen > len)) 85 | return; 86 | 87 | SIZE_T olen, optlen; 88 | UCHAR* opt; 89 | for (olen = hlen - sizeof(TCP_HDR), 90 | opt = (UCHAR*)(th + 1); 91 | olen > 1; 92 | olen -= optlen, opt += optlen) { 93 | if (*opt == TH_OPT_EOL) { 94 | break; 95 | } 96 | else if (*opt == TH_OPT_NOP) { 97 | optlen = 1; 98 | } else { 99 | optlen = *(opt + 1); 100 | if (optlen <= 0 || optlen > olen) { 101 | break; 102 | } 103 | if (*opt == TH_OPT_MSS) { 104 | if (optlen != sizeof(TCP_OPT_MSS)) { 105 | continue; 106 | } 107 | TCP_OPT_MSS* opt_mss = (TCP_OPT_MSS*)opt; 108 | UINT16 mssval = RtlUshortByteSwap(opt_mss->Mss); 109 | if (mssval > maxmss) { 110 | // LOG_INFO("Adjust MSS", TraceLoggingValue(mssval, "old"), TraceLoggingValue(maxmss, "new")); 111 | int accumulate = opt_mss->Mss; 112 | opt_mss->Mss = RtlUshortByteSwap(maxmss); 113 | accumulate -= RtlUshortByteSwap(maxmss); 114 | ADJUST_CHECKSUM(accumulate, th->th_sum); 115 | } 116 | } 117 | } 118 | } 119 | } 120 | 121 | VOID 122 | OvpnMssDoIPv4(_In_ UCHAR* buf, SIZE_T len, UINT16 mss) 123 | { 124 | if (mss == 0) { 125 | return; 126 | } 127 | 128 | IPV4_HEADER* ipv4_hdr = (IPV4_HEADER*)buf; 129 | SIZE_T hlen = (SIZE_T)ipv4_hdr->HeaderLength << 2; 130 | 131 | if (ipv4_hdr->Protocol == IPPROTO_TCP 132 | && RtlUshortByteSwap(ipv4_hdr->TotalLength) == len 133 | && RtlUshortByteSwap(ipv4_hdr->FlagsAndOffset & IP4_OFF_MASK) == 0 134 | && hlen <= len 135 | && len - hlen >= sizeof(TCP_HDR)) 136 | { 137 | buf += hlen; 138 | len -= hlen; 139 | TCP_HDR* tcp_hdr = (TCP_HDR*)buf; 140 | if (tcp_hdr->th_flags & TH_SYN) { 141 | OvpnMssDoWork(buf, len, mss); 142 | } 143 | } 144 | } 145 | 146 | VOID 147 | OvpnMssDoIPv6(_In_ UCHAR* buf, SIZE_T len, UINT16 mss) 148 | { 149 | if (mss == 0) { 150 | return; 151 | } 152 | 153 | IPV6_HEADER* ipv6_hdr = (IPV6_HEADER*)buf; 154 | 155 | // do we have full ipv6 packet? 156 | if (len != RtlUshortByteSwap(ipv6_hdr->PayloadLength) + sizeof(IPV6_HEADER)) { 157 | return; 158 | } 159 | 160 | if (ipv6_hdr->NextHeader != IPPROTO_TCP) { 161 | return; 162 | } 163 | 164 | buf += sizeof(IPV6_HEADER); 165 | len -= sizeof(IPV6_HEADER); 166 | 167 | TCP_HDR* tcp_hdr = (TCP_HDR*)buf; 168 | if (tcp_hdr->th_flags & TH_SYN) { 169 | OvpnMssDoWork(buf, len, mss - 20); 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /mss.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ovpn-dco-win OpenVPN protocol accelerator for Windows 3 | * 4 | * Copyright (C) 2020-2022 OpenVPN Inc 5 | * 6 | * Author: Lev Stipakov 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License version 2 10 | * as published by the Free Software Foundation. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along 18 | * with this program; if not, write to the Free Software Foundation, Inc., 19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | */ 21 | 22 | #pragma once 23 | 24 | #include 25 | 26 | BOOLEAN 27 | OvpnMssIsIPv4(_In_ UCHAR* buf, SIZE_T len); 28 | 29 | VOID 30 | OvpnMssDoIPv4(_In_ UCHAR* buf, SIZE_T len, UINT16 mss); 31 | 32 | BOOLEAN 33 | OvpnMssIsIPv6(_In_ UCHAR* buf, SIZE_T len); 34 | 35 | VOID 36 | OvpnMssDoIPv6(_In_ UCHAR* buf, SIZE_T len, UINT16 mss); 37 | -------------------------------------------------------------------------------- /netringiterator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Copyright (C) Microsoft Corporation. All rights reserved. 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | typedef struct _NET_RING_ITERATOR 10 | { 11 | 12 | NET_RING_COLLECTION const* 13 | Rings; 14 | 15 | UINT32* const 16 | IndexToSet; 17 | 18 | UINT32 19 | Index; 20 | 21 | UINT32 const 22 | End; 23 | 24 | } NET_RING_ITERATOR; 25 | 26 | typedef struct _NET_RING_PACKET_ITERATOR 27 | { 28 | 29 | NET_RING_ITERATOR 30 | Iterator; 31 | 32 | } NET_RING_PACKET_ITERATOR; 33 | 34 | typedef struct _NET_RING_FRAGMENT_ITERATOR 35 | { 36 | 37 | NET_RING_ITERATOR 38 | Iterator; 39 | 40 | } NET_RING_FRAGMENT_ITERATOR; 41 | 42 | 43 | inline 44 | NET_RING_PACKET_ITERATOR 45 | NetRingGetPostPackets( 46 | _In_ NET_RING_COLLECTION const* Rings 47 | ) 48 | { 49 | NET_RING* ring = Rings->Rings[NetRingTypePacket]; 50 | NET_RING_PACKET_ITERATOR iterator = { 51 | Rings, &ring->NextIndex, ring->NextIndex, ring->EndIndex, 52 | }; 53 | 54 | return iterator; 55 | } 56 | 57 | inline 58 | NET_RING_PACKET_ITERATOR 59 | NetRingGetDrainPackets( 60 | _In_ NET_RING_COLLECTION const* Rings 61 | ) 62 | { 63 | NET_RING* ring = Rings->Rings[NetRingTypePacket]; 64 | NET_RING_PACKET_ITERATOR iterator = { 65 | Rings, &ring->BeginIndex, ring->BeginIndex, ring->NextIndex, 66 | }; 67 | 68 | return iterator; 69 | } 70 | 71 | inline 72 | NET_RING_PACKET_ITERATOR 73 | NetRingGetAllPackets( 74 | _In_ NET_RING_COLLECTION const* Rings 75 | ) 76 | { 77 | NET_RING* ring = Rings->Rings[NetRingTypePacket]; 78 | NET_RING_PACKET_ITERATOR iterator = { 79 | Rings, &ring->BeginIndex, ring->BeginIndex, ring->EndIndex, 80 | }; 81 | 82 | return iterator; 83 | } 84 | 85 | inline 86 | NET_PACKET* 87 | NetPacketIteratorGetPacket( 88 | _In_ NET_RING_PACKET_ITERATOR const* Iterator 89 | ) 90 | { 91 | return NetRingGetPacketAtIndex( 92 | Iterator->Iterator.Rings->Rings[NetRingTypePacket], 93 | Iterator->Iterator.Index); 94 | } 95 | 96 | inline 97 | UINT32 98 | NetPacketIteratorGetIndex( 99 | _In_ NET_RING_PACKET_ITERATOR const* Iterator 100 | ) 101 | { 102 | return Iterator->Iterator.Index; 103 | } 104 | 105 | inline 106 | BOOLEAN 107 | NetPacketIteratorHasAny( 108 | _In_ NET_RING_PACKET_ITERATOR const* Iterator 109 | ) 110 | { 111 | return Iterator->Iterator.Index != Iterator->Iterator.End; 112 | } 113 | 114 | inline 115 | UINT32 116 | NetPacketIteratorGetCount( 117 | _In_ NET_RING_PACKET_ITERATOR const* Iterator 118 | ) 119 | { 120 | NET_RING const* ring = Iterator->Iterator.Rings->Rings[NetRingTypePacket]; 121 | 122 | return (Iterator->Iterator.End - Iterator->Iterator.Index) & ring->ElementIndexMask; 123 | } 124 | 125 | inline 126 | void 127 | NetPacketIteratorAdvance( 128 | _In_ NET_RING_PACKET_ITERATOR* Iterator 129 | ) 130 | { 131 | Iterator->Iterator.Index = NetRingIncrementIndex( 132 | Iterator->Iterator.Rings->Rings[NetRingTypePacket], 133 | Iterator->Iterator.Index); 134 | } 135 | 136 | inline 137 | void 138 | NetPacketIteratorAdvanceToTheEnd( 139 | _In_ NET_RING_PACKET_ITERATOR* Iterator 140 | ) 141 | { 142 | Iterator->Iterator.Index = Iterator->Iterator.End; 143 | } 144 | 145 | inline 146 | void 147 | NetPacketIteratorSet( 148 | _In_ NET_RING_PACKET_ITERATOR const* Iterator 149 | ) 150 | { 151 | *Iterator->Iterator.IndexToSet 152 | = Iterator->Iterator.Index; 153 | } 154 | 155 | 156 | inline 157 | NET_RING_FRAGMENT_ITERATOR 158 | NetPacketIteratorGetFragments( 159 | _In_ NET_RING_PACKET_ITERATOR const* Iterator 160 | ) 161 | { 162 | NET_RING const* ring = Iterator->Iterator.Rings->Rings[NetRingTypeFragment]; 163 | NET_PACKET const* packet = NetPacketIteratorGetPacket(Iterator); 164 | UINT32 const end = NetRingIncrementIndex(ring, 165 | packet->FragmentIndex + packet->FragmentCount - 1); 166 | NET_RING_FRAGMENT_ITERATOR iterator = { 167 | Iterator->Iterator.Rings, NULL, packet->FragmentIndex, end, 168 | }; 169 | 170 | return iterator; 171 | } 172 | 173 | inline 174 | NET_RING_FRAGMENT_ITERATOR 175 | NetRingGetPostFragments( 176 | _In_ NET_RING_COLLECTION const* Rings 177 | ) 178 | { 179 | NET_RING* ring = Rings->Rings[NetRingTypeFragment]; 180 | NET_RING_FRAGMENT_ITERATOR iterator = { 181 | Rings, &ring->NextIndex, ring->NextIndex, ring->EndIndex, 182 | }; 183 | 184 | return iterator; 185 | } 186 | 187 | inline 188 | NET_RING_FRAGMENT_ITERATOR 189 | NetRingGetDrainFragments( 190 | _In_ NET_RING_COLLECTION const* Rings 191 | ) 192 | { 193 | NET_RING* ring = Rings->Rings[NetRingTypeFragment]; 194 | NET_RING_FRAGMENT_ITERATOR iterator = { 195 | Rings, &ring->BeginIndex, ring->BeginIndex, ring->NextIndex, 196 | }; 197 | 198 | return iterator; 199 | } 200 | 201 | inline 202 | NET_RING_FRAGMENT_ITERATOR 203 | NetRingGetAllFragments( 204 | _In_ NET_RING_COLLECTION const* Rings 205 | ) 206 | { 207 | NET_RING* ring = Rings->Rings[NetRingTypeFragment]; 208 | NET_RING_FRAGMENT_ITERATOR iterator = { 209 | Rings, &ring->BeginIndex, ring->BeginIndex, ring->EndIndex, 210 | }; 211 | 212 | return iterator; 213 | } 214 | 215 | inline 216 | NET_FRAGMENT* 217 | NetFragmentIteratorGetFragment( 218 | _In_ NET_RING_FRAGMENT_ITERATOR const* Iterator 219 | ) 220 | { 221 | return NetRingGetFragmentAtIndex( 222 | Iterator->Iterator.Rings->Rings[NetRingTypeFragment], 223 | Iterator->Iterator.Index); 224 | } 225 | 226 | inline 227 | UINT32 228 | NetFragmentIteratorGetIndex( 229 | _In_ NET_RING_FRAGMENT_ITERATOR const* Iterator 230 | ) 231 | { 232 | return Iterator->Iterator.Index; 233 | } 234 | 235 | inline 236 | BOOLEAN 237 | NetFragmentIteratorHasAny( 238 | _In_ NET_RING_FRAGMENT_ITERATOR const* Iterator 239 | ) 240 | { 241 | return Iterator->Iterator.Index != Iterator->Iterator.End; 242 | } 243 | 244 | inline 245 | UINT32 246 | NetFragmentIteratorGetCount( 247 | _In_ NET_RING_FRAGMENT_ITERATOR const* Iterator 248 | ) 249 | { 250 | NET_RING const* ring = Iterator->Iterator.Rings->Rings[NetRingTypeFragment]; 251 | 252 | return (Iterator->Iterator.End - Iterator->Iterator.Index) & ring->ElementIndexMask; 253 | } 254 | 255 | inline 256 | void 257 | NetFragmentIteratorAdvance( 258 | _In_ NET_RING_FRAGMENT_ITERATOR* Iterator 259 | ) 260 | { 261 | Iterator->Iterator.Index = NetRingIncrementIndex( 262 | Iterator->Iterator.Rings->Rings[NetRingTypeFragment], 263 | Iterator->Iterator.Index); 264 | } 265 | 266 | inline 267 | void 268 | NetFragmentIteratorAdvanceToTheEnd( 269 | _In_ NET_RING_FRAGMENT_ITERATOR* Iterator 270 | ) 271 | { 272 | Iterator->Iterator.Index = Iterator->Iterator.End; 273 | } 274 | 275 | inline 276 | void 277 | NetFragmentIteratorSet( 278 | _In_ NET_RING_FRAGMENT_ITERATOR const* Iterator 279 | ) 280 | { 281 | *(Iterator->Iterator.IndexToSet) 282 | = Iterator->Iterator.Index; 283 | } 284 | -------------------------------------------------------------------------------- /notifyqueue.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * ovpn-dco-win OpenVPN protocol accelerator for Windows 3 | * 4 | * Copyright (C) 2024- OpenVPN Inc 5 | * 6 | * Author: Lev Stipakov 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License version 2 10 | * as published by the Free Software Foundation. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along 18 | * with this program; if not, write to the Free Software Foundation, Inc., 19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | */ 21 | 22 | #include "notifyqueue.h" 23 | 24 | #include "trace.h" 25 | 26 | VOID 27 | NotifyQueue::Init() 28 | { 29 | LOG_ENTER(); 30 | 31 | InitializeListHead(&Head); 32 | KeInitializeSpinLock(&Lock); 33 | 34 | LOG_EXIT(); 35 | } 36 | 37 | NTSTATUS 38 | NotifyQueue::AddEvent(OVPN_NOTIFY_CMD cmd, int peerId, OVPN_DEL_PEER_REASON delPeerReason) 39 | { 40 | NotifyEvent* event = (NotifyEvent*)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(NotifyEvent), 'ovpn'); 41 | if (!event) { 42 | return STATUS_MEMORY_NOT_ALLOCATED; 43 | } 44 | 45 | RtlZeroMemory(event, sizeof(NotifyEvent)); 46 | 47 | event->Cmd = cmd; 48 | event->PeerId = peerId; 49 | event->DelPeerReason = delPeerReason; 50 | 51 | ExInterlockedInsertTailList(&Head, &event->ListEntry, &Lock); 52 | 53 | return STATUS_SUCCESS; 54 | } 55 | 56 | NotifyEvent* 57 | NotifyQueue::GetEvent() 58 | { 59 | PLIST_ENTRY entry = ExInterlockedRemoveHeadList(&Head, &Lock); 60 | if (entry == nullptr) { 61 | return nullptr; 62 | } 63 | 64 | return CONTAINING_RECORD(entry, NotifyEvent, ListEntry); 65 | } 66 | 67 | VOID 68 | NotifyQueue::FreeEvent(NotifyEvent* event) 69 | { 70 | if (event != nullptr) { 71 | ExFreePoolWithTag(event, 'ovpn'); 72 | } 73 | } 74 | 75 | VOID 76 | NotifyQueue::FlushEvents() 77 | { 78 | LOG_ENTER(); 79 | 80 | NotifyEvent* event = nullptr; 81 | while ((event = GetEvent()) != nullptr) { 82 | FreeEvent(event); 83 | } 84 | 85 | LOG_EXIT(); 86 | } 87 | -------------------------------------------------------------------------------- /notifyqueue.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ovpn-dco-win OpenVPN protocol accelerator for Windows 3 | * 4 | * Copyright (C) 2024- OpenVPN Inc 5 | * 6 | * Author: Lev Stipakov 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License version 2 10 | * as published by the Free Software Foundation. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along 18 | * with this program; if not, write to the Free Software Foundation, Inc., 19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | */ 21 | 22 | #pragma once 23 | 24 | #include 25 | 26 | #include "uapi/ovpn-dco.h" 27 | 28 | struct NotifyEvent { 29 | LIST_ENTRY ListEntry; 30 | 31 | OVPN_NOTIFY_CMD Cmd; 32 | int PeerId; 33 | OVPN_DEL_PEER_REASON DelPeerReason; 34 | }; 35 | 36 | class NotifyQueue { 37 | private: 38 | LIST_ENTRY Head; 39 | KSPIN_LOCK Lock; 40 | 41 | public: 42 | NotifyQueue() = delete; 43 | 44 | VOID Init(); 45 | 46 | NTSTATUS AddEvent(OVPN_NOTIFY_CMD cmd, int peerId, OVPN_DEL_PEER_REASON delPeerReason=OVPN_DEL_PEER_REASON_EXPIRED); 47 | 48 | NotifyEvent* GetEvent(); 49 | 50 | VOID FreeEvent(NotifyEvent* event); 51 | 52 | VOID FlushEvents(); 53 | }; 54 | -------------------------------------------------------------------------------- /ovpn-dco-autologger.reg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenVPN/ovpn-dco-win/b51b2679c322a2c9f2279d0065673d569dad1aae/ovpn-dco-autologger.reg -------------------------------------------------------------------------------- /ovpn-dco-win.ddf: -------------------------------------------------------------------------------- 1 | ;*** Echo.ddf example 2 | ; 3 | .OPTION EXPLICIT ; Generate errors 4 | .Set CabinetFileCountThreshold=0 5 | .Set FolderFileCountThreshold=0 6 | .Set FolderSizeThreshold=0 7 | .Set MaxCabinetSize=0 8 | .Set MaxDiskFileCount=0 9 | .Set MaxDiskSize=0 10 | .Set CompressionType=MSZIP 11 | .Set Cabinet=on 12 | .Set Compress=on 13 | ;Specify file name for new cab file 14 | .Set CabinetNameTemplate=ovpn-dco-win.cab 15 | ; Specify the subdirectory for the files. 16 | ; Your cab file should not have files at the root level, 17 | ; and each driver package must be in a separate subfolder. 18 | .Set DestinationDir=ovpn-dco-win 19 | ;Specify files to be included in cab file 20 | ovpn-dco.inf 21 | ovpn-dco.sys 22 | ovpn-dco.pdb -------------------------------------------------------------------------------- /ovpn-dco-win.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.4.33213.308 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ovpn-dco", "ovpn-dco-win.vcxproj", "{C91D6DBF-F373-44CD-8179-D0F55888A015}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "installer", "msm\installer.vcxproj", "{FDA93664-CA76-44EF-89AA-625FBE9D64F6}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Debug|ARM64 = Debug|ARM64 14 | Debug|x64 = Debug|x64 15 | Debug|x86 = Debug|x86 16 | Debug-Win11|Any CPU = Debug-Win11|Any CPU 17 | Debug-Win11|ARM64 = Debug-Win11|ARM64 18 | Debug-Win11|x64 = Debug-Win11|x64 19 | Debug-Win11|x86 = Debug-Win11|x86 20 | Release|Any CPU = Release|Any CPU 21 | Release|ARM64 = Release|ARM64 22 | Release|x64 = Release|x64 23 | Release|x86 = Release|x86 24 | Release-Win11|Any CPU = Release-Win11|Any CPU 25 | Release-Win11|ARM64 = Release-Win11|ARM64 26 | Release-Win11|x64 = Release-Win11|x64 27 | Release-Win11|x86 = Release-Win11|x86 28 | EndGlobalSection 29 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 30 | {C91D6DBF-F373-44CD-8179-D0F55888A015}.Debug|Any CPU.ActiveCfg = Debug|Win32 31 | {C91D6DBF-F373-44CD-8179-D0F55888A015}.Debug|ARM64.ActiveCfg = Debug|ARM64 32 | {C91D6DBF-F373-44CD-8179-D0F55888A015}.Debug|ARM64.Build.0 = Debug|ARM64 33 | {C91D6DBF-F373-44CD-8179-D0F55888A015}.Debug|ARM64.Deploy.0 = Debug|ARM64 34 | {C91D6DBF-F373-44CD-8179-D0F55888A015}.Debug|x64.ActiveCfg = Debug|x64 35 | {C91D6DBF-F373-44CD-8179-D0F55888A015}.Debug|x64.Build.0 = Debug|x64 36 | {C91D6DBF-F373-44CD-8179-D0F55888A015}.Debug|x64.Deploy.0 = Debug|x64 37 | {C91D6DBF-F373-44CD-8179-D0F55888A015}.Debug|x86.ActiveCfg = Debug|Win32 38 | {C91D6DBF-F373-44CD-8179-D0F55888A015}.Debug|x86.Build.0 = Debug|Win32 39 | {C91D6DBF-F373-44CD-8179-D0F55888A015}.Debug|x86.Deploy.0 = Debug|Win32 40 | {C91D6DBF-F373-44CD-8179-D0F55888A015}.Debug-Win11|Any CPU.ActiveCfg = Debug-Win11|ARM 41 | {C91D6DBF-F373-44CD-8179-D0F55888A015}.Debug-Win11|Any CPU.Build.0 = Debug-Win11|ARM 42 | {C91D6DBF-F373-44CD-8179-D0F55888A015}.Debug-Win11|ARM64.ActiveCfg = Debug-Win11|ARM64 43 | {C91D6DBF-F373-44CD-8179-D0F55888A015}.Debug-Win11|ARM64.Build.0 = Debug-Win11|ARM64 44 | {C91D6DBF-F373-44CD-8179-D0F55888A015}.Debug-Win11|x64.ActiveCfg = Debug-Win11|x64 45 | {C91D6DBF-F373-44CD-8179-D0F55888A015}.Debug-Win11|x64.Build.0 = Debug-Win11|x64 46 | {C91D6DBF-F373-44CD-8179-D0F55888A015}.Debug-Win11|x86.ActiveCfg = Debug-Win11|x64 47 | {C91D6DBF-F373-44CD-8179-D0F55888A015}.Debug-Win11|x86.Build.0 = Debug-Win11|x64 48 | {C91D6DBF-F373-44CD-8179-D0F55888A015}.Release|Any CPU.ActiveCfg = Release|Win32 49 | {C91D6DBF-F373-44CD-8179-D0F55888A015}.Release|ARM64.ActiveCfg = Release|ARM64 50 | {C91D6DBF-F373-44CD-8179-D0F55888A015}.Release|ARM64.Build.0 = Release|ARM64 51 | {C91D6DBF-F373-44CD-8179-D0F55888A015}.Release|ARM64.Deploy.0 = Release|ARM64 52 | {C91D6DBF-F373-44CD-8179-D0F55888A015}.Release|x64.ActiveCfg = Release|x64 53 | {C91D6DBF-F373-44CD-8179-D0F55888A015}.Release|x64.Build.0 = Release|x64 54 | {C91D6DBF-F373-44CD-8179-D0F55888A015}.Release|x64.Deploy.0 = Release|x64 55 | {C91D6DBF-F373-44CD-8179-D0F55888A015}.Release|x86.ActiveCfg = Release|Win32 56 | {C91D6DBF-F373-44CD-8179-D0F55888A015}.Release|x86.Build.0 = Release|Win32 57 | {C91D6DBF-F373-44CD-8179-D0F55888A015}.Release|x86.Deploy.0 = Release|Win32 58 | {C91D6DBF-F373-44CD-8179-D0F55888A015}.Release-Win11|Any CPU.ActiveCfg = Release-Win11|ARM 59 | {C91D6DBF-F373-44CD-8179-D0F55888A015}.Release-Win11|Any CPU.Build.0 = Release-Win11|ARM 60 | {C91D6DBF-F373-44CD-8179-D0F55888A015}.Release-Win11|ARM64.ActiveCfg = Release-Win11|ARM64 61 | {C91D6DBF-F373-44CD-8179-D0F55888A015}.Release-Win11|ARM64.Build.0 = Release-Win11|ARM64 62 | {C91D6DBF-F373-44CD-8179-D0F55888A015}.Release-Win11|x64.ActiveCfg = Release-Win11|x64 63 | {C91D6DBF-F373-44CD-8179-D0F55888A015}.Release-Win11|x64.Build.0 = Release-Win11|x64 64 | {C91D6DBF-F373-44CD-8179-D0F55888A015}.Release-Win11|x86.ActiveCfg = Release-Win11|Win32 65 | {C91D6DBF-F373-44CD-8179-D0F55888A015}.Release-Win11|x86.Build.0 = Release-Win11|Win32 66 | {FDA93664-CA76-44EF-89AA-625FBE9D64F6}.Debug|Any CPU.ActiveCfg = Debug|x64 67 | {FDA93664-CA76-44EF-89AA-625FBE9D64F6}.Debug|Any CPU.Build.0 = Debug|x64 68 | {FDA93664-CA76-44EF-89AA-625FBE9D64F6}.Debug|ARM64.ActiveCfg = Debug|arm64 69 | {FDA93664-CA76-44EF-89AA-625FBE9D64F6}.Debug|ARM64.Build.0 = Debug|arm64 70 | {FDA93664-CA76-44EF-89AA-625FBE9D64F6}.Debug|x64.ActiveCfg = Debug|x64 71 | {FDA93664-CA76-44EF-89AA-625FBE9D64F6}.Debug|x64.Build.0 = Debug|x64 72 | {FDA93664-CA76-44EF-89AA-625FBE9D64F6}.Debug|x86.ActiveCfg = Debug|Win32 73 | {FDA93664-CA76-44EF-89AA-625FBE9D64F6}.Debug|x86.Build.0 = Debug|Win32 74 | {FDA93664-CA76-44EF-89AA-625FBE9D64F6}.Debug-Win11|Any CPU.ActiveCfg = Debug|x64 75 | {FDA93664-CA76-44EF-89AA-625FBE9D64F6}.Debug-Win11|Any CPU.Build.0 = Debug|x64 76 | {FDA93664-CA76-44EF-89AA-625FBE9D64F6}.Debug-Win11|ARM64.ActiveCfg = Debug|arm64 77 | {FDA93664-CA76-44EF-89AA-625FBE9D64F6}.Debug-Win11|ARM64.Build.0 = Debug|arm64 78 | {FDA93664-CA76-44EF-89AA-625FBE9D64F6}.Debug-Win11|x64.ActiveCfg = Debug|x64 79 | {FDA93664-CA76-44EF-89AA-625FBE9D64F6}.Debug-Win11|x86.ActiveCfg = Debug|Win32 80 | {FDA93664-CA76-44EF-89AA-625FBE9D64F6}.Debug-Win11|x86.Build.0 = Debug|Win32 81 | {FDA93664-CA76-44EF-89AA-625FBE9D64F6}.Release|Any CPU.ActiveCfg = Release|x64 82 | {FDA93664-CA76-44EF-89AA-625FBE9D64F6}.Release|Any CPU.Build.0 = Release|x64 83 | {FDA93664-CA76-44EF-89AA-625FBE9D64F6}.Release|ARM64.ActiveCfg = Release|arm64 84 | {FDA93664-CA76-44EF-89AA-625FBE9D64F6}.Release|ARM64.Build.0 = Release|arm64 85 | {FDA93664-CA76-44EF-89AA-625FBE9D64F6}.Release|x64.ActiveCfg = Release|x64 86 | {FDA93664-CA76-44EF-89AA-625FBE9D64F6}.Release|x64.Build.0 = Release|x64 87 | {FDA93664-CA76-44EF-89AA-625FBE9D64F6}.Release|x86.ActiveCfg = Release|Win32 88 | {FDA93664-CA76-44EF-89AA-625FBE9D64F6}.Release|x86.Build.0 = Release|Win32 89 | {FDA93664-CA76-44EF-89AA-625FBE9D64F6}.Release-Win11|Any CPU.ActiveCfg = Release|x64 90 | {FDA93664-CA76-44EF-89AA-625FBE9D64F6}.Release-Win11|Any CPU.Build.0 = Release|x64 91 | {FDA93664-CA76-44EF-89AA-625FBE9D64F6}.Release-Win11|ARM64.ActiveCfg = Release|arm64 92 | {FDA93664-CA76-44EF-89AA-625FBE9D64F6}.Release-Win11|ARM64.Build.0 = Release|arm64 93 | {FDA93664-CA76-44EF-89AA-625FBE9D64F6}.Release-Win11|x64.ActiveCfg = Release|x64 94 | {FDA93664-CA76-44EF-89AA-625FBE9D64F6}.Release-Win11|x86.ActiveCfg = Release|Win32 95 | {FDA93664-CA76-44EF-89AA-625FBE9D64F6}.Release-Win11|x86.Build.0 = Release|Win32 96 | EndGlobalSection 97 | GlobalSection(SolutionProperties) = preSolution 98 | HideSolutionNode = FALSE 99 | EndGlobalSection 100 | GlobalSection(ExtensibilityGlobals) = postSolution 101 | SolutionGuid = {1AF87E8C-07A5-48F3-AB76-2B658F7C02CF} 102 | EndGlobalSection 103 | EndGlobal 104 | -------------------------------------------------------------------------------- /ovpn-dco-win.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {8E41214B-6785-4CFE-B992-037D68949A14} 18 | inf;inv;inx;mof;mc; 19 | 20 | 21 | {5288a68c-a343-462e-a9a6-127ec12dd77a} 22 | 23 | 24 | 25 | 26 | Header Files 27 | 28 | 29 | Header Files 30 | 31 | 32 | Header Files 33 | 34 | 35 | Header Files 36 | 37 | 38 | Header Files 39 | 40 | 41 | Header Files 42 | 43 | 44 | Header Files 45 | 46 | 47 | Header Files 48 | 49 | 50 | Header Files 51 | 52 | 53 | Header Files 54 | 55 | 56 | Header Files 57 | 58 | 59 | Header Files\uapi 60 | 61 | 62 | Header Files 63 | 64 | 65 | Header Files 66 | 67 | 68 | Header Files 69 | 70 | 71 | Header Files 72 | 73 | 74 | Header Files 75 | 76 | 77 | Header Files 78 | 79 | 80 | 81 | 82 | Source Files 83 | 84 | 85 | Source Files 86 | 87 | 88 | Source Files 89 | 90 | 91 | Source Files 92 | 93 | 94 | Source Files 95 | 96 | 97 | Source Files 98 | 99 | 100 | Source Files 101 | 102 | 103 | Source Files 104 | 105 | 106 | Source Files 107 | 108 | 109 | Source Files 110 | 111 | 112 | Source Files 113 | 114 | 115 | Source Files 116 | 117 | 118 | Source Files 119 | 120 | 121 | Source Files 122 | 123 | 124 | 125 | 126 | Driver Files 127 | 128 | 129 | 130 | 131 | Resource Files 132 | 133 | 134 | -------------------------------------------------------------------------------- /ovpn-dco-win.wprp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ovpn-dco.inf: -------------------------------------------------------------------------------- 1 | [Version] 2 | Signature="$WINDOWS NT$" 3 | Class=Net 4 | ClassGuid={4d36e972-e325-11ce-bfc1-08002be10318} 5 | Provider=%ovpn-dco.CompanyName% 6 | CatalogFile=ovpn-dco.cat 7 | 8 | [DestinationDirs] 9 | DefaultDestDir = 12 10 | ovpn-dco_Device_CoInstaller_CopyFiles = 11 11 | 12 | ; ================= Class section ===================== 13 | 14 | [SourceDisksNames] 15 | 1 = %ovpn-dco.DiskName%,,,"" 16 | 17 | [SourceDisksFiles] 18 | ovpn-dco.sys = 1,, 19 | WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll=1 ; make sure the number matches with SourceDisksNames 20 | 21 | [Manufacturer] 22 | %ovpn-dco.CompanyName%=%ovpn-dco.Name%,NT$ARCH$ 23 | 24 | [ovpn-dco.NT$ARCH$] 25 | %ovpn-dco.DeviceDesc%=ovpn-dco_Device, ovpn-dco 26 | 27 | [ovpn-dco_Device.NT] 28 | CopyFiles=Drivers_Dir 29 | AddReg=ovpn-dco_AddReg 30 | 31 | Characteristics = 1 ; NCF_VIRTUAL 32 | 33 | *IfType = 53 ; IF_TYPE_PROP_VIRTUAL 34 | *MediaType = 19 ; NdisMediumIP 35 | *PhysicalMediaType = 0 ; NdisPhysicalMediumUnspecified 36 | 37 | *IfConnectorPresent = 0 38 | *ConnectionType = 1 ; NET_IF_CONNECTION_PASSIVE 39 | *DirectionType = 0 ; NET_IF_DIRECTION_SENDRECEIVE 40 | *AccessType = 2 ; NET_IF_ACCESS_POINT_TO_POINT 41 | *HardwareLoopback = 0 42 | 43 | [ovpn-dco_AddReg] 44 | HKR, Ndi\Interfaces, UpperRange, 0, "ndis5" 45 | HKR, Ndi\Interfaces, LowerRange, 0, "nolower" 46 | HKR, Ndi, Service, 0, %ovpn-dco.Name% 47 | 48 | [Drivers_Dir] 49 | ovpn-dco.sys 50 | 51 | [ovpn-dco_Device.NT.Services] 52 | AddService = ovpn-dco,%SPSVCINST_ASSOCSERVICE%, ovpn-dco_Service_Inst 53 | 54 | [ovpn-dco_Service_Inst] 55 | DisplayName = %ovpn-dco.Name% 56 | ServiceType = 1 ; SERVICE_KERNEL_DRIVER 57 | StartType = 3 ; SERVICE_DEMAND_START 58 | ErrorControl = 1 ; SERVICE_ERROR_NORMAL 59 | ServiceBinary = %12%\ovpn-dco.sys 60 | 61 | [ovpn-dco_Device.NT.CoInstallers] 62 | AddReg=ovpn-dco_Device_CoInstaller_AddReg 63 | CopyFiles=ovpn-dco_Device_CoInstaller_CopyFiles 64 | 65 | [ovpn-dco_Device_CoInstaller_AddReg] 66 | HKR,,CoInstallers32,0x00010000, "WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll,WdfCoInstaller" 67 | 68 | [ovpn-dco_Device_CoInstaller_CopyFiles] 69 | WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll 70 | 71 | [ovpn-dco_Device.NT.Wdf] 72 | KmdfService = ovpn-dco, ovpn-dco_wdfsect 73 | 74 | [ovpn-dco_wdfsect] 75 | KmdfLibraryVersion = $KMDFVERSION$ 76 | 77 | [Strings] 78 | SPSVCINST_ASSOCSERVICE= 0x00000002 79 | ovpn-dco.Name = "ovpn-dco" 80 | ovpn-dco.DiskName = "OpenVPN Data Channel Offload Install Disk" 81 | ovpn-dco.DeviceDesc = "OpenVPN Data Channel Offload" 82 | ovpn-dco.SVCDESC = %ovpn-dco.DeviceDesc% 83 | ovpn-dco.CompanyName = "OpenVPN, Inc" 84 | -------------------------------------------------------------------------------- /ovpn-dco.rc: -------------------------------------------------------------------------------- 1 | /* 2 | * ovpn-dco-win OpenVPN protocol accelerator for Windows 3 | * 4 | * Copyright (C) 2020-2021 OpenVPN Inc 5 | * 6 | * Author: Lev Stipakov 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License version 2 10 | * as published by the Free Software Foundation. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along 18 | * with this program; if not, write to the Free Software Foundation, Inc., 19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | */ 21 | 22 | #include 23 | 24 | #define STRINGIZE(x) #x 25 | #define EXPAND(x) STRINGIZE(x) 26 | 27 | VS_VERSION_INFO VERSIONINFO 28 | FILEVERSION OVPN_DCO_VERSION_MAJOR, OVPN_DCO_VERSION_MINOR, OVPN_DCO_VERSION_PATCH, 0 29 | PRODUCTVERSION OVPN_DCO_VERSION_MAJOR, OVPN_DCO_VERSION_MINOR, OVPN_DCO_VERSION_PATCH, 0 30 | FILEOS VOS_NT_WINDOWS32 31 | FILETYPE VFT_DRV 32 | FILESUBTYPE VFT2_DRV_SYSTEM 33 | BEGIN 34 | BLOCK "StringFileInfo" 35 | BEGIN 36 | BLOCK "040904b0" 37 | BEGIN 38 | VALUE "Comments", "OpenVPN Data Channel Offload" 39 | VALUE "CompanyName", "OpenVPN, Inc" 40 | VALUE "FileDescription", "OpenVPN Data Channel Offload" 41 | VALUE "FileVersion", EXPAND(OVPN_DCO_VERSION_STR) 42 | VALUE "InternalName", "OpenVPN Data Channel Offload" 43 | VALUE "LegalCopyright", "\xa9 2020-2021 OpenVPN, Inc." 44 | VALUE "OriginalFilename", "ovpn-dco.sys" 45 | VALUE "ProductName", "OpenVPN Data Channel Offload" 46 | VALUE "ProductVersion", EXPAND(OVPN_DCO_VERSION_STR) 47 | END 48 | END 49 | BLOCK "VarFileInfo" 50 | BEGIN 51 | VALUE "Translation", 0x409, 1200 52 | END 53 | END -------------------------------------------------------------------------------- /peer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ovpn-dco-win OpenVPN protocol accelerator for Windows 3 | * 4 | * Copyright (C) 2020-2021 OpenVPN Inc 5 | * Copyright (C) 2023 Rubicon Communications LLC (Netgate) 6 | * 7 | * Author: Lev Stipakov 8 | * 9 | * This program is free software; you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License version 2 11 | * as published by the Free Software Foundation. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License along 19 | * with this program; if not, write to the Free Software Foundation, Inc., 20 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | */ 22 | 23 | #pragma once 24 | 25 | #include 26 | 27 | #include "driver.h" 28 | #include "uapi\ovpn-dco.h" 29 | 30 | struct OvpnPeerContext 31 | { 32 | LIST_ENTRY ListEntry; // used by iroute tries for deferred cleanup 33 | 34 | EX_SPIN_LOCK SpinLock; 35 | 36 | OvpnCryptoContext CryptoContext; 37 | 38 | INT32 PeerId; 39 | 40 | // keepalive interval in seconds 41 | LONG KeepaliveInterval; 42 | 43 | // keepalive timeout in seconds 44 | LONG KeepaliveTimeout; 45 | 46 | // 1-sec timer which handles ping intervals and keepalive timeouts 47 | WDFTIMER Timer; 48 | 49 | UINT16 MSS; 50 | 51 | struct { 52 | IN_ADDR IPv4; 53 | IN6_ADDR IPv6; 54 | } VpnAddrs; 55 | 56 | struct { 57 | union { 58 | SOCKADDR_IN IPv4; 59 | SOCKADDR_IN6 IPv6; 60 | } Remote; 61 | 62 | } TransportAddrs; 63 | 64 | LONG RefCounter; 65 | }; 66 | 67 | _Must_inspect_result_ 68 | OvpnPeerContext* 69 | OvpnPeerCtxAlloc(); 70 | 71 | VOID 72 | OvpnPeerCtxFree(_In_ OvpnPeerContext*); 73 | 74 | VOID 75 | OvpnPeerCtxRelease(_In_ OvpnPeerContext*); 76 | 77 | RTL_GENERIC_ALLOCATE_ROUTINE OvpnPeerAllocateRoutine; 78 | RTL_GENERIC_FREE_ROUTINE OvpnPeerFreeRoutine; 79 | RTL_GENERIC_COMPARE_ROUTINE OvpnPeerCompareByPeerIdRoutine; 80 | RTL_GENERIC_COMPARE_ROUTINE OvpnPeerCompareByVPN4Routine; 81 | RTL_GENERIC_COMPARE_ROUTINE OvpnPeerCompareByVPN6Routine; 82 | 83 | _Must_inspect_result_ 84 | NTSTATUS 85 | OvpnAddPeerToTable(POVPN_DEVICE device, _In_ RTL_GENERIC_TABLE* table, _In_ OvpnPeerContext* peer); 86 | 87 | VOID 88 | OvpnCleanupPeerTable(_In_ POVPN_DEVICE device, _In_ RTL_GENERIC_TABLE*); 89 | 90 | _Must_inspect_result_ 91 | OvpnPeerContext* 92 | OvpnGetFirstPeer(_In_ POVPN_DEVICE device); 93 | 94 | _Must_inspect_result_ 95 | OvpnPeerContext* 96 | OvpnFindPeer(_In_ POVPN_DEVICE device, INT32 PeerId); 97 | 98 | _Must_inspect_result_ 99 | OvpnPeerContext* 100 | OvpnFindPeerVPN4(_In_ POVPN_DEVICE device, _In_ IN_ADDR addr); 101 | 102 | _Must_inspect_result_ 103 | OvpnPeerContext* 104 | OvpnFindPeerVPN6(_In_ POVPN_DEVICE device, _In_ IN6_ADDR addr); 105 | 106 | VOID 107 | OvpnDeletePeerFromTable(POVPN_DEVICE device, RTL_GENERIC_TABLE* table, OvpnPeerContext* peer, char* tableName); 108 | 109 | _Must_inspect_result_ 110 | _IRQL_requires_(PASSIVE_LEVEL) 111 | NTSTATUS 112 | OvpnPeerNew(_In_ POVPN_DEVICE device, WDFREQUEST request); 113 | 114 | _Must_inspect_result_ 115 | _IRQL_requires_(PASSIVE_LEVEL) 116 | NTSTATUS 117 | OvpnMPPeerNew(_In_ POVPN_DEVICE device, WDFREQUEST request); 118 | 119 | _Must_inspect_result_ 120 | _Requires_exclusive_lock_held_(device->SpinLock) 121 | NTSTATUS 122 | OvpnPeerSet(_In_ POVPN_DEVICE device, WDFREQUEST request); 123 | 124 | _Must_inspect_result_ 125 | _Requires_exclusive_lock_held_(device->SpinLock) 126 | NTSTATUS 127 | OvpnMPPeerSet(_In_ POVPN_DEVICE device, WDFREQUEST request); 128 | 129 | _Must_inspect_result_ 130 | NTSTATUS 131 | _Requires_shared_lock_held_(device->SpinLock) 132 | OvpnPeerGetStats(_In_ POVPN_DEVICE device, WDFREQUEST request, _Out_ ULONG_PTR* bytesReturned); 133 | 134 | _Must_inspect_result_ 135 | _IRQL_requires_(PASSIVE_LEVEL) 136 | _IRQL_requires_same_ 137 | NTSTATUS 138 | OvpnPeerStartVPN(_In_ POVPN_DEVICE device); 139 | 140 | _Must_inspect_result_ 141 | _Requires_exclusive_lock_held_(device->SpinLock) 142 | NTSTATUS 143 | OvpnPeerNewKey(_In_ POVPN_DEVICE device, WDFREQUEST request); 144 | 145 | _Must_inspect_result_ 146 | _Requires_exclusive_lock_held_(device->SpinLock) 147 | NTSTATUS 148 | OvpnPeerNewKeyV2(_In_ POVPN_DEVICE device, WDFREQUEST request); 149 | 150 | _Must_inspect_result_ 151 | NTSTATUS 152 | OvpnPeerSwapKeys(_In_ POVPN_DEVICE device); 153 | 154 | _Must_inspect_result_ 155 | NTSTATUS 156 | OvpnPeerDelete(POVPN_DEVICE device, INT32 peerId, OVPN_DEL_PEER_REASON reason, BOOLEAN notify); 157 | 158 | _Must_inspect_result_ 159 | NTSTATUS 160 | OvpnMPPeerDelete(POVPN_DEVICE device, WDFREQUEST request); 161 | 162 | _Must_inspect_result_ 163 | NTSTATUS 164 | OvpnMPPeerSwapKeys(_In_ POVPN_DEVICE device, WDFREQUEST request); -------------------------------------------------------------------------------- /pktid.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * ovpn-dco-win OpenVPN protocol accelerator for Windows 3 | * 4 | * Copyright (C) 2020-2021 OpenVPN Inc 5 | * 6 | * Author: Lev Stipakov 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License version 2 10 | * as published by the Free Software Foundation. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along 18 | * with this program; if not, write to the Free Software Foundation, Inc., 19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | */ 21 | 22 | #include "pktid.h" 23 | #include "trace.h" 24 | 25 | #include 26 | 27 | /* Warn when packet ID crosses this threshold. */ 28 | #define PKTID_WRAP_WARN 0xf0000000ULL 29 | 30 | _Use_decl_annotations_ 31 | NTSTATUS OvpnPktidXmitNext(OvpnPktidXmit* px, VOID* pktId, BOOLEAN pktId64bit) 32 | { 33 | ULONG64 seqNum = InterlockedIncrementNoFence64(&px->SeqNum); 34 | 35 | if (pktId64bit) { 36 | *static_cast(pktId) = seqNum; 37 | } 38 | else 39 | { 40 | *static_cast(pktId) = static_cast(seqNum); 41 | if (seqNum >= PKTID_WRAP_WARN) { 42 | LOG_ERROR("Pktid wrapped"); 43 | return STATUS_INTEGER_OVERFLOW; 44 | } 45 | } 46 | 47 | return STATUS_SUCCESS; 48 | } 49 | 50 | #define PKTID_RECV_EXPIRE ((30 * WDF_TIMEOUT_TO_SEC) / KeQueryTimeIncrement()) 51 | 52 | _Use_decl_annotations_ 53 | NTSTATUS OvpnPktidRecvVerify(OvpnPktidRecv* pr, UINT64 pktId) 54 | { 55 | LARGE_INTEGER now; 56 | KeQueryTickCount(&now); 57 | 58 | /* expire backtracks at or below pr->id after PKTID_RECV_EXPIRE time */ 59 | if ((now.QuadPart - pr->Expire.QuadPart) >= 0) 60 | pr->IdFloor = pr->Id; 61 | 62 | /* ID must not be zero */ 63 | if (pktId == 0) { 64 | return STATUS_INVALID_PARAMETER; 65 | } 66 | 67 | if (pktId == pr->Id + 1) { 68 | /* well-formed ID sequence (incremented by 1) */ 69 | pr->Base = REPLAY_INDEX(pr->Base, -1); 70 | pr->History[pr->Base / 8] |= (1 << (pr->Base % 8)); 71 | if (pr->Extent < REPLAY_WINDOW_SIZE) 72 | ++pr->Extent; 73 | pr->Id = pktId; 74 | } 75 | else if (pktId > pr->Id) { 76 | /* ID jumped forward by more than one */ 77 | const auto delta = pktId - pr->Id; 78 | 79 | if (delta < REPLAY_WINDOW_SIZE) { 80 | pr->Base = REPLAY_INDEX(pr->Base, -(INT32)delta); 81 | pr->History[pr->Base / 8] |= (1 << (pr->Base % 8)); 82 | pr->Extent += static_cast(delta); 83 | if (pr->Extent > REPLAY_WINDOW_SIZE) 84 | pr->Extent = REPLAY_WINDOW_SIZE; 85 | for (auto i = 1; i < delta; ++i) { 86 | const auto newb = REPLAY_INDEX(pr->Base, i); 87 | 88 | pr->History[newb / 8] &= ~BIT(newb % 8); 89 | } 90 | } 91 | else { 92 | pr->Base = 0; 93 | pr->Extent = REPLAY_WINDOW_SIZE; 94 | memset(pr->History, 0, sizeof(pr->History)); 95 | pr->History[0] = 1; 96 | } 97 | pr->Id = pktId; 98 | } 99 | else { 100 | /* ID backtrack */ 101 | const auto delta = pr->Id - pktId; 102 | 103 | if (delta < pr->Extent) { 104 | if (pktId > pr->IdFloor) { 105 | UINT32 ri = REPLAY_INDEX(pr->Base, delta); 106 | PUCHAR p = &pr->History[ri / 8]; 107 | UCHAR mask = (1 << (ri % 8)); 108 | 109 | if (*p & mask) 110 | return STATUS_INVALID_PARAMETER; 111 | *p |= mask; 112 | } 113 | else { 114 | return STATUS_INVALID_PARAMETER; 115 | } 116 | } 117 | else { 118 | return STATUS_INVALID_PARAMETER; 119 | } 120 | } 121 | 122 | pr->Expire.QuadPart = now.QuadPart + PKTID_RECV_EXPIRE; 123 | return STATUS_SUCCESS; 124 | } 125 | -------------------------------------------------------------------------------- /pktid.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ovpn-dco-win OpenVPN protocol accelerator for Windows 3 | * 4 | * Copyright (C) 2020-2021 OpenVPN Inc 5 | * 6 | * Author: Lev Stipakov 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License version 2 10 | * as published by the Free Software Foundation. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along 18 | * with this program; if not, write to the Free Software Foundation, Inc., 19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | */ 21 | 22 | #pragma once 23 | 24 | #include 25 | 26 | struct OvpnPktidXmit 27 | { 28 | LONG64 SeqNum; 29 | }; 30 | 31 | /* replay window sizing in bytes = 2^REPLAY_WINDOW_ORDER */ 32 | #define REPLAY_WINDOW_ORDER 8 33 | #define BIT(nr) (1UL << (nr)) 34 | #define REPLAY_WINDOW_BYTES BIT(REPLAY_WINDOW_ORDER) 35 | #define REPLAY_WINDOW_SIZE (REPLAY_WINDOW_BYTES * 8) 36 | #define REPLAY_INDEX(base, i) (((base) + (i)) & (REPLAY_WINDOW_SIZE - 1)) 37 | 38 | struct OvpnPktidRecv 39 | { 40 | /* "sliding window" bitmask of recent packet IDs received */ 41 | UCHAR History[REPLAY_WINDOW_BYTES]; 42 | 43 | /* bit position of deque base in history */ 44 | UINT32 Base; 45 | 46 | /* extent (in bits) of deque in history */ 47 | UINT32 Extent; 48 | 49 | /* expiration of history in count of timer interrupts */ 50 | LARGE_INTEGER Expire; 51 | 52 | /* highest sequence number received */ 53 | UINT64 Id; 54 | 55 | /* we will only accept backtrack IDs > id_floor */ 56 | UINT64 IdFloor; 57 | }; 58 | 59 | /* Get the next packet ID for xmit */ 60 | NTSTATUS OvpnPktidXmitNext(_In_ OvpnPktidXmit* px, _Out_ VOID* pktId, BOOLEAN pktId64bit); 61 | 62 | 63 | /* Packet replay detection. 64 | * Allows ID backtrack of up to REPLAY_WINDOW_SIZE - 1. 65 | */ 66 | NTSTATUS OvpnPktidRecvVerify(_In_ OvpnPktidRecv* pid, UINT64 pktId); 67 | -------------------------------------------------------------------------------- /rxqueue.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * ovpn-dco-win OpenVPN protocol accelerator for Windows 3 | * 4 | * Copyright (C) 2020-2021 OpenVPN Inc 5 | * 6 | * Author: Lev Stipakov 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License version 2 10 | * as published by the Free Software Foundation. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along 18 | * with this program; if not, write to the Free Software Foundation, Inc., 19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "driver.h" 29 | #include "bufferpool.h" 30 | #include "peer.h" 31 | #include "rxqueue.h" 32 | #include "netringiterator.h" 33 | #include "trace.h" 34 | 35 | EVT_PACKET_QUEUE_ADVANCE OvpnEvtRxQueueAdvance; 36 | 37 | _Use_decl_annotations_ 38 | VOID 39 | OvpnEvtRxQueueStart(NETPACKETQUEUE netPacketQueue) 40 | { 41 | LOG_ENTER(TraceLoggingPointer(netPacketQueue, "RxQueue")); 42 | 43 | POVPN_RXQUEUE queue = OvpnGetRxQueueContext(netPacketQueue); 44 | queue->Adapter->RxQueue = netPacketQueue; 45 | 46 | LOG_EXIT(); 47 | } 48 | 49 | _Use_decl_annotations_ 50 | VOID 51 | OvpnEvtRxQueueStop(NETPACKETQUEUE netPacketQueue) 52 | { 53 | LOG_ENTER(TraceLoggingPointer(netPacketQueue, "RxQueue")); 54 | 55 | POVPN_RXQUEUE queue = OvpnGetRxQueueContext(netPacketQueue); 56 | queue->Adapter->RxQueue = WDF_NO_HANDLE; 57 | 58 | LOG_EXIT(); 59 | } 60 | 61 | _Use_decl_annotations_ 62 | VOID 63 | OvpnEvtRxQueueDestroy(WDFOBJECT rxQueue) 64 | { 65 | LOG_ENTER(TraceLoggingPointer(rxQueue, "RxQueue")); 66 | LOG_EXIT(); 67 | } 68 | 69 | static inline UINT8 70 | OvpnRxQueueGetLayer4Type(const VOID* buf, size_t len) 71 | { 72 | UINT8 ret = NetPacketLayer4TypeUnspecified; 73 | 74 | if (len < sizeof(IPV4_HEADER)) 75 | return ret; 76 | 77 | const auto ipv4hdr = (IPV4_HEADER*)buf; 78 | if (ipv4hdr->Version == IPV4_VERSION) { 79 | if (ipv4hdr->Protocol == IPPROTO_TCP) 80 | ret = NetPacketLayer4TypeTcp; 81 | else if (ipv4hdr->Protocol == IPPROTO_UDP) 82 | ret = NetPacketLayer4TypeUdp; 83 | } 84 | else if (ipv4hdr->Version == 6) { 85 | if (len < sizeof(IPV6_HEADER)) 86 | return ret; 87 | 88 | const auto ipv6hdr = (IPV6_HEADER*)buf; 89 | if (ipv6hdr->NextHeader == IPPROTO_TCP) 90 | ret = NetPacketLayer4TypeTcp; 91 | else if (ipv6hdr->NextHeader == IPPROTO_UDP) 92 | ret = NetPacketLayer4TypeUdp; 93 | } 94 | 95 | return ret; 96 | } 97 | 98 | _Use_decl_annotations_ 99 | VOID 100 | OvpnEvtRxQueueAdvance(NETPACKETQUEUE netPacketQueue) 101 | { 102 | POVPN_RXQUEUE queue = OvpnGetRxQueueContext(netPacketQueue); 103 | OVPN_DEVICE* device = OvpnGetDeviceContext(queue->Adapter->WdfDevice); 104 | 105 | NET_RING_FRAGMENT_ITERATOR fi = NetRingGetAllFragments(queue->Rings); 106 | NET_RING_PACKET_ITERATOR pi = NetRingGetAllPackets(queue->Rings); 107 | while (NetFragmentIteratorHasAny(&fi)) { 108 | // get RX workitem, if any 109 | LIST_ENTRY* entry = OvpnBufferQueueDequeue(device->DataRxBufferQueue); 110 | if (entry == NULL) 111 | break; 112 | 113 | OVPN_RX_BUFFER* buffer = CONTAINING_RECORD(entry, OVPN_RX_BUFFER, QueueListEntry); 114 | 115 | NET_FRAGMENT* fragment = NetFragmentIteratorGetFragment(&fi); 116 | fragment->ValidLength = buffer->Len; 117 | fragment->Offset = 0; 118 | NET_FRAGMENT_VIRTUAL_ADDRESS* virtualAddr = NetExtensionGetFragmentVirtualAddress(&queue->VirtualAddressExtension, NetFragmentIteratorGetIndex(&fi)); 119 | RtlCopyMemory(virtualAddr->VirtualAddress, buffer->Data, buffer->Len); 120 | 121 | InterlockedExchangeAddNoFence64(&device->Stats.TunBytesReceived, buffer->Len); 122 | 123 | NET_PACKET* packet = NetPacketIteratorGetPacket(&pi); 124 | packet->FragmentIndex = NetFragmentIteratorGetIndex(&fi); 125 | packet->FragmentCount = 1; 126 | 127 | packet->Layout = {}; 128 | 129 | const auto checksum = NetExtensionGetPacketChecksum(&queue->ChecksumExtension, NetPacketIteratorGetIndex(&pi)); 130 | 131 | // Win11/2022 and newer 132 | if (checksum) { 133 | checksum->Layer3 = NetPacketRxChecksumEvaluationValid; // IP checksum 134 | checksum->Layer4 = NetPacketRxChecksumEvaluationValid; // TCP/UDP checksum 135 | packet->Layout.Layer4Type = OvpnRxQueueGetLayer4Type(virtualAddr->VirtualAddress, buffer->Len); 136 | } 137 | 138 | NetFragmentIteratorAdvance(&fi); 139 | NetPacketIteratorAdvance(&pi); 140 | 141 | OvpnRxBufferPoolPut(buffer); 142 | 143 | InterlockedIncrementNoFence(&device->Stats.ReceivedDataPackets); 144 | } 145 | NetFragmentIteratorSet(&fi); 146 | NetPacketIteratorSet(&pi); 147 | } 148 | 149 | _Use_decl_annotations_ 150 | VOID 151 | OvpnEvtRxQueueSetNotificationEnabled(NETPACKETQUEUE queue, BOOLEAN notificationEnabled) 152 | { 153 | POVPN_RXQUEUE rxQueue = OvpnGetRxQueueContext(queue); 154 | 155 | InterlockedExchangeNoFence(&rxQueue->NotificationEnabled, notificationEnabled); 156 | } 157 | 158 | _Use_decl_annotations_ 159 | VOID 160 | OvpnEvtRxQueueCancel(NETPACKETQUEUE netPacketQueue) 161 | { 162 | POVPN_RXQUEUE queue = OvpnGetRxQueueContext(netPacketQueue); 163 | 164 | // mark all packets as "ignore" 165 | NET_RING_PACKET_ITERATOR pi = NetRingGetAllPackets(queue->Rings); 166 | while (NetPacketIteratorHasAny(&pi)) { 167 | NetPacketIteratorGetPacket(&pi)->Ignore = 1; 168 | NetPacketIteratorAdvance(&pi); 169 | } 170 | NetPacketIteratorSet(&pi); 171 | 172 | // return all fragments' ownership back to netadapter 173 | NET_RING* fragmentRing = NetRingCollectionGetFragmentRing(queue->Rings); 174 | fragmentRing->BeginIndex = fragmentRing->EndIndex; 175 | } 176 | 177 | _Use_decl_annotations_ 178 | VOID 179 | OvpnRxQueueInitialize(NETPACKETQUEUE netPacketQueue, POVPN_ADAPTER adapter) 180 | { 181 | POVPN_RXQUEUE queue = OvpnGetRxQueueContext(netPacketQueue); 182 | queue->Adapter = adapter; 183 | queue->Rings = NetRxQueueGetRingCollection(netPacketQueue); 184 | 185 | NET_EXTENSION_QUERY extension; 186 | NET_EXTENSION_QUERY_INIT(&extension, NET_FRAGMENT_EXTENSION_VIRTUAL_ADDRESS_NAME, NET_FRAGMENT_EXTENSION_VIRTUAL_ADDRESS_VERSION_1, NetExtensionTypeFragment); 187 | NetRxQueueGetExtension(netPacketQueue, &extension, &queue->VirtualAddressExtension); 188 | 189 | // Query checksum packet extension offset and store it in the context 190 | NET_EXTENSION_QUERY_INIT(&extension, NET_PACKET_EXTENSION_CHECKSUM_NAME, NET_PACKET_EXTENSION_CHECKSUM_VERSION_1, NetExtensionTypePacket); 191 | NetRxQueueGetExtension(netPacketQueue, &extension, &queue->ChecksumExtension); 192 | } 193 | -------------------------------------------------------------------------------- /rxqueue.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ovpn-dco-win OpenVPN protocol accelerator for Windows 3 | * 4 | * Copyright (C) 2020-2021 OpenVPN Inc 5 | * 6 | * Author: Lev Stipakov 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License version 2 10 | * as published by the Free Software Foundation. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along 18 | * with this program; if not, write to the Free Software Foundation, Inc., 19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | */ 21 | 22 | #pragma once 23 | 24 | #include "adapter.h" 25 | 26 | typedef struct _OVPN_RXQUEUE 27 | { 28 | POVPN_ADAPTER Adapter; 29 | 30 | NET_RING_COLLECTION const * Rings; 31 | 32 | NET_EXTENSION VirtualAddressExtension; 33 | NET_EXTENSION ChecksumExtension; 34 | 35 | LONG NotificationEnabled = 0; 36 | } OVPN_RXQUEUE, * POVPN_RXQUEUE; 37 | 38 | WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(OVPN_RXQUEUE, OvpnGetRxQueueContext); 39 | 40 | EVT_PACKET_QUEUE_SET_NOTIFICATION_ENABLED OvpnEvtRxQueueSetNotificationEnabled; 41 | EVT_PACKET_QUEUE_ADVANCE OvpnEvtRxQueueAdvance; 42 | EVT_PACKET_QUEUE_CANCEL OvpnEvtRxQueueCancel; 43 | 44 | EVT_PACKET_QUEUE_START OvpnEvtRxQueueStart; 45 | EVT_PACKET_QUEUE_STOP OvpnEvtRxQueueStop; 46 | 47 | EVT_WDF_OBJECT_CONTEXT_DESTROY OvpnEvtRxQueueDestroy; 48 | 49 | VOID 50 | OvpnRxQueueInitialize(NETPACKETQUEUE rxQueue, _In_ POVPN_ADAPTER adapter); 51 | -------------------------------------------------------------------------------- /socket.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ovpn-dco-win OpenVPN protocol accelerator for Windows 3 | * 4 | * Copyright (C) 2020-2021 OpenVPN Inc 5 | * 6 | * Author: Lev Stipakov 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License version 2 10 | * as published by the Free Software Foundation. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along 18 | * with this program; if not, write to the Free Software Foundation, Inc., 19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | */ 21 | 22 | #pragma once 23 | 24 | #include 25 | #include 26 | 27 | #include "bufferpool.h" 28 | 29 | struct OvpnSocketTcpState 30 | { 31 | // filled with 2-bytes length which prepends OpenVPN TCP packet 32 | UCHAR LenBuf[2]; 33 | 34 | USHORT PacketLength; 35 | 36 | // how many bytes already read for header or buffer 37 | USHORT BytesRead; 38 | 39 | // packet buffer if packet is scattered across MDLs 40 | UCHAR PacketBuf[OVPN_SOCKET_RX_PACKET_BUFFER_SIZE]; 41 | }; 42 | 43 | struct OvpnSocketUdpState 44 | { 45 | // packet buffer if datagram scattered across MDLs 46 | // this seems to only happen in unlikely case when datagram is fragmented 47 | UCHAR PacketBuf[OVPN_SOCKET_RX_PACKET_BUFFER_SIZE]; 48 | }; 49 | 50 | struct OvpnSocket 51 | { 52 | BOOLEAN Tcp; 53 | PWSK_SOCKET Socket; 54 | 55 | OvpnSocketTcpState TcpState; 56 | OvpnSocketUdpState UdpState; 57 | }; 58 | 59 | _Must_inspect_result_ 60 | _IRQL_requires_(PASSIVE_LEVEL) 61 | NTSTATUS 62 | OvpnSocketInit(_In_ WSK_PROVIDER_NPI* wskProviderNpi, _In_ WSK_REGISTRATION* wskRegistration, ADDRESS_FAMILY addrFamily, 63 | BOOLEAN tcp, _In_ PSOCKADDR localAddr, _In_opt_ PSOCKADDR remoteAddr, SIZE_T remoteAddrSize, 64 | _In_ PVOID deviceContext, _Out_ PWSK_SOCKET* socket, BOOLEAN ipv6only); 65 | 66 | _Must_inspect_result_ 67 | _IRQL_requires_(PASSIVE_LEVEL) 68 | NTSTATUS 69 | OvpnSocketClose(_In_opt_ PWSK_SOCKET socket); 70 | 71 | _Must_inspect_result_ 72 | NTSTATUS 73 | OvpnSocketSend(_In_ OvpnSocket* ovpnSocket, _In_ OVPN_TX_BUFFER* buffer, _In_opt_ SOCKADDR* sa); 74 | 75 | _Must_inspect_result_ 76 | NTSTATUS 77 | OvpnSocketTcpConnect(_In_ PWSK_SOCKET socket, _In_ PVOID context, _In_ PSOCKADDR remote); 78 | 79 | template 80 | static 81 | VOID 82 | OvpnSocketCopyRemoteToSockaddr(T& remote, SOCKADDR_STORAGE* sockaddr) { 83 | // Copy the appropriate address based on the family 84 | if (remote.IPv4.sin_family == AF_INET) { 85 | RtlCopyMemory(sockaddr, &remote.IPv4, sizeof(SOCKADDR_IN)); 86 | } 87 | else if (remote.IPv6.sin6_family == AF_INET6) { 88 | RtlCopyMemory(sockaddr, &remote.IPv6, sizeof(SOCKADDR_IN6)); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /timer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * ovpn-dco-win OpenVPN protocol accelerator for Windows 3 | * 4 | * Copyright (C) 2020-2021 OpenVPN Inc 5 | * 6 | * Author: Lev Stipakov 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License version 2 10 | * as published by the Free Software Foundation. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along 18 | * with this program; if not, write to the Free Software Foundation, Inc., 19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | */ 21 | 22 | // without this include, RTL_GENERIC_TABLE in Device.h is undefined 23 | #include 24 | 25 | #include "bufferpool.h" 26 | #include "driver.h" 27 | #include "trace.h" 28 | #include "timer.h" 29 | #include "socket.h" 30 | #include "peer.h" 31 | 32 | static const UCHAR OvpnKeepaliveMessage[] = { 33 | 0x2a, 0x18, 0x7b, 0xf3, 0x64, 0x1e, 0xb4, 0xcb, 34 | 0x07, 0xed, 0x2d, 0x0a, 0x98, 0x1f, 0xc7, 0x48 35 | }; 36 | 37 | // Context added to a timer's attributes 38 | typedef struct _OVPN_PEER_TIMER_CONTEXT { 39 | OvpnPeerContext* Peer; 40 | 41 | LARGE_INTEGER lastXmit; 42 | LARGE_INTEGER lastRecv; 43 | 44 | // 0 means "not set" 45 | LONG recvTimeout; 46 | LONG xmitInterval; 47 | } OVPN_PEER_TIMER_CONTEXT, * POVPN_PEER_TIMER_CONTEXT; 48 | 49 | WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(OVPN_PEER_TIMER_CONTEXT, OvpnGetPeerTimerContext); 50 | 51 | _Use_decl_annotations_ 52 | BOOLEAN OvpnTimerIsKeepaliveMessage(const PUCHAR buf, SIZE_T len) 53 | { 54 | return RtlCompareMemory(buf, OvpnKeepaliveMessage, len) == sizeof(OvpnKeepaliveMessage); 55 | } 56 | 57 | static VOID OvpnTimerXmit(WDFTIMER timer) 58 | { 59 | POVPN_DEVICE device = OvpnGetDeviceContext(WdfTimerGetParentObject(timer)); 60 | POVPN_PEER_TIMER_CONTEXT timerCtx = OvpnGetPeerTimerContext(timer); 61 | OVPN_TX_BUFFER* buffer; 62 | NTSTATUS status; 63 | 64 | LOG_IF_NOT_NT_SUCCESS(status = OvpnTxBufferPoolGet(device->TxBufferPool, &buffer)); 65 | 66 | if (!NT_SUCCESS(status)) { 67 | LOG_EXIT(); 68 | return; 69 | } 70 | 71 | // copy keepalive magic message to the buffer 72 | RtlCopyMemory(OvpnBufferPut(buffer, sizeof(OvpnKeepaliveMessage)), OvpnKeepaliveMessage, sizeof(OvpnKeepaliveMessage)); 73 | 74 | OvpnPeerContext* peer = timerCtx->Peer; 75 | 76 | ExAcquireSpinLockSharedAtDpcLevel(&peer->SpinLock); 77 | 78 | auto peerId = peer->PeerId; 79 | SOCKADDR_STORAGE sa; 80 | OvpnSocketCopyRemoteToSockaddr(peer->TransportAddrs.Remote, &sa); 81 | 82 | OvpnCryptoContext* cryptoContext = &peer->CryptoContext; 83 | if (cryptoContext->Encrypt) { 84 | // make space to crypto overhead 85 | BOOLEAN pktId64bit = cryptoContext->CryptoOptions & CRYPTO_OPTIONS_64BIT_PKTID; 86 | BOOLEAN aeadTagEnd = cryptoContext->CryptoOptions & CRYPTO_OPTIONS_AEAD_TAG_END; 87 | 88 | OvpnTxBufferPush(buffer, OVPN_DATA_V2_LEN + (pktId64bit ? 8 : 4) + (aeadTagEnd ? 0 : AEAD_AUTH_TAG_LEN)); 89 | 90 | // in-place encrypt, always with primary key 91 | status = cryptoContext->Encrypt(&cryptoContext->Primary, buffer->Data, buffer->Len, cryptoContext->CryptoOptions); 92 | } 93 | else { 94 | status = STATUS_INVALID_DEVICE_STATE; 95 | // LOG_WARN("CryptoContext not initialized"); 96 | } 97 | ExReleaseSpinLockSharedFromDpcLevel(&peer->SpinLock); 98 | 99 | if (NT_SUCCESS(status)) { 100 | // start async send, completion handler will return ciphertext buffer to the pool 101 | LOG_IF_NOT_NT_SUCCESS(status = OvpnSocketSend(&device->Socket, buffer, (SOCKADDR*)&sa)); 102 | if (NT_SUCCESS(status)) { 103 | LOG_INFO("Ping sent", TraceLoggingValue(peerId, "peer-id")); 104 | } 105 | } 106 | else { 107 | OvpnTxBufferPoolPut(buffer); 108 | } 109 | } 110 | 111 | static BOOLEAN OvpnTimerRecv(WDFTIMER timer) 112 | { 113 | POVPN_DEVICE device = OvpnGetDeviceContext(WdfTimerGetParentObject(timer)); 114 | 115 | POVPN_PEER_TIMER_CONTEXT timerCtx = OvpnGetPeerTimerContext(timer); 116 | auto peerId = timerCtx->Peer->PeerId; 117 | LOG_INFO("Keepalive timeout", TraceLoggingValue(peerId, "peer-id")); 118 | 119 | WDFREQUEST request; 120 | NTSTATUS status = STATUS_SUCCESS; 121 | 122 | if (device->Mode == OVPN_MODE_P2P) { 123 | status = WdfIoQueueRetrieveNextRequest(device->PendingReadsQueue, &request); 124 | if (!NT_SUCCESS(status)) { 125 | LOG_INFO("No pending request for keepalive timeout notification"); 126 | return FALSE; 127 | } 128 | 129 | ULONG_PTR bytesSent = 0; 130 | WdfRequestCompleteWithInformation(request, STATUS_CONNECTION_DISCONNECTED, bytesSent); 131 | } 132 | else { 133 | (VOID)OvpnPeerDelete(device, peerId, OVPN_DEL_PEER_REASON_EXPIRED, TRUE); 134 | } 135 | 136 | return NT_SUCCESS(status); 137 | } 138 | 139 | _Use_decl_annotations_ 140 | VOID OvpnTimerDestroy(WDFTIMER* timer) 141 | { 142 | if (*timer != WDF_NO_HANDLE) { 143 | WdfTimerStop(*timer, FALSE); 144 | WdfObjectDelete(*timer); 145 | 146 | *timer = WDF_NO_HANDLE; 147 | } 148 | } 149 | 150 | _Function_class_(EVT_WDF_TIMER) 151 | static VOID OvpnTimerTick(WDFTIMER timer) 152 | { 153 | LARGE_INTEGER now; 154 | KeQuerySystemTime(&now); 155 | 156 | POVPN_PEER_TIMER_CONTEXT timerCtx = OvpnGetPeerTimerContext(timer); 157 | 158 | if ((timerCtx->xmitInterval > 0) && (((now.QuadPart - timerCtx->lastXmit.QuadPart) / WDF_TIMEOUT_TO_SEC) > timerCtx->xmitInterval)) 159 | { 160 | OvpnTimerXmit(timer); 161 | timerCtx->lastXmit = now; 162 | } 163 | 164 | if ((timerCtx->recvTimeout > 0) && (((now.QuadPart - timerCtx->lastRecv.QuadPart) / WDF_TIMEOUT_TO_SEC) > timerCtx->recvTimeout)) 165 | { 166 | // have we have completed pending read request? 167 | if (OvpnTimerRecv(timer)) 168 | { 169 | timerCtx->recvTimeout = 0; // one-off timer 170 | } 171 | } 172 | } 173 | 174 | _Use_decl_annotations_ 175 | NTSTATUS OvpnTimerCreate(WDFOBJECT parent, OvpnPeerContext* peer, _Inout_ WDFTIMER* timer) 176 | { 177 | LOG_ENTER(); 178 | 179 | if (*timer != WDF_NO_HANDLE) { 180 | WdfTimerStop(*timer, FALSE); 181 | WdfObjectDelete(*timer); 182 | 183 | *timer = WDF_NO_HANDLE; 184 | } 185 | 186 | WDF_TIMER_CONFIG timerConfig; 187 | WDF_TIMER_CONFIG_INIT(&timerConfig, OvpnTimerTick); 188 | timerConfig.TolerableDelay = TolerableDelayUnlimited; 189 | timerConfig.Period = 1000; 190 | 191 | WDF_OBJECT_ATTRIBUTES timerAttributes; 192 | WDF_OBJECT_ATTRIBUTES_INIT(&timerAttributes); 193 | WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&timerAttributes, OVPN_PEER_TIMER_CONTEXT); 194 | timerAttributes.ParentObject = parent; 195 | 196 | *timer = WDF_NO_HANDLE; 197 | NTSTATUS status; 198 | LOG_IF_NOT_NT_SUCCESS(status = WdfTimerCreate(&timerConfig, &timerAttributes, timer)); 199 | if (NT_SUCCESS(status)) { 200 | POVPN_PEER_TIMER_CONTEXT pTimerContext = OvpnGetPeerTimerContext(*timer); 201 | pTimerContext->Peer = peer; 202 | WdfTimerStart(*timer, WDF_REL_TIMEOUT_IN_SEC(1)); 203 | } 204 | 205 | LOG_EXIT(); 206 | return status; 207 | } 208 | 209 | #define CHECK_TIMER_HANDLE(timer) \ 210 | do { \ 211 | if ((timer) == WDF_NO_HANDLE) { \ 212 | LOG_ERROR("Timer handle is not initialized"); \ 213 | return; \ 214 | } \ 215 | } while (0) 216 | 217 | VOID OvpnTimerSetXmitInterval(WDFTIMER timer, LONG xmitInterval) 218 | { 219 | CHECK_TIMER_HANDLE(timer); 220 | 221 | POVPN_PEER_TIMER_CONTEXT timerCtx = OvpnGetPeerTimerContext(timer); 222 | timerCtx->xmitInterval = xmitInterval; 223 | KeQuerySystemTime(&timerCtx->lastXmit); 224 | } 225 | 226 | VOID OvpnTimerSetRecvTimeout(WDFTIMER timer, LONG recvTimeout) 227 | { 228 | CHECK_TIMER_HANDLE(timer); 229 | 230 | POVPN_PEER_TIMER_CONTEXT timerCtx = OvpnGetPeerTimerContext(timer); 231 | timerCtx->recvTimeout = recvTimeout; 232 | KeQuerySystemTime(&timerCtx->lastRecv); 233 | } 234 | 235 | VOID OvpnTimerResetXmit(WDFTIMER timer) 236 | { 237 | CHECK_TIMER_HANDLE(timer); 238 | 239 | POVPN_PEER_TIMER_CONTEXT timerCtx = OvpnGetPeerTimerContext(timer); 240 | KeQuerySystemTime(&timerCtx->lastXmit); 241 | } 242 | 243 | VOID OvpnTimerResetRecv(WDFTIMER timer) 244 | { 245 | CHECK_TIMER_HANDLE(timer); 246 | 247 | POVPN_PEER_TIMER_CONTEXT timerCtx = OvpnGetPeerTimerContext(timer); 248 | KeQuerySystemTime(&timerCtx->lastRecv); 249 | } 250 | -------------------------------------------------------------------------------- /timer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ovpn-dco-win OpenVPN protocol accelerator for Windows 3 | * 4 | * Copyright (C) 2020-2021 OpenVPN Inc 5 | * Copyright (C) 2023 Rubicon Communications LLC (Netgate) 6 | * 7 | * Author: Lev Stipakov 8 | * 9 | * This program is free software; you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License version 2 11 | * as published by the Free Software Foundation. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License along 19 | * with this program; if not, write to the Free Software Foundation, Inc., 20 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | */ 22 | 23 | #pragma once 24 | 25 | #include 26 | #include 27 | 28 | #include "peer.h" 29 | 30 | VOID 31 | OvpnTimerResetXmit(WDFTIMER timer); 32 | 33 | VOID 34 | OvpnTimerResetRecv(WDFTIMER timer); 35 | 36 | _Must_inspect_result_ 37 | NTSTATUS 38 | OvpnTimerCreate(WDFOBJECT parent, OvpnPeerContext* peer, _Inout_ WDFTIMER* timer); 39 | 40 | VOID 41 | OvpnTimerSetXmitInterval(WDFTIMER timer, LONG xmitInterval); 42 | 43 | VOID 44 | OvpnTimerSetRecvTimeout(WDFTIMER timer, LONG recvTimeout); 45 | 46 | VOID 47 | OvpnTimerDestroy(_Inout_ WDFTIMER* timer); 48 | 49 | _Must_inspect_result_ 50 | BOOLEAN 51 | OvpnTimerIsKeepaliveMessage(_In_reads_(len) const PUCHAR buf, SIZE_T len); 52 | -------------------------------------------------------------------------------- /trace.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ovpn-dco-win OpenVPN protocol accelerator for Windows 3 | * 4 | * Copyright (C) 2020-2021 OpenVPN Inc 5 | * 6 | * Author: Lev Stipakov 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License version 2 10 | * as published by the Free Software Foundation. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along 18 | * with this program; if not, write to the Free Software Foundation, Inc., 19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | */ 21 | 22 | #pragma once 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | 31 | TRACELOGGING_DECLARE_PROVIDER(g_hOvpnEtwProvider); 32 | 33 | #define TraceLoggingFunctionName() TraceLoggingWideString(__FUNCTIONW__, "Func") 34 | 35 | #define LOG_NTSTATUS(Status, ...) do {\ 36 | TraceLoggingWrite( \ 37 | g_hOvpnEtwProvider, \ 38 | "Status", \ 39 | TraceLoggingLevel(TRACE_LEVEL_ERROR), \ 40 | TraceLoggingFunctionName(), \ 41 | TraceLoggingUInt32(__LINE__, "Line"), \ 42 | TraceLoggingNTStatus(Status, "Status"), \ 43 | __VA_ARGS__); \ 44 | } while (0,0) 45 | 46 | #define LOG_ERROR(Error, ...) do {\ 47 | TraceLoggingWrite( \ 48 | g_hOvpnEtwProvider, \ 49 | "Error", \ 50 | TraceLoggingLevel(TRACE_LEVEL_ERROR), \ 51 | TraceLoggingFunctionName(), \ 52 | TraceLoggingUInt32(__LINE__, "Line"), \ 53 | TraceLoggingValue(Error, "Msg"), \ 54 | __VA_ARGS__); \ 55 | } while (0,0) 56 | 57 | #define LOG_WARN(Info, ...) do {\ 58 | TraceLoggingWrite( \ 59 | g_hOvpnEtwProvider, \ 60 | "Warn", \ 61 | TraceLoggingLevel(TRACE_LEVEL_WARNING), \ 62 | TraceLoggingFunctionName(), \ 63 | TraceLoggingUInt32(__LINE__, "Line"), \ 64 | TraceLoggingValue(Info, "Msg"), \ 65 | __VA_ARGS__); \ 66 | } while (0,0) 67 | 68 | #define LOG_ENTER(...) do {\ 69 | TraceLoggingWrite( \ 70 | g_hOvpnEtwProvider, \ 71 | "FunctionEntry", \ 72 | TraceLoggingLevel(TRACE_LEVEL_VERBOSE), \ 73 | TraceLoggingFunctionName(), \ 74 | TraceLoggingUInt32(__LINE__, "Line"), \ 75 | __VA_ARGS__); \ 76 | } while (0,0) 77 | 78 | #define LOG_EXIT(...) do {\ 79 | TraceLoggingWrite( \ 80 | g_hOvpnEtwProvider, \ 81 | "FunctionExit", \ 82 | TraceLoggingLevel(TRACE_LEVEL_VERBOSE), \ 83 | TraceLoggingFunctionName(), \ 84 | TraceLoggingUInt32(__LINE__, "Line"), \ 85 | __VA_ARGS__); \ 86 | } while (0,0) 87 | 88 | #define LOG_INFO(Info, ...) do {\ 89 | TraceLoggingWrite( \ 90 | g_hOvpnEtwProvider, \ 91 | "Info", \ 92 | TraceLoggingLevel(TRACE_LEVEL_INFORMATION), \ 93 | TraceLoggingFunctionName(), \ 94 | TraceLoggingUInt32(__LINE__, "Line"), \ 95 | TraceLoggingValue(Info, "Msg"), \ 96 | __VA_ARGS__); \ 97 | } while (0,0) 98 | 99 | #define LOG_IF_NOT_NT_SUCCESS(Expression, ...) do {\ 100 | NTSTATUS p_status = (Expression); \ 101 | if (!NT_SUCCESS(p_status)) \ 102 | { \ 103 | LOG_NTSTATUS(p_status, \ 104 | TraceLoggingWideString(L#Expression, "Expression"), \ 105 | __VA_ARGS__); \ 106 | } \ 107 | } while(0,0) 108 | 109 | #define GOTO_IF_NOT_NT_SUCCESS(Label, StatusLValue, Expression, ...) do {\ 110 | StatusLValue = (Expression); \ 111 | if (!NT_SUCCESS(StatusLValue)) \ 112 | { \ 113 | LOG_NTSTATUS(StatusLValue, \ 114 | TraceLoggingWideString(L#Expression, "Expression"), \ 115 | __VA_ARGS__); \ 116 | goto Label; \ 117 | } \ 118 | } while(0,0) 119 | 120 | #ifndef TraceLoggingIPv4Address 121 | #define TraceLoggingIPv4Address(value, ...) _tlgArgScalarVal(UINT32, value, TlgInUINT32, (TlgOutIPV4), __VA_ARGS__) 122 | #endif 123 | 124 | #ifndef TraceLoggingIPv6Address 125 | #define TraceLoggingIPv6Address(pValue, ...) _tlgArgBinary(void, pValue, 16u, TlgInBINARY, (TlgOutIPV6), __VA_ARGS__) 126 | #endif -------------------------------------------------------------------------------- /trie.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * ovpn-dco-win OpenVPN protocol accelerator for Windows 3 | * 4 | * Copyright (C) 2024- OpenVPN Inc 5 | * 6 | * Author: Lev Stipakov 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License version 2 10 | * as published by the Free Software Foundation. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along 18 | * with this program; if not, write to the Free Software Foundation, Inc., 19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | */ 21 | 22 | #include "trie.h" 23 | 24 | #include "trace.h" 25 | #include "peer.h" 26 | 27 | class IPTrie::TrieNode { 28 | public: 29 | TrieNode* children[2]; // Two branches: 0 and 1 30 | OvpnPeerContext* peer; // Associated peer context (if this is a valid prefix) 31 | bool isRoute; // Marks if this node represents a valid route 32 | 33 | TrieNode() : peer(nullptr), isRoute(false) { 34 | children[0] = nullptr; 35 | children[1] = nullptr; 36 | } 37 | 38 | static TrieNode* AllocateNode() { 39 | TrieNode* node = (TrieNode*)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(TrieNode), 'ovpn'); 40 | if (node) { 41 | RtlZeroMemory(node, sizeof(TrieNode)); 42 | } 43 | return node; 44 | } 45 | 46 | static void FreeNode(TrieNode* node) { 47 | if (node) { 48 | ExFreePoolWithTag(node, 'ovpn'); 49 | } 50 | } 51 | }; 52 | 53 | VOID 54 | IPTrie::Init(BOOLEAN isIPv6) { 55 | maxBits = isIPv6 ? 128 : 32; 56 | root = nullptr; 57 | } 58 | 59 | VOID 60 | IPTrie::FreeTrie(TrieNode* node) { 61 | if (!node) return; 62 | if (node->children[0]) FreeTrie(node->children[0]); 63 | if (node->children[1]) FreeTrie(node->children[1]); 64 | TrieNode::FreeNode(node); 65 | } 66 | 67 | VOID 68 | IPTrie::Cleanup() { 69 | LOG_ENTER(); 70 | 71 | KIRQL oldIrql; 72 | LIST_ENTRY cleanupList; 73 | InitializeListHead(&cleanupList); 74 | 75 | oldIrql = ExAcquireSpinLockExclusive(&Lock); 76 | 77 | if (root) { 78 | CleanupNode(root, &cleanupList); 79 | root = nullptr; // Reset the root after cleaning up 80 | } 81 | 82 | ExReleaseSpinLockExclusive(&Lock, oldIrql); 83 | 84 | // Perform deferred cleanup of peers outside the lock 85 | PLIST_ENTRY entry; 86 | while (!IsListEmpty(&cleanupList)) { 87 | entry = RemoveHeadList(&cleanupList); 88 | OvpnPeerContext* peer = CONTAINING_RECORD(entry, OvpnPeerContext, ListEntry); 89 | OvpnPeerCtxRelease(peer); 90 | } 91 | 92 | LOG_EXIT(); 93 | } 94 | 95 | VOID IPTrie::CleanupNode(TrieNode* node, PLIST_ENTRY cleanupList) { 96 | if (!node) return; 97 | 98 | CleanupNode(node->children[0], cleanupList); 99 | CleanupNode(node->children[1], cleanupList); 100 | 101 | if (node->peer) { 102 | InsertTailList(cleanupList, &node->peer->ListEntry); 103 | node->peer = nullptr; 104 | } 105 | 106 | TrieNode::FreeNode(node); 107 | } 108 | 109 | NTSTATUS 110 | IPTrie::Insert(const UCHAR* ip, int prefixLength, OvpnPeerContext* peer) { 111 | LOG_ENTER(); 112 | 113 | NTSTATUS status = STATUS_SUCCESS; 114 | 115 | KIRQL kirql = ExAcquireSpinLockExclusive(&Lock); 116 | 117 | if (!root) { 118 | root = TrieNode::AllocateNode(); 119 | if (!root) { 120 | ExReleaseSpinLockExclusive(&Lock, kirql); 121 | status = STATUS_INSUFFICIENT_RESOURCES; 122 | goto done; 123 | } 124 | } 125 | 126 | TrieNode* current = root; 127 | for (int i = 0; i < prefixLength && i < maxBits; ++i) { 128 | int bit = (ip[i / 8] >> (7 - (i % 8))) & 1; 129 | if (!current->children[bit]) { 130 | current->children[bit] = TrieNode::AllocateNode(); 131 | if (!current->children[bit]) { 132 | ExReleaseSpinLockExclusive(&Lock, kirql); 133 | status = STATUS_INSUFFICIENT_RESOURCES; 134 | goto done; 135 | } 136 | } 137 | current = current->children[bit]; 138 | } 139 | 140 | // update the node with the peer info 141 | current->peer = peer; 142 | current->isRoute = true; 143 | 144 | // increment peer refcnt since it has been stored in a trie 145 | InterlockedIncrement(&peer->RefCounter); 146 | 147 | ExReleaseSpinLockExclusive(&Lock, kirql); 148 | 149 | done: 150 | LOG_EXIT(); 151 | return status; 152 | } 153 | 154 | OvpnPeerContext* 155 | IPTrie::Find(const UCHAR* ip) { 156 | if (!root) return nullptr; 157 | 158 | KIRQL kirql = ExAcquireSpinLockShared(&Lock); 159 | 160 | TrieNode* current = root; 161 | OvpnPeerContext* peer = nullptr; 162 | for (int i = 0; i < maxBits && current; ++i) { 163 | if (current->isRoute) { 164 | peer = current->peer; 165 | } 166 | // Calculate the next bit of the IP address 167 | int bit = (ip[i / 8] >> (7 - (i % 8))) & 1; 168 | current = current->children[bit]; 169 | } 170 | 171 | if (current && current->isRoute) { 172 | peer = current->peer; 173 | } 174 | 175 | ExReleaseSpinLockShared(&Lock, kirql); 176 | 177 | // before returning the peer, increment refcnt 178 | if (peer) { 179 | InterlockedIncrement(&peer->RefCounter); 180 | } 181 | 182 | return peer; 183 | } 184 | 185 | VOID 186 | IPTrie::RemoveByPeerId(INT32 peerId) { 187 | LOG_ENTER(); 188 | 189 | LIST_ENTRY cleanupList; 190 | InitializeListHead(&cleanupList); 191 | 192 | // collect nodes to be deleted into the list 193 | KIRQL oldIrql = ExAcquireSpinLockExclusive(&Lock); 194 | root = RemoveByPeerId(root, peerId, &cleanupList); 195 | ExReleaseSpinLockExclusive(&Lock, oldIrql); 196 | 197 | // perform cleanup outside of the lock 198 | PLIST_ENTRY entry; 199 | while (!IsListEmpty(&cleanupList)) { 200 | entry = RemoveHeadList(&cleanupList); 201 | OvpnPeerContext* peer = CONTAINING_RECORD(entry, OvpnPeerContext, ListEntry); 202 | OvpnPeerCtxRelease(peer); 203 | } 204 | 205 | LOG_EXIT(); 206 | } 207 | 208 | IPTrie::TrieNode* 209 | IPTrie::RemoveByPeerId(TrieNode* node, INT32 peerId, PLIST_ENTRY cleanupList) { 210 | if (!node) return nullptr; 211 | 212 | // Recursively process left and right children 213 | node->children[0] = RemoveByPeerId(node->children[0], peerId, cleanupList); 214 | node->children[1] = RemoveByPeerId(node->children[1], peerId, cleanupList); 215 | 216 | // Check if this node's peer matches the target peerId 217 | if (node->peer && node->peer->PeerId == peerId) { 218 | 219 | // if we're last to hold a reference, defer the cleanup by adding the peer to the cleanup list 220 | if (InterlockedDecrement(&node->peer->RefCounter) == 0) { 221 | InsertTailList(cleanupList, &node->peer->ListEntry); 222 | } 223 | 224 | node->peer = nullptr; 225 | node->isRoute = false; 226 | } 227 | 228 | // If this node has no children and is no longer a route, delete it 229 | if (!node->children[0] && !node->children[1] && !node->isRoute) { 230 | TrieNode::FreeNode(node); 231 | return nullptr; 232 | } 233 | 234 | return node; 235 | } 236 | 237 | NTSTATUS 238 | IPTrie::Remove(const UCHAR* ip, int prefixLength) { 239 | if (prefixLength < 0 || prefixLength > maxBits) { 240 | return STATUS_INVALID_PARAMETER; 241 | } 242 | 243 | OvpnPeerContext* peerToRelease = nullptr; 244 | 245 | KIRQL oldIrql = ExAcquireSpinLockExclusive(&Lock); 246 | root = RemoveRouteNode(root, ip, prefixLength, &peerToRelease); 247 | ExReleaseSpinLockExclusive(&Lock, oldIrql); 248 | 249 | // Release the peer outside the lock if needed 250 | if (peerToRelease) { 251 | OvpnPeerCtxRelease(peerToRelease); 252 | } 253 | 254 | return STATUS_SUCCESS; 255 | } 256 | 257 | IPTrie::TrieNode* 258 | IPTrie::RemoveRouteNode(TrieNode* node, const UCHAR* ip, int prefixLength, OvpnPeerContext** peerToRelease) { 259 | if (!node) return nullptr; 260 | 261 | if (prefixLength > 0) { 262 | int bit = (ip[(maxBits - prefixLength) / 8] >> (7 - ((maxBits - prefixLength) % 8))) & 1; 263 | node->children[bit] = RemoveRouteNode(node->children[bit], ip, prefixLength - 1, peerToRelease); 264 | } 265 | else { 266 | if (node->peer) { 267 | *peerToRelease = node->peer; 268 | node->peer = nullptr; 269 | } 270 | node->isRoute = false; 271 | } 272 | 273 | // Cleanup: If the node has no children and is not a route, delete it 274 | if (!node->children[0] && !node->children[1] && !node->isRoute) { 275 | TrieNode::FreeNode(node); 276 | return nullptr; 277 | } 278 | 279 | return node; 280 | } 281 | -------------------------------------------------------------------------------- /trie.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ovpn-dco-win OpenVPN protocol accelerator for Windows 3 | * 4 | * Copyright (C) 2024- OpenVPN Inc 5 | * 6 | * Author: Lev Stipakov 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License version 2 10 | * as published by the Free Software Foundation. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along 18 | * with this program; if not, write to the Free Software Foundation, Inc., 19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | */ 21 | 22 | #pragma once 23 | 24 | #include 25 | 26 | struct OvpnPeerContext; 27 | 28 | // IPTrie interface 29 | class IPTrie { 30 | public: 31 | IPTrie() = delete; 32 | 33 | // Initialize the trie 34 | VOID Init(BOOLEAN isIPv6); 35 | 36 | // Insert a route into the trie 37 | NTSTATUS Insert(const UCHAR* ip, int prefixLength, OvpnPeerContext* peerContext); 38 | 39 | // Remove a route from the trie 40 | NTSTATUS Remove(const UCHAR* ip, int prefixLength); 41 | 42 | // Find the best match for a given IP address 43 | OvpnPeerContext* Find(const UCHAR* ip); 44 | 45 | // Remove all nodes associated with a specific peer-id 46 | VOID RemoveByPeerId(INT32 peerId); 47 | 48 | // Clean up the trie (explicitly called by the user) 49 | VOID Cleanup(); 50 | 51 | private: 52 | class TrieNode; // Forward declaration of TrieNode 53 | TrieNode* root; // Root of the trie 54 | int maxBits; // Maximum number of bits (32 for IPv4, 128 for IPv6) 55 | 56 | EX_SPIN_LOCK Lock = 0; // Lock for shared/exclusive access 57 | 58 | TrieNode* RemoveByPeerId(TrieNode* node, INT32 peerId, PLIST_ENTRY cleanupList); 59 | TrieNode* RemoveRouteNode(TrieNode* node, const UCHAR* ip, int prefixLength, OvpnPeerContext** peerToRelease); 60 | 61 | VOID FreeTrie(TrieNode* node); 62 | VOID CleanupNode(TrieNode* node, PLIST_ENTRY cleanupList); 63 | }; -------------------------------------------------------------------------------- /txqueue.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ovpn-dco-win OpenVPN protocol accelerator for Windows 3 | * 4 | * Copyright (C) 2020-2021 OpenVPN Inc 5 | * 6 | * Author: Lev Stipakov 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License version 2 10 | * as published by the Free Software Foundation. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along 18 | * with this program; if not, write to the Free Software Foundation, Inc., 19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | */ 21 | 22 | #pragma once 23 | 24 | #include "adapter.h" 25 | 26 | typedef struct _OVPN_TXQUEUE 27 | { 28 | POVPN_ADAPTER Adapter; 29 | 30 | NET_RING_COLLECTION const * Rings; 31 | 32 | NET_EXTENSION VirtualAddressExtension; 33 | } OVPN_TXQUEUE, * POVPN_TXQUEUE; 34 | 35 | WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(OVPN_TXQUEUE, OvpnGetTxQueueContext); 36 | 37 | EVT_PACKET_QUEUE_SET_NOTIFICATION_ENABLED OvpnEvtTxQueueSetNotificationEnabled; 38 | EVT_PACKET_QUEUE_ADVANCE OvpnEvtTxQueueAdvance; 39 | EVT_PACKET_QUEUE_CANCEL OvpnEvtTxQueueCancel; 40 | 41 | VOID 42 | OvpnTxQueueInitialize(NETPACKETQUEUE txQueue, _In_ POVPN_ADAPTER adapter); 43 | -------------------------------------------------------------------------------- /uapi/ovpn-dco.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ovpn-dco-win OpenVPN protocol accelerator for Windows 3 | * 4 | * Copyright (C) 2020-2021 OpenVPN Inc 5 | * 6 | * Author: Lev Stipakov 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License version 2 10 | * as published by the Free Software Foundation. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along 18 | * with this program; if not, write to the Free Software Foundation, Inc., 19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * This particular file (uapi.h) is also licensed using the MIT license (see COPYRIGHT.MIT). 22 | */ 23 | 24 | #pragma once 25 | #ifndef _KERNEL_MODE 26 | #include 27 | #endif 28 | #include 29 | #include 30 | 31 | typedef enum { 32 | OVPN_PROTO_UDP, 33 | OVPN_PROTO_TCP 34 | } OVPN_PROTO; 35 | 36 | typedef struct _OVPN_NEW_PEER { 37 | union { 38 | SOCKADDR_IN Addr4; 39 | SOCKADDR_IN6 Addr6; 40 | } Local; 41 | 42 | union { 43 | SOCKADDR_IN Addr4; 44 | SOCKADDR_IN6 Addr6; 45 | } Remote; 46 | 47 | OVPN_PROTO Proto; 48 | } OVPN_NEW_PEER, * POVPN_NEW_PEER; 49 | 50 | typedef struct _OVPN_MP_NEW_PEER { 51 | union { 52 | SOCKADDR_IN Addr4; 53 | SOCKADDR_IN6 Addr6; 54 | } Local; 55 | 56 | union { 57 | SOCKADDR_IN Addr4; 58 | SOCKADDR_IN6 Addr6; 59 | } Remote; 60 | 61 | IN_ADDR VpnAddr4; 62 | IN6_ADDR VpnAddr6; 63 | 64 | int PeerId; 65 | } OVPN_MP_NEW_PEER, * POVPN_MP_NEW_PEER; 66 | 67 | typedef struct _OVPN_STATS { 68 | LONG LostInControlPackets; 69 | LONG LostOutControlPackets; 70 | 71 | LONG LostInDataPackets; 72 | LONG LostOutDataPackets; 73 | 74 | LONG ReceivedDataPackets; 75 | LONG ReceivedControlPackets; 76 | 77 | LONG SentControlPackets; 78 | LONG SentDataPackets; 79 | 80 | LONG64 TransportBytesSent; 81 | LONG64 TransportBytesReceived; 82 | 83 | LONG64 TunBytesSent; 84 | LONG64 TunBytesReceived; 85 | } OVPN_STATS, * POVPN_STATS; 86 | 87 | typedef enum _OVPN_KEY_SLOT { 88 | OVPN_KEY_SLOT_PRIMARY, 89 | OVPN_KEY_SLOT_SECONDARY 90 | } OVPN_KEY_SLOT; 91 | 92 | typedef enum _OVPN_CIPHER_ALG { 93 | OVPN_CIPHER_ALG_NONE, 94 | OVPN_CIPHER_ALG_AES_GCM, 95 | OVPN_CIPHER_ALG_CHACHA20_POLY1305 96 | } OVPN_CIPHER_ALG; 97 | 98 | typedef struct _OVPN_KEY_DIRECTION 99 | { 100 | unsigned char Key[32]; 101 | unsigned char KeyLen; // 16/24/32 -> AES-128-GCM/AES-192-GCM/AES-256-GCM 102 | unsigned char NonceTail[8]; 103 | } OVPN_KEY_DIRECTION; 104 | 105 | typedef struct _OVPN_CRYPTO_DATA { 106 | OVPN_KEY_DIRECTION Encrypt; 107 | OVPN_KEY_DIRECTION Decrypt; 108 | OVPN_KEY_SLOT KeySlot; 109 | OVPN_CIPHER_ALG CipherAlg; 110 | unsigned char KeyId; 111 | int PeerId; 112 | } OVPN_CRYPTO_DATA, * POVPN_CRYPTO_DATA; 113 | 114 | #define CRYPTO_OPTIONS_AEAD_TAG_END (1<<1) 115 | #define CRYPTO_OPTIONS_64BIT_PKTID (1<<2) 116 | 117 | typedef struct _OVPN_CRYPTO_DATA_V2 { 118 | OVPN_CRYPTO_DATA V1; 119 | UINT32 CryptoOptions; 120 | } OVPN_CRYPTO_DATA_V2, * POVPN_CRYPTO_DATA_V2; 121 | 122 | typedef struct _OVPN_MP_SET_PEER { 123 | int PeerId; 124 | LONG KeepaliveInterval; 125 | LONG KeepaliveTimeout; 126 | LONG MSS; 127 | } OVPN_MP_SET_PEER, * POVPN_MP_SET_PEER; 128 | 129 | typedef struct _OVPN_SET_PEER { 130 | LONG KeepaliveInterval; 131 | LONG KeepaliveTimeout; 132 | LONG MSS; 133 | } OVPN_SET_PEER, * POVPN_SET_PEER; 134 | 135 | typedef struct _OVPN_VERSION { 136 | LONG Major; 137 | LONG Minor; 138 | LONG Patch; 139 | } OVPN_VERSION, * POVPN_VERSION; 140 | 141 | typedef enum { 142 | OVPN_MODE_P2P, 143 | OVPN_MODE_MP 144 | } OVPN_MODE; 145 | 146 | typedef struct _OVPN_SET_MODE { 147 | OVPN_MODE Mode; 148 | } OVPN_SET_MODE, * POVPN_SET_MODE; 149 | 150 | typedef struct _OVPN_MP_START_VPN { 151 | union { 152 | SOCKADDR_IN Addr4; 153 | SOCKADDR_IN6 Addr6; 154 | } ListenAddress; 155 | int IPv6Only; 156 | } OVPN_MP_START_VPN, * POVPN_MP_START_VPN; 157 | 158 | typedef enum { 159 | OVPN_CMD_DEL_PEER, 160 | OVPN_CMD_SWAP_KEYS 161 | } OVPN_NOTIFY_CMD; 162 | 163 | typedef enum { 164 | OVPN_DEL_PEER_REASON_TEARDOWN, 165 | OVPN_DEL_PEER_REASON_USERSPACE, 166 | OVPN_DEL_PEER_REASON_EXPIRED, 167 | OVPN_DEL_PEER_REASON_TRANSPORT_ERROR, 168 | OVPN_DEL_PEER_REASON_TRANSPORT_DISCONNECT 169 | } OVPN_DEL_PEER_REASON; 170 | 171 | typedef struct _OVPN_NOTIFY_EVENT { 172 | OVPN_NOTIFY_CMD Cmd; 173 | int PeerId; 174 | OVPN_DEL_PEER_REASON DelPeerReason; 175 | } OVPN_NOTIFY_EVENT, * POVPN_NOTIFY_EVENT; 176 | 177 | typedef struct _OVPN_MP_DEL_PEER { 178 | int PeerId; 179 | } OVPN_MP_DEL_PEER, * POVPN_MP_DEL_PEER; 180 | 181 | typedef struct _OVPN_MP_SWAP_KEYS { 182 | int PeerId; 183 | } OVPN_MP_SWAP_KEYS, * POVPN_MP_SWAP_KEYS; 184 | 185 | typedef struct _OVPN_MP_IROUTE { 186 | union { 187 | IN_ADDR Addr4; 188 | IN6_ADDR Addr6; 189 | } Addr; 190 | int Netbits; 191 | int PeerId; 192 | int IPv6; 193 | } OVPN_MP_IROUTE, * POVPN_MP_IROUTE; 194 | 195 | #define OVPN_IOCTL_NEW_PEER CTL_CODE(FILE_DEVICE_UNKNOWN, 1, METHOD_BUFFERED, FILE_ANY_ACCESS) 196 | #define OVPN_IOCTL_GET_STATS CTL_CODE(FILE_DEVICE_UNKNOWN, 2, METHOD_BUFFERED, FILE_ANY_ACCESS) 197 | #define OVPN_IOCTL_NEW_KEY CTL_CODE(FILE_DEVICE_UNKNOWN, 3, METHOD_BUFFERED, FILE_ANY_ACCESS) 198 | #define OVPN_IOCTL_SWAP_KEYS CTL_CODE(FILE_DEVICE_UNKNOWN, 4, METHOD_BUFFERED, FILE_ANY_ACCESS) 199 | #define OVPN_IOCTL_SET_PEER CTL_CODE(FILE_DEVICE_UNKNOWN, 5, METHOD_BUFFERED, FILE_ANY_ACCESS) 200 | #define OVPN_IOCTL_START_VPN CTL_CODE(FILE_DEVICE_UNKNOWN, 6, METHOD_BUFFERED, FILE_ANY_ACCESS) 201 | #define OVPN_IOCTL_DEL_PEER CTL_CODE(FILE_DEVICE_UNKNOWN, 7, METHOD_BUFFERED, FILE_ANY_ACCESS) 202 | #define OVPN_IOCTL_GET_VERSION CTL_CODE(FILE_DEVICE_UNKNOWN, 8, METHOD_BUFFERED, FILE_ANY_ACCESS) 203 | #define OVPN_IOCTL_NEW_KEY_V2 CTL_CODE(FILE_DEVICE_UNKNOWN, 9, METHOD_BUFFERED, FILE_ANY_ACCESS) 204 | #define OVPN_IOCTL_SET_MODE CTL_CODE(FILE_DEVICE_UNKNOWN, 10, METHOD_BUFFERED, FILE_ANY_ACCESS) 205 | 206 | #define OVPN_IOCTL_MP_START_VPN CTL_CODE(FILE_DEVICE_UNKNOWN, 11, METHOD_BUFFERED, FILE_ANY_ACCESS) 207 | #define OVPN_IOCTL_MP_NEW_PEER CTL_CODE(FILE_DEVICE_UNKNOWN, 12, METHOD_BUFFERED, FILE_ANY_ACCESS) 208 | #define OVPN_IOCTL_MP_SET_PEER CTL_CODE(FILE_DEVICE_UNKNOWN, 13, METHOD_BUFFERED, FILE_ANY_ACCESS) 209 | 210 | #define OVPN_IOCTL_NOTIFY_EVENT CTL_CODE(FILE_DEVICE_UNKNOWN, 14, METHOD_BUFFERED, FILE_ANY_ACCESS) 211 | 212 | #define OVPN_IOCTL_MP_DEL_PEER CTL_CODE(FILE_DEVICE_UNKNOWN, 15, METHOD_BUFFERED, FILE_ANY_ACCESS) 213 | #define OVPN_IOCTL_MP_SWAP_KEYS CTL_CODE(FILE_DEVICE_UNKNOWN, 16, METHOD_BUFFERED, FILE_ANY_ACCESS) 214 | 215 | #define OVPN_IOCTL_MP_ADD_IROUTE CTL_CODE(FILE_DEVICE_UNKNOWN, 17, METHOD_BUFFERED, FILE_ANY_ACCESS) 216 | #define OVPN_IOCTL_MP_DEL_IROUTE CTL_CODE(FILE_DEVICE_UNKNOWN, 18, METHOD_BUFFERED, FILE_ANY_ACCESS) 217 | --------------------------------------------------------------------------------