The OpenVPN DCO kernel module handles asynchronous crypto requests inefficiently, waiting for each request to complete before starting the next one.
An official DCO patchset has been submitted upstream with improved async request handling. However, it is currently incompatible with the existing OpenVPN userspace version.
https://lore.kernel.org/netdev/20250211-b4-ovpn-v19-0-86d5daf2a47a@openvpn.net/
To bridge this gap, I created a fork that incorporates these changes, tested on OpenWrt 22.
Feedback, tests, and PRs are welcome!
Config tweaks:
mssfix
or fragment
is not supported by DCO. In order to avoid fragmentation, you should calculate MTU/MSS:
Inner MTU = Outer MTU (WAN MTU) - 20 bytes IPv4 header (for IPv4 endpoint) or 40 bytes IPv6 header (for IPv6 endpoint) - 8 bytes UDP header - 4 bytes OP32 - 4 bytes SEQ# - 16 bytes GCM auth tag - 1 byte compress stub (if the server enables compression).
Say you have a PPPoE WAN with a typical MTU of 1492, and you connected to an OpenVPN server over IPv4, and you have the full access to the server, then the calculated inner MTU should be 1492-20-8-4-4-16=1440 bytes
On both the server and the client configs, add the two options below
allow-compression no
tun-mtu 1440
Once the correct MTU is set, you can rely on the clamp MSS option of OpenWrt firewall.
But in many cases when you cannot tweak the server config, those options should not be added, as MTU mismatch can result in large packets being dropped. Then you have to manually set MSS:
IPv4 MSS = Inner MTU - 20 bytes IPv4 header - 20 bytes TCP header
IPv6 MSS = Inner MTU - 40 bytes IPv6 header - 20 bytes TCP header
Use the same case above, without the full access to the server, we must assume it has enabled compression (1 byte overhead), so the inner MTU should be 1439 bytes, IPv4 MSS = 1399 bytes, and IPv6 MSS = 1379 bytes.
Say the virtual device is tun0
:
iptables/fw3: add the rules below to /etc/firewall.user
iptables -t mangle -A POSTROUTING -o tun0 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1399
iptables -t mangle -A PREROUTING -i tun0 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1399
ip6tables -t mangle -A POSTROUTING -o tun0 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1379
ip6tables -t mangle -A PREROUTING -i tun0 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1379
nftables/fw4: create a new file in /etc/nftables.d
, and add the rules below into the new file
chain mangle_postrouting {
type filter hook postrouting priority mangle
oifname tun0 meta nfproto ipv4 tcp flags syn / syn,rst tcp option maxseg size set 1399
oifname tun0 meta nfproto ipv6 tcp flags syn / syn,rst tcp option maxseg size set 1379
}
chain mangle_prerouting {
type filter hook prerouting priority mangle
iifname tun0 meta nfproto ipv4 tcp flags syn / syn,rst tcp option maxseg size set 1399
iifname tun0 meta nfproto ipv6 tcp flags syn / syn,rst tcp option maxseg size set 1379
}