Analyzing VPN Throughput Degradation: Benchmarking File Transfer Speeds with FortiClient SSL VPN


2 views

When working with VPN connections (particularly SSL VPNs like FortiClient), throughput reduction is expected due to:

  • Encryption/decryption overhead
  • Protocol encapsulation
  • Additional routing hops
  • Potential MTU size mismatches

While the 55% reduction claim might seem high, it's not unrealistic in certain scenarios. Here's how to properly measure:

# Python script to test transfer speeds
import speedtest
import time

def vpn_speed_test():
    st = speedtest.Speedtest()
    
    print("Testing without VPN...")
    start = time.time()
    download_no_vpn = st.download() / 1_000_000  # Convert to Mbps
    elapsed_no_vpn = time.time() - start
    
    # Connect to VPN here (manual step)
    input("Connect VPN and press Enter...")
    
    print("Testing with VPN...")
    start = time.time()
    download_with_vpn = st.download() / 1_000_000
    elapsed_with_vpn = time.time() - start
    
    reduction = ((download_no_vpn - download_with_vpn) / download_no_vpn) * 100
    print(f"Throughput reduction: {reduction:.2f}%")

vpn_speed_test()

BandwidthPlace tests often measure short bursts rather than sustained transfers. For file operations, consider:

# Linux: Test sustained transfer with dd over network
dd if=/dev/zero bs=1M count=512 | nc -v work_server 9999

# Windows alternative using PowerShell:
Measure-Command { 
    Get-Content largefile.bin | Out-File \\vpn_mapped_drive\path\testfile.bin 
} | Select-Object TotalSeconds

Try these registry tweaks (Windows):

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\FortiSslvpn\Parameters]
"TcpTuning"=dword:00000001
"TcpWindowSize"=dword:00080000
"MtuDiscover"=dword:00000001

Run these commands during your transfer:

# Continuous ping to detect latency spikes
ping -t work_server

# Path analysis
tracert work_server

# Check for packet fragmentation
netsh interface ipv4 show subinterfaces

At such extreme slowdowns, investigate:

  • VPN tunnel mode (DTLS vs TLS)
  • Split tunneling configuration
  • Corporate traffic shaping policies
  • Potential ISP throttling

When troubleshooting slow file transfers (5KB/s upload) through FortiClient SSL VPN, I encountered conflicting information about expected throughput reduction. Network administrators often cite a 55% performance penalty as standard, but real-world testing tells a different story.

Running speed tests before/after VPN connection showed negligible difference, which contradicts the admin's claim. This reveals an important insight about VPN testing methodology:

# Sample network test script (Python)
import speedtest
import time

def run_vpn_benchmark():
    st = speedtest.Speedtest()
    
    print("Testing raw connection...")
    pre_vpn = st.download()/1024/1024  # Convert to Mbps
    
    # Simulate VPN connection
    connect_to_vpn()
    time.sleep(10)  # Allow tunnel stabilization
    
    print("Testing VPN throughput...")
    post_vpn = st.download()/1024/1024
    
    print(f"Throughput reduction: {((pre_vpn-post_vpn)/pre_vpn)*100:.2f}%")

Forticlient SSL VPN introduces several layers that impact throughput:

  • Encryption overhead (AES-256 typically adds 15-20% overhead)
  • Tunnel MTU size reduction (often 1400 bytes vs standard 1500)
  • TCP-over-TCP problems (especially for large file transfers)

For extreme cases like 5KB/s transfers:

# Troubleshooting steps (Bash commands)
# 1. Check baseline latency:
ping -c 10 corporate-server.com

# 2. Test raw TCP throughput without VPN:
iperf3 -c corporate-server.com -p 5201

# 3. Test through VPN tunnel:
iperf3 -c vpn.corporate-server.com -p 5201

For file transfers, consider these adjustments:

# SFTP configuration for better VPN performance
Host corporate-vpn
    HostName vpn.corporate.com
    User employee
    ServerAliveInterval 60
    TCPKeepAlive yes
    Compression no
    Ciphers aes128-ctr
    MACs hmac-sha1

When VPN throughput remains unacceptable:

// Node.js example for chunked file transfer
const fs = require('fs');
const { exec } = require('child_process');

function chunkedUpload(filePath, chunkSize = 102400) {
  const stream = fs.createReadStream(filePath, { highWaterMark: chunkSize });
  
  stream.on('data', (chunk) => {
    exec(curl -T - https://vpn-server/upload --data-binary @-, 
      { input: chunk.toString('base64') });
  });
}