WireGuard «працює», і це найнебезпечніший стан для VPN. Ви можете виконати ping, підключитися по SSH, можливо навіть відкрити веб‑сторінку — однак передавання файлів повзає,
резервні копії пропускають вікно, а ваш «швидкий» волоконний канал нагадує готельний Wi‑Fi із проблемами відданості.
Це польовий посібник, який перетворює «WireGuard повільний» на вимірювану вузьку місцину, конкретне виправлення й відтворюваний метод. Жодної міфології. Жодних
випадкових значень MTU з форумного посту 2019 року. Ми протестуємо, прочитаємо лічильники і внесемо зміни, які можна обґрунтувати в постмортемі.
Ментальна модель: де WireGuard може гальмувати
WireGuard — це «лише» мережевий інтерфейс та UDP‑транспорт. Ця простота робить його улюбленим — і водночас пояснює, чому проблеми з продуктивністю часто походять
від усього, що його оточує: MTU, маршрутизація, NAT, поведінка драйвера NIC, черги ядра та планування CPU.
Є чотири поширені режими відмов, що проявляються як «повільно»:
- PMTU / чорні діри фрагментації: малі пакети працюють, великі передачі стоять або коливаються.
- Неправильний маршрут / неправильна політика: трафік завертається, проходить через NAT двічі або виходить не тим інтерфейсом.
- Бутиль у CPU: одне ядро загрузло на 100% під час iperf; пропускна здатність обмежена рівними «круглими» значеннями.
- Втрати/буферизація на UDP: TCP через UDP погано реагує, коли підлягаюча мережа втрачає або перемішує пакети.
Уникайте «тюнінгу», поки не з’ясуєте, що саме у вас. Сліпий тюнінг призводить до конфігурацій, які працюють лише у вівторки.
Операційний підхід: почніть з одного потоку, який можна відтворити (iperf3 підходить). Підтвердіть, чи вузьке місце — на локальному хості,
віддаленому хості чи на шляху. Потім внесіть найменшу зміну, що перемістить стрілку, і знову виміряйте.
Корисна цитата на стікері: «Надія — це не стратегія.» — Gene Kranz.
Жарт №1: Якщо ви випадково змінюєте значення MTU, ви не налаштовуєте VPN — ви займаєтеся нумерологією з додатковими кроками.
Швидкий план діагностики (перший/другий/третій)
Перший: доведіть, чи це MTU/фрагментація
- Запустіть PMTU‑подібний тест ping (не гадіть). Якщо великі DF ping‑и не проходять — у вас чорна діра або невідповідність.
- Перевірте лічильники на предмет необхідності фрагментації / блокування ICMP.
- Якщо PMTU зламаний, зупиніться та виправте MTU або MSS clamp, перш ніж щось міняти далі.
Другий: доведіть, чи це CPU
- Запустіть iperf3 через тунель, одночасно спостерігаючи за завантаженням по ядрах і softirq.
- Якщо одне ядро загрузло (або ksoftirqd шаленіє), ви обмежені CPU/перериваннями.
- Виправляйте це, увімкнувши швидший шлях шифрування (зазвичай уже оптимальний), покращивши розподіл IRQ між ядрами або масштабуючи потоки/пірів/хости.
Третій: доведіть маршрутизацію і коректність шляху
- Перевірте маршрут до призначення (і вибір вихідної адреси) з хоста‑відправника.
- Перевірте асиметричну маршрутизацію: в одному напрямку використовується тунель, в іншому — WAN.
- Підтвердіть, що NAT та правила фаєрволу не переписують або не обмежують UDP.
Якщо всі три виглядають добре — розглядайте втрати/черги UDP
- Виміряйте втрати/повторні передавання через TCP‑статистику та лічильники інтерфейсу.
- Перевірте qdisc, шейпінг, bufferbloat і стан підлягаючого каналу.
- Лише тоді думайте про складний тюнінг (буфери сокетів, fq, pacing).
Цікавинки та контекст (чому ці проблеми існують)
- WireGuard увійшов у ядро Linux у 2020 році, що зробило продуктивність і розгортання значно передбачуванішими, ніж модулі поза ядром.
- Він їде по UDP за дизайном, частково щоб уникнути TCP‑over‑TCP колапсу і частково для простішого обходу NAT — але успадковує «найкраще зусилля» UDP.
- Криптографія використовує ChaCha20‑Poly1305, обрану за високу швидкодію на системах без апаратного AES; на багатьох CPU вона блискавично швидка.
- Path MTU Discovery давно крихкий, бо залежить від ICMP «fragmentation needed», які часто блокують фаєрволи.
- Класичний MTU Ethernet 1500 — історичний артефакт, а не фізичний закон; тунелі додають заголовки і роблять 1500 пасткою.
- Linux‑овські offload‑и (GSO/GRO/TSO) можуть робити знімки трафіку невірними та також приховувати проблеми продуктивності до оновлення драйвера.
- Peer‑и WireGuard ідентифікуються по публічних ключах, а не по IP; помилки маршрутизації часто виглядають як «підключається, але повільно», коли трафік підпадає під неправильні AllowedIPs.
- Хмарні мережі часто вже інкапсулюють ваші пакети (VXLAN/Geneve), тож ваш тунель опиняється всередині іншого тунелю — MTU‑смерть від тисячі заголовків.
- Колапс TCP‑пропускної здатності може спричинити дуже невеликий рівень втрат на великих мережах; наклад VPN рідко є головним ворогом у порівнянні з втратою і RTT.
Практичні завдання: команди, виводи, рішення
Нижче — практичні завдання, які можна виконати на Linux‑хостах (або всередині Linux VM), щоб знайти вузьке місце. Кожне містить: команду, що типовий
вивід означає, і як діяти далі. Виконуйте їх по порядку, якщо хочете швидкості без містики.
Завдання 1: Підтвердіть, що ви справді тестуєте через WireGuard
cr0x@server:~$ ip route get 10.60.0.10
10.60.0.10 dev wg0 src 10.60.0.1 uid 1000
cache
Значення: Трафік до 10.60.0.10 виходить через wg0 з джерелом 10.60.0.1.
Рішення: Якщо ви не бачите dev wg0, зупиніться. Виправте маршрутизацію/AllowedIPs/policy routing перш ніж продовжувати — інакше тести некоректні.
Завдання 2: Перевірте стан peers WireGuard і чи змінюються endpoints
cr0x@server:~$ sudo wg show wg0
interface: wg0
public key: 2r4...redacted...Kk=
listening port: 51820
peer: q0D...redacted...xw=
endpoint: 203.0.113.44:51820
allowed ips: 10.60.0.10/32, 10.20.0.0/16
latest handshake: 28 seconds ago
transfer: 18.42 GiB received, 25.11 GiB sent
persistent keepalive: every 25 seconds
Значення: Handshake недавній; endpoint стабільний; лічильники трафіку рухаються.
Рішення: Якщо latest handshake давній або endpoint часто змінюється, підозрюйте таймаути NAT, роумінг або проблеми зі станом фаєрволу — очікуйте втрат і джиттера.
Завдання 3: Базовий тест підлягаючого каналу (без VPN) — пропускна здатність і затримка
cr0x@server:~$ ping -c 5 203.0.113.44
PING 203.0.113.44 (203.0.113.44) 56(84) bytes of data.
64 bytes from 203.0.113.44: icmp_seq=1 ttl=53 time=19.8 ms
64 bytes from 203.0.113.44: icmp_seq=2 ttl=53 time=20.4 ms
64 bytes from 203.0.113.44: icmp_seq=3 ttl=53 time=19.9 ms
64 bytes from 203.0.113.44: icmp_seq=4 ttl=53 time=62.1 ms
64 bytes from 203.0.113.44: icmp_seq=5 ttl=53 time=20.2 ms
--- 203.0.113.44 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4005ms
rtt min/avg/max/mdev = 19.8/28.5/62.1/16.8 ms
Значення: RTT підлягаючого каналу має стрибки (62 ms). VPN підсилить це у вигляді проблем з TCP‑пропускною здатністю.
Рішення: Якщо є джиттер/втрати у підлягаючому каналі, не чекайте дива від налаштування MTU; можливо потрібне управління чергами або кращий провайдер/шлях.
Завдання 4: Виміряйте пропускну здатність тунелю iperf3 (один потік)
cr0x@server:~$ iperf3 -c 10.60.0.10 -t 15
Connecting to host 10.60.0.10, port 5201
[ 5] local 10.60.0.1 port 43144 connected to 10.60.0.10 port 5201
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-1.00 sec 62.2 MBytes 522 Mbits/sec 0
[ 5] 1.00-2.00 sec 61.8 MBytes 518 Mbits/sec 1
[ 5] 2.00-3.00 sec 44.9 MBytes 377 Mbits/sec 12
[ 5] 3.00-4.00 sec 58.2 MBytes 488 Mbits/sec 3
[ 5] 14.00-15.00 sec 60.1 MBytes 504 Mbits/sec 2
- - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-15.00 sec 848 MBytes 474 Mbits/sec 44 sender
[ 5] 0.00-15.00 sec 846 MBytes 473 Mbits/sec receiver
Значення: Пропускна здатність нестабільна і є повторні передавання.
Рішення: Повторні передавання через VPN зазвичай означають чорні діри MTU, втрати/джиттер підлягаючого каналу або проблеми з буферизацією/чергами. Далі: тести MTU і лічильники втрат.
Завдання 5: Виміряйте з кількома паралельними потоками (щоб виявити обмеження одного ядра)
cr0x@server:~$ iperf3 -c 10.60.0.10 -P 8 -t 15
[SUM] 0.00-15.00 sec 3.62 GBytes 2.07 Gbits/sec 81 sender
[SUM] 0.00-15.00 sec 3.61 GBytes 2.07 Gbits/sec receiver
Значення: Паралелізм значно покращив пропускну здатність.
Рішення: Якщо -P 8 набагато швидше за один потік, можливо ви обмежені CPU на потік або TCP бореться з втратою/RTT. Перевірте CPU і qdisc далі.
Завдання 6: Перевірте MTU на wg0 і підлягаючому інтерфейсі
cr0x@server:~$ ip link show wg0
7: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/none
cr0x@server:~$ ip link show eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
link/ether 52:54:00:12:34:56 brd ff:ff:ff:ff:ff:ff
Значення: wg0 за замовчуванням 1420, типово для IPv4 підлягаючого каналу з MTU 1500.
Рішення: Якщо підлягаючий MTU менший, ніж ви думаєте (хмарний оверлей, PPPoE тощо), 1420 може бути все ще завеликим. Не вгадуйте — тестуйте PMTU.
Завдання 7: PMTU‑тест з DF ping через тунель
cr0x@server:~$ ping -M do -s 1372 -c 3 10.60.0.10
PING 10.60.0.10 (10.60.0.10) 1372(1400) bytes of data.
1380 bytes from 10.60.0.10: icmp_seq=1 ttl=64 time=23.4 ms
1380 bytes from 10.60.0.10: icmp_seq=2 ttl=64 time=23.1 ms
1380 bytes from 10.60.0.10: icmp_seq=3 ttl=64 time=23.3 ms
--- 10.60.0.10 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2002ms
Значення: Payload 1372 (1400 на каналі ICMP) працює. Добрий знак.
Рішення: Збільшуйте, поки не впаде; точка відмови покаже реальний PMTU. Якщо непередбачено низька — ймовірна інкапсуляція або блокування ICMP.
cr0x@server:~$ ping -M do -s 1412 -c 3 10.60.0.10
PING 10.60.0.10 (10.60.0.10) 1412(1440) bytes of data.
ping: local error: message too long, mtu=1420
ping: local error: message too long, mtu=1420
ping: local error: message too long, mtu=1420
--- 10.60.0.10 ping statistics ---
3 packets transmitted, 0 received, +3 errors, 100% packet loss, time 2038ms
Значення: Локальний MTU на wg0 зупиняє вас на 1420 — це ще не тест шляху, це обмеження інтерфейсу.
Рішення: Якщо реальний PMTU нижчий за MTU wg0, ви побачите збої при менших розмірах теж (або дивні зависання). Продовжуйте тестувати поблизу MTU wg0 і слідкуйте за втратами/повторами.
Завдання 8: Перевірте, чи приходить ICMP «frag needed» (PMTU працює)
cr0x@server:~$ sudo ip -s -s link show eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
RX: bytes packets errors dropped missed mcast
9012345678 8123456 0 1452 0 120345
TX: bytes packets errors dropped carrier collsns
8123456789 7012345 0 0 0 0
Значення: Є RX‑дропи (1452). Це може бути перевантаження, переповнення кільця або поліси на вході.
Рішення: Якщо дропи зростають під час iperf, сприймайте це як тиск на шлях прийому — досліджуйте NIC rings/interrupts/qdisc і upstream‑шейпінг.
Завдання 9: Спостерігайте стан TCP (повтори, конгешн) під час передачі
cr0x@server:~$ ss -ti dst 10.60.0.10
ESTAB 0 0 10.60.0.1:43144 10.60.0.10:5201
cubic wscale:7,7 rto:204 rtt:24.1/2.1 ato:40 mss:1360 pmtu:1420 rcvmss:1360 advmss:1360 cwnd:64 bytes_acked:8123456 segs_out:6021 segs_in:5844 send 1.9Gbps lastsnd:8 lastrcv:8 lastack:8 pacing_rate 3.8Gbps delivery_rate 1.7Gbps retrans:12/44
Значення: MSS 1360, PMTU 1420. Є повторні передавання.
Рішення: Повтори плюс стабільний PMTU вказують на втрати/джиттер/черги, а не лише невідповідність MTU. Якщо MSS/PMTU виглядають неправильно, виправте MTU/MSS clamp перш ніж рухатися далі.
Завдання 10: Перевірте насичення CPU і softirq під навантаженням
cr0x@server:~$ mpstat -P ALL 1 5
Linux 6.8.0 (server) 12/27/2025 _x86_64_ (8 CPU)
12:10:01 AM CPU %usr %nice %sys %iowait %irq %soft %steal %idle
12:10:02 AM all 22.1 0.0 18.4 0.0 0.0 20.9 0.0 38.6
12:10:02 AM 0 12.0 0.0 10.1 0.0 0.0 62.3 0.0 15.6
12:10:02 AM 1 28.4 0.0 21.0 0.0 0.0 8.2 0.0 42.4
12:10:02 AM 2 30.1 0.0 25.7 0.0 0.0 5.9 0.0 38.3
12:10:02 AM 3 29.8 0.0 19.4 0.0 0.0 6.1 0.0 44.6
12:10:02 AM 4 18.0 0.0 15.2 0.0 0.0 28.0 0.0 38.8
12:10:02 AM 5 20.5 0.0 17.1 0.0 0.0 26.4 0.0 36.0
12:10:02 AM 6 19.7 0.0 15.9 0.0 0.0 25.1 0.0 39.3
12:10:02 AM 7 17.6 0.0 13.4 0.0 0.0 29.0 0.0 40.0
Значення: CPU0 має велике %soft (softirq). Це часто обробка мережевого прийому і може обмежувати пропускну здатність.
Рішення: Якщо одне ядро домінує в softirq, ймовірно потрібен кращий розподіл IRQ (RSS/RPS), налаштування черг NIC або перенесення навантаження з маленької VM.
Завдання 11: Перевірте тиск на сокети WireGuard та UDP‑буфери
cr0x@server:~$ netstat -su
Udp:
2212345 packets received
0 packets to unknown port received
0 packet receive errors
1832 packets sent
0 receive buffer errors
412 send buffer errors
UdpLite:
IpExt:
InNoRoutes: 0
InBcastPkts: 0
Значення: send buffer errors натякають, що хост не зміг швидко поставити UDP у чергу (або qdisc/NIC зазнає перевантаження).
Рішення: Дослідіть qdisc, TX‑черги NIC і, можливо, обережно збільшіть сокет‑буфери з вимірюванням. Також перевірте вихідний поліcинг.
Завдання 12: Перевірте qdisc і чи використовується адекватна дисципліна черги
cr0x@server:~$ tc qdisc show dev eth0
qdisc fq_codel 0: root refcnt 2 limit 10240p flows 1024 quantum 1514 target 5.0ms interval 100.0ms memory_limit 32Mb ecn
Значення: fq_codel загалом хороший дефолт для латентності та чесності.
Рішення: Якщо бачите pfifo_fast або величезний накопичувач черги, розгляньте перехід на fq_codel або cake (де доступно) на виході, який має значення.
Завдання 13: Підтвердьте policy routing (поширене при split‑tunnel)
cr0x@server:~$ ip rule show
0: from all lookup local
1000: from 10.60.0.0/24 lookup 100
32766: from all lookup main
32767: from all lookup default
cr0x@server:~$ ip route show table 100
default dev wg0 scope link
Значення: Трафік з 10.60.0.0/24 використовує таблицю 100 і за замовчуванням іде через wg0.
Рішення: Якщо відповіді не дотримуються тієї самої політики, буде асиметрія, дивні застої і «лише деякі додатки повільні». Виправте правила, щоб обидва напрямки погоджувалися.
Завдання 14: Шукайте проблеми conntrack/NAT, що карають UDP
cr0x@server:~$ sudo conntrack -S
cpu=0 found=912345 invalid=12 ignore=0 insert=0 insert_failed=0 drop=0 early_drop=0 error=0 search_restart=120
cpu=1 found=901234 invalid=8 ignore=0 insert=0 insert_failed=0 drop=0 early_drop=0 error=0 search_restart=98
Значення: Є invalid, але не багато; search_restart може натякати на тиск на таблицю.
Рішення: Якщо invalid швидко зростає під час навантаження VPN, перевірте правила NAT, таймаути UDP і чи не переписують стан середні елементи.
Завдання 15: Перевірте offloads і чи драйвер NIC робить щось «креативне»
cr0x@server:~$ sudo ethtool -k eth0 | egrep 'gro|gso|tso|tx-checksum|rx-checksum'
rx-checksumming: on
tx-checksumming: on
tcp-segmentation-offload: on
generic-segmentation-offload: on
generic-receive-offload: on
Значення: Offload‑и увімкнені. Зазвичай це добре. Іноді — катастрофа з певними віртуальними NIC або багатими драйверами.
Рішення: Якщо бачите високий CPU, дивну поведінку захоплень пакетів або колапс пропускної здатності тунелю після оновлення ядра/драйвера, експериментально відключіть GRO на wg0 або на підлягаючому інтерфейсі — потім поверніть, якщо не допомогло.
Завдання 16: Захопіть докази без самообману (tcpdump з ясністю)
cr0x@server:~$ sudo tcpdump -ni eth0 udp port 51820 -c 5
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
01:12:01.123456 IP 198.51.100.10.51820 > 203.0.113.44.51820: UDP, length 148
01:12:01.143211 IP 203.0.113.44.51820 > 198.51.100.10.51820: UDP, length 92
01:12:01.163001 IP 198.51.100.10.51820 > 203.0.113.44.51820: UDP, length 1200
01:12:01.182992 IP 198.51.100.10.51820 > 203.0.113.44.51820: UDP, length 1200
01:12:01.203114 IP 203.0.113.44.51820 > 198.51.100.10.51820: UDP, length 92
5 packets captured
Значення: Ви бачите двосторонній UDP‑трафік на порту WireGuard. Добре.
Рішення: Якщо трафік односторонній, проблеми з продуктивністю другорядні — у вас збій шляху/фаєрволу/NAT в одному напрямку.
MTU і фрагментація: найпоширеніша брехня «все гаразд»
Коли WireGuard повільний, MTU буває винуватцем достатньо часто, щоб розглядати його як стандартну підозру — але не як автоматичне виправлення. Правильний підхід:
визначте ефективний PMTU, потім встановіть MTU на wg0 (або clamp MSS), щоб ваш трафік не залежав від того, чи доставлені фрагменти.
Що насправді відбувається
WireGuard інкапсулює пакети всередині UDP. Це означає додаткові заголовки. Якщо внутрішній пакет розміром під 1500 байт, зовнішній пакет може
перевищити 1500 і або:
- бути фрагментованим (кращий випадок: фрагменти приходять; гірший випадок: фрагменти губляться), або
- бути відкинутим з ICMP «fragmentation needed» (якщо PMTUD працює), або
- тихо відкинутись (PMTUD‑чорна діра, класика).
Шаблони відмов характерні:
- Малі пакети ОК, великі падають: SSH працює, копіювання файлів зупиняється, веб‑сторінки частково завантажуються.
- Пилообразна пропускна здатність: TCP розганяється, досягає стіни, колапсує, повторюється.
- «Виправляється» на інших мережах: бо PMTU різниться по шляхах та політиках фільтрації ICMP.
Не поклоняйтеся 1420
1420 — розумний дефолт для IPv4‑підлягаючого каналу з MTU 1500. Але:
- Якщо WireGuard працює по IPv6‑підлягаючому каналу, наклад інший.
- Якщо ваш «1500 MTU» — оверлей (cloud SDN) або PPPoE, у вас може бути менше.
- Якщо є IPSec, GRE, VXLAN або «допоміжні» middlebox‑и — у вас менше.
Дві надійні стратегії
-
Встановити MTU wg0 на відоме безпечне значення.
Це впливає на весь трафік, включно з UDP‑додатками. Жорсткий, але надійний підхід. -
Завести MSS clamp на вході/виході тунелю.
Це впливає лише на TCP і може зберегти більший MTU для UDP‑потоків, які переносять фрагментацію або не надсилають великі payload.
Практичний робочий процес MTU, який можна відстояти
- Запустіть DF ping‑тести до віддаленого IP тунелю (Завдання 7), знайдіть найбільший payload, що проходить стабільно.
- Відніміть правильний наклад, якщо тестуєте на різних рівнях (будьте послідовні).
- Встановіть MTU wg0 так, щоб лишалося запасу, а не «ледь‑ледь проходить у гарний день».
- Повторіть iperf3 і порівняйте повторні передавання та стабільність, а не тільки пік швидкості.
Приклад: безпечне встановлення MTU
cr0x@server:~$ sudo ip link set dev wg0 mtu 1380
cr0x@server:~$ ip link show wg0
7: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1380 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/none
Значення: MTU wg0 тепер 1380.
Рішення: Якщо продуктивність стабілізувалась (менше повторів, більш послідовна пропускна здатність), залиште і задокументуйте виміряний PMTU. Якщо нічого не змінилось — MTU не головний вузький місце.
Приклад: MSS clamp (iptables)
cr0x@server:~$ sudo iptables -t mangle -A FORWARD -o wg0 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
cr0x@server:~$ sudo iptables -t mangle -S FORWARD | tail -n 2
-A FORWARD -o wg0 -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -j TCPMSS --clamp-mss-to-pmtu
Значення: TCP SYN пакети, що виходять через wg0, матимуть MSS‑clamp.
Рішення: Використовуйте це, коли не можете надійно контролювати MTU енд‑ту‑енд (багато пірів, роумінг клієнтів) або коли у вас проблеми лише з TCP. Підтвердіть через ss -ti, що MSS/PMTU вирівняні.
Маршрутизація і policy routing: коли пакети їдуть мальовничим шляхом
Помилки маршрутизації не завжди порушують зв’язок. Вони погіршують його так, що виглядає як «повільний VPN», тоді як корінь — «пакети туристять». Це особливо видно в split‑tunnel, мультихомінгових серверах та на ноутбуках із змінними маршрутами.
AllowedIPs — це маршрутизація, а не контроль доступу
AllowedIPs WireGuard — це універсальне поле: воно каже ядру, які призначення слід направляти до того піра, і також виконує роль таблиці маршрутизації за криптоключем. Важливо: якщо вказати неправильно, трафік може піти до неправильного пира, або вихід через тунель не відбутися, або використовуватися неправильна вихідна адреса.
Асиметрична маршрутизація: мовчазний вбивця пропускної здатності
Ваш вихідний трафік може йти через WireGuard, але відповіді повертатимуться через підлягаючий канал, інший тунель або шлях NAT, що спотворює стан. TCP ненавидить асиметрію. UDP теж, просто менше скаржиться.
Як швидко виявити обман маршрутизації
- Використовуйте
ip route getдля призначення з відправника (Завдання 1). - Використовуйте
ip ruleтаip route show table X, коли є policy routing (Завдання 13). - Перевірте reverse path filtering (
rp_filter), якщо у вас кілька інтерфейсів і policy routing.
Reverse path filtering: «функція безпеки», що стала багом доступності
cr0x@server:~$ sysctl net.ipv4.conf.all.rp_filter net.ipv4.conf.eth0.rp_filter net.ipv4.conf.wg0.rp_filter
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.eth0.rp_filter = 1
net.ipv4.conf.wg0.rp_filter = 0
Значення: Строгий rp_filter увімкнено глобально і на eth0. У політично роутованих налаштуваннях це може скидати легітимні асиметричні відповіді.
Рішення: Якщо ви бачите нез’ясовані дропи і асиметрію, встановіть rp_filter у 2 (loose) на залучених інтерфейсах або вимкніть там, де це доречно — потім перевірте лічильники пакетів.
cr0x@server:~$ sudo sysctl -w net.ipv4.conf.all.rp_filter=2
net.ipv4.conf.all.rp_filter = 2
Значення: Режим loose. Дає певні перевірки без руйнування policy routing.
Рішення: Робіть це постійним тільки після підтвердження, що це вирішує проблему і не суперечить вашій моделі загроз.
CPU і крипто: коли кількість ядер обмежує пропускну здатність
WireGuard швидкий. Але «швидкий» не означає «безкоштовний», і підлягаючий канал може бути достатньо швидким, щоб виставити CPU як стелю. На дрібних хмарних інстансах, старих Xeon, зайнятих гіпервізорах або хостах із великою роботою conntrack/фаєрволу ви цілком можете загрузити ядро й припинити масштабування.
Як виглядає вузьке місце CPU
- iperf3 виходить на плато стабільної швидкості, незалежно від можливостей каналу.
- Одне ядро зафіксовано в
%softабо%sys, інші — переважно вільні. - Паралельні потоки iperf3 підвищують пропускну здатність більше, ніж слід було б.
- Системний час зростає з частотою пакетів, а не з байтами.
Відрізнити вартість крипто від вартості обробки пакетів
Люди люблять звинувачувати крипто, тому що це звучить витончено. Часто реальна проблема — витрати на обробку пакетів: переривання, softirq, поведінка GRO/GSO,
conntrack і qdisc. Крипто WireGuard ефективне; проблеми часто у мережевому шляху хоста.
Перевірте системний час ядра і softirq
cr0x@server:~$ top -b -n 1 | head -n 15
top - 01:20:11 up 16 days, 3:12, 1 user, load average: 3.21, 2.88, 2.44
Tasks: 213 total, 2 running, 211 sleeping, 0 stopped, 0 zombie
%Cpu(s): 18.2 us, 0.0 ni, 27.6 sy, 0.0 id, 0.0 wa, 0.0 hi, 54.2 si, 0.0 st
MiB Mem : 16000.0 total, 2200.0 free, 4100.0 used, 9700.0 buff/cache
MiB Swap: 2048.0 total, 2048.0 free, 0.0 used. 11200.0 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
934 root 20 0 0 0 0 S 62.0 0.0 31:22.11 ksoftirqd/0
2112 root 20 0 0 0 0 S 18.0 0.0 12:04.22 ksoftirqd/4
Значення: Потоки softirq спалюють CPU. Це мережеві витрати на обробку.
Рішення: Досліджуйте IRQ affinity, кількість черг NIC, RPS/XPS і чи не голодує VM за vCPU або не страждає від noisy neighbor.
Розподіл переривань і черги
cr0x@server:~$ grep -E 'eth0|wg0' /proc/interrupts | head
24: 8123456 0 0 0 0 0 0 0 PCI-MSI 327680-edge virtio0-input.0
25: 0 7012345 0 0 0 0 0 0 PCI-MSI 327681-edge virtio0-output.0
Значення: Переривання сконцентровані на певних CPU.
Рішення: Якщо більшість переривань приходить на одне ядро, налаштуйте IRQ affinity і переконайтеся, що увімкнено multi‑queue. Мета: розподіл обробки пакетів по ядрах без надмірних кеш‑колізій.
Реалії апаратного прискорення крипто
ChaCha20 WireGuard добре працює на багатьох CPU, навіть без AES‑NI. Але картина змінюється при:
- Дуже високій частоті пакетів (малі пакети): витрати домінують на пакет.
- VM з обмеженими vCPU і поганим налаштуванням virtio.
- Фаєрволах з великим навантаженням conntrack на тому ж хості.
Якщо стеля — CPU: збільшіть інстанс, винесіть фаєрвол або завершіть тунель на машині, створеній для пропуску пакетів. Купівля більшого VM — іноді правильне й дешевше рішення, ніж витрати часу команди.
NIC offloads, checksum‑дивності та податок віртуальних машин
WireGuard живе в ядрі — це добре. Але він все ще залежить від драйверів NIC і стеку Linux‑мережі. Offload‑и (GRO/GSO/TSO) зазвичай покращують пропускну здатність і знижують CPU. Іноді вони взаємодіють погано з тунелями, віртуальними NIC або фільтрами пакетів.
Симптоми, що нагадують проблеми з offload
- Захоплення пакетів показує «гігантські» пакети, яких немає на дроті.
- Продуктивність різко змінилася після оновлення ядра без змін конфігурації.
- Пропускна здатність в одному напрямку нормальна, в іншому — жахлива.
- Високий CPU в softirq плюс незрозумілі дропи на хості.
Контрольований експеримент: відключіть GRO на wg0
cr0x@server:~$ sudo ethtool -K wg0 gro off
Cannot get device settings: Operation not supported
Значення: Інтерфейс WireGuard не підтримує стандартні ethtool‑перемикачі так, як фізичний NIC.
Рішення: Використовуйте sysctls і зосередьтеся на offload‑ах підлягаючого NIC. Для самого WireGuard зосередьтесь на MTU, маршрутизації та поведінці CPU/IRQ.
Релевантніше: переключення offload на підлягаючому інтерфейсі (тестуйте, не коммітьте сліпо)
cr0x@server:~$ sudo ethtool -K eth0 gro off gso off tso off
cr0x@server:~$ sudo ethtool -k eth0 | egrep 'gro|gso|tso'
tcp-segmentation-offload: off
generic-segmentation-offload: off
generic-receive-offload: off
Значення: Offload‑и вимкнені. CPU‑витрати зростуть; частота пакетів зросте.
Рішення: Якщо відключення offload вирішує стабільність тунелю (рідко, але буває), скоріш за все це регрес драйвера/віртуалізації. Тримайте це тимчасовим обхідним шляхом; плануйте виправлення ядра/драйвера/гіпервізора.
Податок VM
На багатьох гіпервізорах VPN‑кінцевий пункт у VM платить додатковим накладом:
- Драйвери virtio/netfront і обробка vSwitch додають затримку й CPU‑витрати.
- Койлінг переривань і планування vCPU можуть створювати вибухоподібну доставку.
- Провайдери хмар можуть обмежувати UDP або знижувати пріоритет під час навантаження.
Якщо ви намагаєтеся провести багатогігабітний трафік через маленьку VM — позитивне мислення не перетворить її на велику.
Реалії UDP: втрати, джиттер, буфери та шейпінг
WireGuard використовує UDP. Це перевага, але означає, що ви успадковуєте поведінку підлягаючого каналу без вбудованого шару повторної передачі. Якщо підлягаюча мережа втрачає або перемішує пакети, тунель цього не «виправить». TCP всередині тунелю помітить це і покарає вас зниженням congestion window і повторними передаваннями.
Що вимірювати, коли «іноді просто повільно»
- Втрати: дропи інтерфейсу, помилки UDP, TCP‑повтори.
- Джиттер: варіація ping і таймінги додатків.
- Черговання: статистика qdisc, симптоми bufferbloat (стрибки затримки під навантаженням).
- Поліси: шейпінг виходу хмари або middlebox‑и, що лімітують (часто суворо щодо UDP‑сплесків).
Перевірте статистику qdisc на дропи/overlimits
cr0x@server:~$ tc -s qdisc show dev eth0
qdisc fq_codel 0: root refcnt 2 limit 10240p flows 1024 quantum 1514 target 5.0ms interval 100.0ms memory_limit 32Mb ecn
Sent 912345678 bytes 701234 packets (dropped 123, overlimits 0 requeues 12)
backlog 0b 0p requeues 12
Значення: На qdisc були дропи. Це локальне перевантаження або шейпінг.
Рішення: Якщо дропи корелюють зі сповільненнями, вирішіть проблеми вихідного чергування: забезпечте fq_codel/cake, зменшіть навантаження або виправте upstream‑шейпінг замість звинувачення WireGuard.
Сокет‑буфери: лише після помітних помилок буфера
Люди люблять піднімати rmem_max і wmem_max. Іноді це допомагає; часто просто підвищує затримку й використання пам’яті, тоді як реальна проблема — перевантаження лінії.
cr0x@server:~$ sysctl net.core.rmem_max net.core.wmem_max
net.core.rmem_max = 212992
net.core.wmem_max = 212992
Значення: Стандартні максимальні буфери.
Рішення: Збільшуйте тільки якщо бачите помилки буфера (netstat -su) і розумієте, куди переміститься чергування. Вимірюйте після кожної зміни.
cr0x@server:~$ sudo sysctl -w net.core.rmem_max=8388608
net.core.rmem_max = 8388608
cr0x@server:~$ sudo sysctl -w net.core.wmem_max=8388608
net.core.wmem_max = 8388608
Значення: Дозволено більші максимальні буфери.
Рішення: Повторіть iperf3 і перевірте netstat -su. Якщо помилки впали і пропускна здатність стабілізувалась без вибухового зростання затримки — залиште; інакше відкотіть і дивіться на підлягаючу перевантаженість.
Жарт №2: UDP — як офісні плітки: швидкий, легкий, і якщо щось загубилось, ніхто не повернеться, щоб це виправити.
Три корпоративні міні‑історії з полів
Інцидент через неправильне припущення: «MTU у хмарі завжди 1500»
Середня компанія запускала флот шлюзів WireGuard у великому cloud‑провайдері. У staging все було гаразд. В проді — щоденні дампи бази «іноді» зависали. Не падали — зависали.
Першою реакцією було очікуване: звинуватити базу, потім інструмент бекапу, потім сторідж. Хтось запропонував «можливо WireGuard повільний». Знизили MTU до 1280, бо бачили в блозі. Трохи покращилося, потім наступного тижня регрес після зміни шляху в мережі cloud.
Неправильне припущення: підлягаючий шлях не був простим Ethernet 1500. Провайдер додав інкапсуляцію, і ефективний PMTU різнився між зонами доступності. PMTUD мав би це вирішити, але ICMP «frag needed» фільтрувався security‑аппаратом, який встановили «тимчасово», а потім він став постійним — як часто буває.
Виправлення було нудне: дозволити релевантні типи ICMP, встановити MTU wg0 на основі виміряного PMTU для найгіршого шляху і клеппнути MSS для безпеки. Нічні бекапи стали стабільні, і команда перестала викликати інженерів сховища через проблему мережі під маскою сховища.
Урок: якщо ви не виміряли PMTU end‑to‑end, у вас не MTU — у вас система вірувань.
Оптимізація, що бумерангом: «Вимкнути qdisc, щоб зменшити наклад»
Внутрішня платформа вирішила отримати максимум пропускної здатності між двома дата‑центрами. Вони прочитали, що дисципліни черг додають наклад, і вирішили «спростити», переключивши egress на простий FIFO і збільшивши буфери. Графіки в лабораторії полюбили це: більший пиковий throughput.
Потім прийшов прод. Днем великий transfer викликав стрибки затримки для інших сервісів. Тунель був спільним для кількох застосунків, і FIFO‑черга перетворилася на гармату латентності. RPC таймаути підскочили. Ретраи збільшили навантаження. Класичний самонанесений шторм.
Інженери хотіли звинуватити WireGuard. Але головною проблемою було не втрата пакета; це була черга і затримка. VPN просто став місцем, де зійшовся трафік, і отримав несправедливу вину.
Відкат до fq_codel стабілізував затримки і покращив ефективну пропускну здатність для інтерактивних потоків. Піковий throughput великого transfer‑у впав трохи. Нікому це не заважало, бо бізнес турбувався про «системи працюють», а не про один красивий показник в бенчмарку.
Урок: якщо ви оптимізуєте throughput без контролю черг, ви будуєте механізм DoS і називаєте це продуктивністю.
Нудна, але правильна практика, що врятувала день: «Вимірювати, фіксувати, перевіряти після кожної зміни»
Компанія з суворим контролем змін тримала WireGuard між on‑prem і хмарою. Була скарга: «VPN повільний». Замість тюнінгу, SRE написав простий ранбук: один iperf3 тест, один MTU тест, одна перевірка маршруту, одна перевірка CPU. Результати в тикеті.
За кілька тижнів проявилися патерни. Затримки корелювали з певним ISP‑маршрутом і зростанням джиттера підлягаючого каналу. Інші інциденти корелювали з конкретним розміром VM для шлюзу: у пікові години високий steal time.
Коли великий спад продуктивності стався під час релізу, команда не сперечалася. Вони запустили ті самі тести. Пінг підлягаючого каналу показав джиттер. iperf3 — повтори. CPU в нормі. Маршрути правильні. Це швидко відсікло половину здогадок.
Вони тимчасово перенаправили трафік через інший gateway з кращим шляхом і відкрили ескалацію ISP з чіткими доказами. VPN не «виправили» магічним sysctl — виправили, знаючи, де проблема.
Урок: нудна дисципліна вимірювань — це мультиплікатор сили. Вона не героїчна, але запобігає дорогим здогадкам.
Поширені помилки: симптом → корінь → виправлення
1) Симптом: SSH працює, великі завантаження зависають або зупиняються
Корінь: PMTU‑чорна діра або невідповідність MTU; фрагментація відкидається; ICMP заблоковано.
Виправлення: Виміряйте PMTU DF ping‑ами; знизьте MTU на wg0; clamp MSS; дозвольте ICMP «fragmentation needed» на шляху.
2) Симптом: Пропускна здатність нормальна з iperf3 -P 8 але погана з одним потоком
Корінь: Один потік TCP обмежений RTT/втратою або CPU на потік; congestion window не росте.
Виправлення: Перевірте повторні передавання і джиттер; спочатку виправте MTU/втрати; якщо CPU‑ліміт — покращте IRQ‑розподіл або масштабуйте шлюз.
3) Симптом: Тунель повільний лише з одного сайту / одного ASN / одного Wi‑Fi
Корінь: Підлягаючий шлях має втрати/джиттер або провайдер шейпить; іноді UDP обробляють погано.
Виправлення: Базуйте підлягаючий ping/jitter; порівняйте з різних мереж; подумайте про зміну порту endpoint, але лише після перевірки MTU і маршруту.
4) Симптом: Швидко кілька хвилин, потім колапс, потім відновлення
Корінь: Чергування/bufferbloat або транзитні дропи; іноді флапінг стану NAT без keepalive.
Виправлення: Використовуйте fq_codel/cake на egress; моніторте qdisc дропи; встановіть PersistentKeepalive для роумінг‑/NAT‑пірів; перевірте conntrack.
5) Симптом: Один напрям значно повільніший за інший
Корінь: Асиметрична маршрутизація, вихідний поліcинг або різна MTU/PMTU поведінка по кожному шляху.
Виправлення: Запустіть перевірки маршруту з обох кінців; перевірте policy routing і rp_filter; порівняйте статистику qdisc і дропи інтерфейсу по напрямках.
6) Симптом: Продуктивність погіршилася після оновлення ядра/драйвера/гіпервізора
Корінь: Зміни поведінки offload; регрес у virtio або драйвері NIC; інша модерація переривань.
Виправлення: Тестуйте offload‑перемикачі як тимчасове рішення; налаштуйте IRQ affinity; плануйте оновлення/відкат з доказами.
7) Симптом: «WireGuard повільний» лише на шлюзі, що також робить фаєрволінг
Корінь: conntrack та правила iptables/nft додають навантаження CPU; softirq‑тиск; контенція таблиць.
Виправлення: Зменшіть conntrack для трафіку тунелю там, де безпечно, спростіть правила, збільшіть CPU або розділіть ролі (VPN vs stateful firewall).
8) Симптом: Стрибки затримки під навантаженням, навіть коли throughput прийнятний
Корінь: Поганий qdisc (FIFO), занадто великі буфери або шейпінг без AQM.
Виправлення: Використовуйте fq_codel/cake; уникайте «просто збільшити буфери»; вимірюйте ping під навантаженням.
Чек‑лісти / покроковий план (безпечний, нудний, ефективний)
Чек‑ліст A: Одногодинна діагностика інциденту в проді
- Підтвердьте маршрутизацію:
ip route getдо віддаленого IP тунелю. Якщо не wg0 — виправте routing/AllowedIPs спочатку. - Підтвердьте стан тунелю:
wg showдля свіжості handshake і руху лічильників. - Запустіть iperf3: один потік і
-P 8. Зверніть увагу на повтори і стабільність. - MTU‑тест: DF ping до IP піра; шукайте чорні діри.
- Перевірка CPU/softirq:
mpstat/top. Якщо одне ядро під завантаженням — це обмеження хоста. - Втрати і черги:
ip -s linkдропи;tc -s qdiscдропи/overlimits. - NAT/conntrack: статистика conntrack, лічильники фаєрволу, якщо є.
- Вирішіть: MTU/MSS виправлення, маршрут, масштаб CPU шлюзу або ескалація провайдера з доказами.
Чек‑ліст B: Жорсткість WireGuard‑розгортання для передбачуваної продуктивності
- Виберіть стратегію MTU: встановіть MTU wg0 на найгірший виміряний PMTU і/або clamp MSS для TCP.
- Стандартизуйтесь на qdisc: fq_codel на WAN‑виході; уникайте FIFO, якщо вас турбує латентність.
- Плануйте CPU: бенчмаркуйте iperf3 і виміряйте softirq по ядрах перед тим, як вважати «достатньо.»
- Задокументуйте маршрути: AllowedIPs, policy rules, source‑based routing; додайте пояснення в коментарі конфігурацій.
- Вирішіть політику keepalive: роумінг‑клієнти і NAT‑шляхи потребують PersistentKeepalive; сервери на стабільних лінках — часто ні.
- Інструментуйте: експортуйте дропи інтерфейсів, dropи qdisc, softirq CPU і вік handshake; налаштуйте алерти на тренди, а не на одноразові сплески.
Чек‑ліст C: План змін для підозрюваного MTU з відкатом
- Виміряйте базовий iperf3 і
ss -tiна предмет повторних передавань. - Виміряйте максимальний Payload DF ping, що проходить стабільно.
- Зменшіть MTU wg0 на невелику, обґрунтовану величину (наприклад, 20–40 байт) або реалізуйте MSS clamp.
- Повторіть iperf3 і спостерігайте зміну повторів.
- Відкотуйте, якщо немає покращення; MTU не був вузьким місцем.
- Запишіть виміряний PMTU і обране значення, щоб ніхто потім «не оптимізував» його випадково.
FAQ
1) Яку пропускну здатність слід очікувати від WireGuard?
На сучасному обладнанні WireGuard може досягати багатогігабітних швидкостей. На практиці ваша стеля зазвичай — CPU, поведінка NIC/драйвера та втрати/джиттер підлягаючого каналу.
Вимірюйте iperf3 і стежте за softirq по ядрах.
2) Чи завжди 1420 — правильний MTU для wg0?
Ні. Це розумний дефолт для підлягаючого IPv4 з MTU 1500. Хмарні оверлеї, PPPoE і вкладені тунелі можуть вимагати менших MTU. Вимірюйте PMTU і встановлюйте MTU на основі доказів.
3) Краще MSS clamp чи встановити MTU?
Якщо проблема переважно з TCP і у вас різні клієнти/шляхи, MSS clamp часто менш руйнівний. Якщо ви несете значний UDP‑трафік (VoIP, ігри, QUIC) і PMTU ненадійний, встановлення безпечного MTU для wg0 чистіше.
4) Чому iperf3 -P 8 виглядає чудово, а один потік посередній?
Паралельні потоки ховають обмеження одного потоку (RTT, чутливість до втрат, поведінку congestion control) і розподіляють витрати CPU. Якщо бізнес‑трафік — один потік (бекапи, завантаження об’єктів), оптимізуйте під нього: виправте втрати/MTU і зменшіть чергування.
5) Чи можуть правила фаєрволу робити WireGuard повільним?
Абсолютно. Станний фільтр і conntrack додають CPU‑витрати і можуть скидати пакети під навантаженням. Також фаєрволи, що блокують ICMP «fragmentation needed», ламають PMTUD і викликають класичний симптом «малі пакети ОК, великі пакети гинуть».
6) Чи WireGuard повільніший за OpenVPN?
Часто він швидший, особливо в режимі ядра. Але якщо вузьке місце — PMTU‑чорні діри, асиметрична маршрутизація або втрати підлягаючого каналу, зміна VPN не вирішить фізику і політику. Спочатку діагностуйте.
7) Чи допомагає зміна порту WireGuard?
Іноді так, але це не перше рішення. Зміна порту може обійти дурні rate limits або пошкоджені middlebox‑и. Спочатку робіть MTU/mаршрут/CPU‑перевірки, щоб не «вирішити» неправильну проблему.
8) Чому WireGuard повільний лише в одній мережі ноутбука?
Ймовірно PMTU/фільтрація ICMP на тій мережі або шейпінг/втрати UDP на шляху. Роумінг‑клієнти за NAT також страждають без keepalive. Порівняйте DF ping і спостерігайте стабільність handshake.
9) Чи варто збільшувати сокет‑буфери Linux для WireGuard?
Тільки якщо є доказ, як помилки буфера UDP. Великі буфери можуть підвищити затримку і приховати перевантаження. Виправляйте чергування і втрати перш ніж налаштовувати буфери.
Наступні кроки, які ви можете виконати цього тижня
- Виберіть один відтворюваний тест (iperf3 один потік +
-P 8) і зафіксуйте базову пропускну здатність, повтори і CPU. - Запустіть DF ping PMTU‑тести через тунель і визначте MTU або MSS clamp на основі найбільшого надійного розміру, а не відчуттів.
- Підтвердіть маршрути детерміністично за допомогою
ip route getіip ruleна обох кінцях; шукайте асиметрію. - Стежте за softirq під навантаженням; якщо одне ядро загрузло, налаштуйте IRQ розподіл або масштаб шлюзу.
- Перевірте дропи qdisc і перейдіть на fq_codel там, де це важливо; припиніть використовувати FIFO, якщо латентність має значення (а вона завжди має).
- Напишіть односторінковий runbook з точними командами, які ви сьогодні виконали, і «якщо вивід X, зробити Y» рішеннями.
WireGuard не «повільний». Повільний ваш шлях, ваш MTU, ваша маршрутизація або ваш CPU. Добра новина: їх можна діагностувати кількома командами і відмова від вгадувань.