Стабильная работа туннеля: anti-RST, --fix-gro, QUIC-блок, автозагрузка

Проверено на живом стенде: 0% потерь на 10.66.66.1 и 8.8.8.8, выход через
сервер (curl -> 95.174.102.77).

Транспорт (устранение разрывов faketcp):
- anti-RST вписан в конфиги вместо хрупкого -a+flush:
  клиент — первой строкой chain input (ip saddr 95.174.102.77 tcp sport 25565 drop),
  сервер — в таблице wgfwd (ip daddr 192.168.31.163 tcp dport 25565 drop, переживает ufw reload).
  Причина: RST от ядра убивал CGNAT-маппинг оператора -> direction timeout и смена порта.
- --fix-gro на обоих концах: чинит дроп GRO «супер-пакетов» (huge packet > max_data_len).

Клиент:
- nftables.conf: flush ruleset (убирает дубли), reject QUIC/UDP 443 (откат браузера на TCP).
- udp2raw-client.service: After=nftables.service (anti-RST применяется до старта udp2raw).

Автозагрузка/порядок (обе службы udp2raw):
- After=network-online.target, Before=wg-quick@wg0.service.

README: раздел 5 «Известные нюансы» (anti-RST vs flush, CGNAT/RST, GRO),
сниппеты и таблицы приведены в соответствие.
This commit is contained in:
Maxim
2026-07-08 11:35:46 +03:00
parent 888fd86dfa
commit 538414f2c8
5 changed files with 67 additions and 21 deletions

View File

@@ -1,4 +1,7 @@
# RASPBERRY PI 4: /etc/nftables.conf
#!/usr/sbin/nft -f
# RASPBERRY PI 4: /etc/nftables.conf
# flush обязателен: без него повторный `nft -f` накапливает дубли правил.
flush ruleset
table ip nat {
chain postrouting {
@@ -11,6 +14,15 @@ table ip nat {
table inet filter {
chain input {
type filter hook input priority filter; policy drop;
# udp2raw faketcp anti-RST (КРИТИЧНО, должно стоять ПЕРВЫМ — выше ct-accept):
# обратный faketcp от сервера попадает под ct established и доходит до TCP-стека
# ядра, а тот шлёт RST на «левый» TCP -> шторм RST -> туннель встаёт, но данные
# не идут. Прячем эти пакеты от стека; udp2raw читает их через AF_PACKET (до
# netfilter), поэтому drop ему не мешает. Аналог правила, которое ставит флаг -a.
# Держим правило ЗДЕСЬ (а не через -a), чтобы flush ruleset его не стирал.
ip saddr 95.174.102.77 tcp sport 25565 drop
ct state established,related accept
iifname "lo" accept
ip protocol icmp accept
@@ -28,6 +40,11 @@ table inet filter {
iifname "wlan0" oifname "usbnet" accept
iifname "usbnet" oifname "wlan0" ct state established,related accept
# Блокируем QUIC/HTTP-3 (UDP 443) в туннель: он НЕ подчиняется MSS-clamp,
# крупные датаграммы теряются при MTU 1200 и сайты «висят».
# reject (а не drop) → браузер сразу откатывается на TCP/HTTPS. Ставить ДО accept.
iifname "wlan0" oifname "wg0" udp dport 443 reject
iifname "wlan0" oifname "wg0" accept
iifname "wg0" oifname "wlan0" ct state established,related accept

View File

@@ -1,13 +1,21 @@
[Unit]
Description=udp2raw Client (FakeTCP)
After=network.target
# network-online (а не network.target): нужен поднятый uplink usbnet до старта.
Wants=network-online.target
# nftables.service должен примениться ДО udp2raw: в /etc/nftables.conf лежит anti-RST
# правило (ip saddr 95.174.102.77 tcp sport 25565 drop). Требует enabled nftables.service.
After=network-online.target nftables.service
# Порядок автозагрузки: udp2raw поднимается ДО туннеля WireGuard (Endpoint=127.0.0.1:51821).
Before=wg-quick@wg0.service
[Service]
Type=simple
User=root
# Слушает локальный UDP-порт 51821 и транслирует в TCP на внешний вход сервера 95.174.102.77:25565
# ВАЖНО: Пароль в параметре -k должен строго совпадать с паролем на сервере!
ExecStart=/usr/local/bin/udp2raw -c -l 127.0.0.1:51821 -r 95.174.102.77:25565 -k "MY_CONFIDENTIAL_PASS" --raw-mode faketcp -a
# --fix-gro: стек склеивает входящие faketcp-сегменты в «супер-пакеты» >1800,
# udp2raw их дропает ("huge packet ... dropped") — флаг это чинит.
ExecStart=/usr/local/bin/udp2raw -c -l 127.0.0.1:51821 -r 95.174.102.77:25565 -k "MY_CONFIDENTIAL_PASS" --raw-mode faketcp --fix-gro -a
Restart=always
RestartSec=5