anthony.wingerter

← ~/blog · #pfsense

One cipher change made my PIA VPN tunnels 4–8× faster

My OpenVPN tunnels to Private Internet Access topped out around 100 Mbps. The firewall CPU was idle. Switching from AES-256-CBC to AES-256-GCM took them to 400–845 Mbps.

· 2 min read · updated Oct 8

A couple of networks at my house route through Private Internet Access over OpenVPN, terminated on my pfSense firewalls. They worked, but they were slow: 46 to 113 Mbps down on a connection that does 930 Mbps without the VPN. I assumed that was just the price of a VPN. It wasn't. One setting change made the same tunnels four to eight times faster.

The assumption that was wrong

The obvious suspect was the firewall's CPU. OpenVPN without kernel offload is single-threaded, so encryption usually caps throughput long before the line does. So I measured it: during a speed test through the tunnel, the OpenVPN process used about 11% of one core. The firewall was nowhere near its limit.

That made it look like the bottleneck was PIA's servers, and that changing anything on my side wouldn't help. That conclusion was wrong too, and I nearly didn't bother trying.

What the tunnels were actually using

Both clients were configured with a single data cipher: AES-256-CBC with SHA256 authentication. CBC mode needs a separate HMAC pass and can't be parallelized well, and it's the legacy option. Modern OpenVPN (2.5 and later) negotiates the data cipher with the server from a list you provide, and PIA's servers support the AEAD ciphers AES-256-GCM and AES-128-GCM.

The change

In pfSense: VPN → OpenVPN → Clients → edit → Data Encryption Algorithms:

  • Allowed ciphers: AES-256-GCM, AES-128-GCM, CHACHA20-POLY1305
  • Fallback data cipher: AES-256-CBC, so the tunnel still comes up if a server doesn't support GCM

Then I checked what actually got negotiated instead of trusting the setting. With the log level raised briefly, the client logs a line like this when it connects:

Data Channel: cipher 'AES-256-GCM', peer-id: 26, compression: 'stub'

I changed one tunnel first, confirmed it negotiated GCM and passed traffic, then did the second.

The result

Download through the VPNAES-256-CBCAES-256-GCM
Same test server (Dallas)113 Mbps398 Mbps
Other servers46–92 Mbps704–845 Mbps

OpenVPN on the firewall now uses 57–89% of a core at those speeds, so it is finally doing real work. My best explanation for the old numbers is that PIA's servers handle the legacy CBC path far more slowly than GCM, so the limit was on their side, and only the cipher choice could change it.

What about DCO?

OpenVPN Data Channel Offload moves encryption into the kernel and is the next big jump. I tried it, but pfSense CE 2.8 doesn't ship the if_ovpn kernel module or the GUI support: DCO is a pfSense Plus feature. The setting was silently ignored and the tunnel kept using the regular tun device. GCM alone was enough of a win anyway.

One more PIA quirk worth knowing: their servers push comp-lzo no, so the session keeps a compression "stub" framing byte even though nothing is compressed. That is harmless for normal OpenVPN, but it's one of the things DCO refuses to handle.

Takeaways

  • Measure before blaming hardware: an idle CPU during a slow transfer tells you the bottleneck is somewhere else.
  • Don't stop at "the provider is slow". The cipher you negotiate can be what makes the provider slow.
  • Offer a list of modern AEAD ciphers with a CBC fallback, then confirm in the logs what was actually negotiated.
  • Change one tunnel at a time and test it before touching the next.