-
Notifications
You must be signed in to change notification settings - Fork 5
Description
In UDP mode, a received OpenVPN data packet typically looks like:
IP header || UDP header || 4-byte opcode || 4-byte seq-no || 16-byte AEAD tag || ciphertext
The data area begins right after the UDP header, as long as the outer IP header is aligned, the 4-byte opcode and subsequent fields are also aligned.
In TCP mode, because TCP is a byte stream, an OpenVPN data packet may start at any offset within the TCP payload.
For example, a TCP segment may contain:
IP header || TCP header || 2-byte length || 4-byte opcode || 4-byte seq-no || 16-byte AEAD tag || ciphertext
or, if a packet spans multiple segments:
IP header || TCP header || (tail of previous ciphertext) || 2-byte length || 4-byte opcode || 4-byte seq-no || ...
ovpn uses strparser to process the TCP stream. strparser does not copy packet data - it only clones skbs and uses skb_shared_info::frag_list for continuation when one data packet spans multiple TCP segments.
As a result:
- The 4-byte opcode and 4-byte sequence number are not guaranteed to be aligned.
- After in-place decryption, the inner IP header may also become unaligned.
This is problematic on architectures without efficient unaligned access, such as MIPS. On MIPS, unaligned memory access triggers an exception and must be fixed in the exception handler (i.e., the cost is comparable to a syscall). Because the inner IP header is accessed multiple times during packet processing, this can severely impact performance.
The previous ovpn-dco module (and userspace implementation) did not suffer from this issue, because packet data were always copied into a newly allocated skb.