= OpenVPN Data Channel Offload (aka OVPN-DCO) == Intro The expression ''Data Channel Offload'' refers to any technique implemented with the goal of moving the processing of data packets from the OpenVPN userspace program to a separate entity. Given that OpenVPN spends a considerable amount of time passing data packets back and forth from kernel-land to user-land, where decryption and re-routing happens, it was decided to offload the data processing directly to the kernel. As direct consequence, data packets are not required to leave the kernelspace anymore, thus boosting the performance of active VPN connections. **Antonio's speech about DCO, held at Netdev 0x16 (Lisbon, October 2022), is [[https://www.youtube.com/watch?v=ry9qNzDHaUs|online here]]** === Before DCO The picture below depicts how data packets flow on a generic OpenVPN server: [[Image(no-dco.png)]] From the application, packets enter the kernel networking stack through ''tun0'', but then they are immediately sent back to userspace, so that the OpenVPN process can decide where they are headed to and then encrypt them. At this point packets are sent back to the networking stack of the kernel and finally to the Internet. On the way back the sequence of events is the same but in the opposite direction. There are two main disadvantages with this approach: 1. data packets enter and leave the kernel twice; 1. all client traffic is handled by the single-threaded OpenVPN process. Both have been known limiting factors for OpenVPN2.x performance. Running OpenVPN on a beefy CPU has often been the answer in the past in order to push the throughput as high as possible, but due to continuously increasing line rates this approach is simply not feasible anymore. === Introducing DCO To overcome the limitations described in the section above we have developed ''ovpn-dco'', a Linux kernel module designed to work back-to-back with the OpenVPN userspace software. When a VPN connection is established, being it on a client, on a server or on a p2p instance, the userspace process will first perform the usual handshake and will then pass the data channel parameters to ovpn-dco using its ''NetLink'' interface, so that it can take over from there. At this point, data packets are all handled in kernelspace and are never sent up to the userspace process. The picture below helps to visualize the difference with to the basic scenario shown above: [[Image(dco.png)]] Context switches are therefore reduced to the minimum and packet processing can take advantage of the kernel concurrency model. The two main OpenVPN functions, crypto and routing, are now implemented in kernel using the provided API. For what concerns routing, the system routing table is directly used to understand if packets have to be re-routed directly to another peer (i.e. client-to-client mode), without the need to ask the userspace process at all. OpenVPN in userspace is still in charge of handling the control channel, where all the complex and less throughput-critical operations take place. This is considered an advantage as it allowed to keep the complexity of the ovpn-dco kernel module to the minimum and thus reduce the attack surface. This means that the TLS handshake, data channel key (re-)negotiations and parameters exchange is still performed in userspace. It should be noted that embedded devices, like small routers, will probably benefit considerably from DCO. The ovpn-dco source code for Linux is currently available at the following repository: https://github.com/OpenVPN/ovpn-dco Please note that **OpenVPN 2.6 or greater** is required in order to use ovpn-dco. === DCO on Windows A kernel module has also been developed for Windows, namely ''ovpn-dco-win''. It is a device driver implemented in kernelspace that substitutes all previous drivers used by OpenVPN (i.e. tap-windows6, wintun, etc..). Differently from the other drivers, ovpn-dco-win uses the Windows Kernel API to also implement crypto operations, thus allowing the driver to process data packets entirely in kernelspace, similarly to ovpn-dco for Linux. The main limitation of ovpn-dco-win is that it only supports client/p2p mode, while server mode is not available. This decision was made due to the fact that there is less and less demand for running OpenVPN server on Windows. The ovpn-dco-win source code is currently available at the following repository: https://github.com/OpenVPN/ovpn-dco-win === DCO on FreeBSD The FreeBSD community has shown great interest for the DCO model and so they also implemented a FreeBSD kernel module. It is expected to be a drop-in replacement for ovpn-dco on Linux, thus implementing the same feature set. The source code can be found at: https://reviews.freebsd.org/D34340 === Expected limitations Not all functionalities available in OpenVPN have been implemented in ovpn-dco(-win). The reasons for this decision are mainly: 1. avoid unneeded complexity in a critical component like a kernel module; 1. take the chance to give a clear cut with legacy features that OpenVPN has carried around for a while. In particular, this is a list (may not be complete) of features that are **not** available when using ovpn-dco: * ciphers other than AES-GCM and CHACHA20-POLY1305; * due to the above, when using ovpn-dco peers must use OpenVPN 2.4 or greater (AEAD ciphers are not supported in earlier versions); * compression or compression framing; * fragmentation; * TAP/Ethernet mode; * topologies other than ''subnet''; * traffic shaping or any other sort of data packets manipulation (system tools should be used when available). === supported features, by plattform (work in progress, so subject to change) ||= Feature =||= Windows =||= Linux =||= FreeBSD =||= Remark =|| ||TUN mode (L3)|| yes|| yes|| yes|| || ||TAP mode (L2)|| no|| no|| no|| not planned|| ||OpenVPN over UDP|| yes|| yes|| yes|| || ||OpenVPN over TCP|| yes|| yes|| no|| || ||Ciphers|| AES-GCM, ChaCha20-Poly1305 (Win11 only)|| AES-GCM, ChaCha20-Poly1305||AES-GCM, ChaCha20-Poly1305|| || ||Client, P2MP-Server?|| client|| client+server|| client+server|| || ||{{{--iroute}}} handling || -||standard routes|| ?|| || ||outside fragment (inside MTU 1500)|| yes! || handled by kernel (to be tested)|| yes (to be tested)|| || ||OpenVPN fragmentation ({{{--fragment}}})|| no|| no|| no|| maybe?|| ||{{{--mssfix}}}|| yes(?)|| no*|| no*|| desirable|| ||compression|| no|| no|| no|| not planned|| ||OCC packets in data channel|| ?|| ?|| ?|| mtu-test, others? || ||RPF check|| - (client only)|| yes|| ?|| ||{{{--inactive}}}|| no|| no|| no|||| === Using ovpn-dco If OpenVPN has DCO support compiled-in it will always try to use ovpn-dco (note that Windows builds have DCO support always enabled), unless an incompatible option has been specified. In this case OpenVPN will fall-back to another driver (i.e. tun on Linux). If using DCO is not desirable, it can be deactivated at startup by passing the {{{--disable-dco}}} option.