Ограничение HTTP/3 (QUIC)

Я проверял с DigitalOcean AMS. Оператор Yota.

Тесты с 157.240.201.35:
Initial с server_name www.google.com:

$ nping --udp -p 443 -c 1 --data "$(xxd -ps -c 999999 google_quic_initial.bin)" 157.240.201.35    

Starting Nping 0.7.92 ( https://nmap.org/nping ) at 2022-03-20 11:18 MSK
SENT (0.0016s) UDP packet with 1357 bytes to 157.240.201.35:443
RCVD (0.2062s) UDP packet with 1232 bytes from 157.240.201.35:443

Max rtt: 204.642ms | Min rtt: 204.642ms | Avg rtt: 204.642ms
UDP packets sent: 1 | Rcvd: 1 | Lost: 0 (0.00%)
Nping done: 1 IP address pinged in 0.21 seconds

Initial с server_name www.instagram.com:

$ nping --udp -p 443 -c 1 --data "$(xxd -ps -c 999999 instagram_quic_inital.bin)" 157.240.201.35  

Starting Nping 0.7.92 ( https://nmap.org/nping ) at 2022-03-20 11:18 MSK
SENT (0.0015s) UDP packet with 1357 bytes to 157.240.201.35:443

Max rtt: N/A | Min rtt: N/A | Avg rtt: N/A
UDP packets sent: 1 | Rcvd: 0 | Lost: 1 (100.00%)
Nping done: 1 IP address pinged in 1.00 seconds

Тоже самое происходит и с www.facebook.com. При использовании nfqws оба server_name доходят.

Через Tele2 instagram отвечает почти всегда, но не каждый раз. На личный сервер этот пакет доходит всегда.

На Yota, действительно, есть фильтр, и на первый взгляд, он декодирует QUIC-пакет и выявляет имя домена.

На моем провайдере с ТСПУ начались непонятные и хаотичные отрубы QUIC сессий, пробитых с помощью nfqws --dpi-desync=fake. Пейлоад пробовал разный. Разницы никакой.

Тест : с firefox с вычищенным кэшем заходим на https://ipv4.google.com
через раз получается как на картинке
По паре ip:port перестают ходить пакеты, как если бы DPI занес UDP поток в черный список. Точно так же, как если первым пакетом пошел бы QUIC initial. Но здесь происходит отруб внезапно в середине сеанса. Всовывание фейков перед каждым пакетом не помогает.
Пробовал завертывать UDP через свой сервер. Тоже самое. Если пропустить через VPN - проблема уходит
Пробовал взять первые 2 пакета от клиента и от сервера (initial) и долбиться ими nping-ом по одной паре ip:port с сервера и клиента после начального пробития фейком. Не отрубает.
nping по блокированной паре ip:port с ограниченным TTL показывает, что блок идет на 5-хопе. Там стоит как раз ТСПУ и блокировщик сайтов
Эвристика - статистика какая-то ?

У домру недавно наблюдались блокировки QUIC на youtube, facebook, в отличие от VK. Сейчас проверил, пока QUIC заработал на них. Тестят наверно…

Крайний раз, когда тестил домру около месяца назад, у них наблюдался лоад балансинг.
Часть трафика уходит через ТСПУ, а часть нет. Где нет - там простой пассивный DPI.
Потому может то работать QUIC, то не работать. По какому критерию балансируется трафик я не изучал, возможно по IP. Учитывая прыгающий характер ip от крупных сервисов, может так и получиться, что когда-то запросы идут через ТСПУ, а когда-то нет

Почему после включения nfqws может совсем переставать работать http3? Если отключить nfqws, то через curl работает http3, но в хроме только http2. Проверял на quic.nginx.org и cloudflare-quic.com, ютубе и др. Если включить VPN, то в хроме начинает полноценно работать http3. В firefox работает http3 и без vpn, но частично, только cloudflare-quic.com, а на quic.nginx.org не работает, на других сайтах чаще тоже работает. В Safari совсем ситуация странная, буквально пару дней назад http3 работал везде даже лучше, чем в firefox, а сегодня даже через vpn не работает.

nfqws --dpi-desync-fwmark=0x40000000 --qnum=210 --dpi-desync=fake --debug

curl -4ILv --max-time 5 --http3 https://vk.com                                                                                                                                                                                                    28 ↵
*   Trying 87.240.190.72:443...
* Connect socket 5 over QUIC to 87.240.190.72:443
* Sent QUIC client Initial, ALPN: h3-29,h3-28,h3-27
* After 2498ms connect time, move on!
* connect to 87.240.190.72 port 443 failed: Connection timed out
*   Trying 87.240.190.67:443...
* Connect socket 6 over QUIC to 87.240.190.67:443
* Sent QUIC client Initial, ALPN: h3-29,h3-28,h3-27
* After 1249ms connect time, move on!
* connect to 87.240.190.67 port 443 failed: Connection timed out
*   Trying 87.240.190.78:443...
* Connect socket 5 over QUIC to 87.240.190.78:443
* Sent QUIC client Initial, ALPN: h3-29,h3-28,h3-27
* After 624ms connect time, move on!
* connect to 87.240.190.78 port 443 failed: Connection timed out
*   Trying 87.240.137.158:443...
* Connect socket 6 over QUIC to 87.240.137.158:443
* Sent QUIC client Initial, ALPN: h3-29,h3-28,h3-27
* After 313ms connect time, move on!
* connect to 87.240.137.158 port 443 failed: Connection timed out
*   Trying 87.240.139.194:443...
* Connect socket 5 over QUIC to 87.240.139.194:443
* Sent QUIC client Initial, ALPN: h3-29,h3-28,h3-27
* After 156ms connect time, move on!
* connect to 87.240.139.194 port 443 failed: Connection timed out
*   Trying 93.186.225.208:443...
* Connect socket 6 over QUIC to 93.186.225.208:443
* Sent QUIC client Initial, ALPN: h3-29,h3-28,h3-27
* After 78ms connect time, move on!
* connect to 93.186.225.208 port 443 failed: Connection timed out
* Failed to connect to vk.com port 443: Connection timed out
* Closing connection 0
curl: (28) Failed to connect to vk.com port 443: Connection timed out
IP4: 192.168.1.113 => 87.240.137.158 proto=udp sport=61894 dport=443
UDP: CF FF 00 00 1D 10 2C 20 B7 21 87 58 81 F7 7C 35 DD 53 A2 C5 3E 53 14 B9 69 39 3D BA 50 CD 16 54 ... : ......, .!.X..|5.S..>S..i9=.P..T ...
packet contains QUIC initial
hostname: vk.com
dpi desync src=192.168.1.113:61894 dst=87.240.137.158:443
sending fake request : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ... : ................................ ...
reinjecting original packet. len=1228 len_payload=1200
packet: id=30 drop
packet: id=31 len=1228
IP4: 192.168.1.113 => 87.240.139.194 proto=udp sport=61598 dport=443
UDP: C2 FF 00 00 1D 10 38 DB 6E CE F1 CB 56 79 07 F0 93 1A 44 1A F7 B7 14 D2 BD E2 80 6C A9 B3 09 58 ... : ......8.n...Vy....D........l...X ...
packet contains QUIC initial
hostname: vk.com
dpi desync src=192.168.1.113:61598 dst=87.240.139.194:443
sending fake request : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ... : ................................ ...
reinjecting original packet. len=1228 len_payload=1200
packet: id=31 drop
packet: id=32 len=1228
IP4: 192.168.1.113 => 93.186.225.208 proto=udp sport=60154 dport=443
UDP: CF FF 00 00 1D 10 2D F6 2E 56 03 F9 77 77 84 84 EA E7 CE D4 48 AF 14 EF E5 4F 88 92 50 04 C4 1F ... : ......-..V..ww......H....O..P... ...
packet contains QUIC initial
hostname: vk.com
dpi desync src=192.168.1.113:60154 dst=93.186.225.208:443
sending fake request : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ... : ................................ ...
reinjecting original packet. len=1228 len_payload=1200
packet: id=32 drop

Единственная проблема с атакой fake против QUIC, которую я нашел , это спорадическое застревание потока на середине, как описано выше. Видимо оно вызванно какими-то статистическими методами анализа на ТСПУ, срабатывающими через раз.

Но если этого не случается, то поведение броузеров на провайдерах с ТСПУ + nfqws и провайдерах без ТСПУ и без nfqws выглядит идентичным

Броузеры сейчас слишком умные. Их цель - не подключить вас по самому крутому и модному протоколу, а загрузить страницу как можно быстрее. Исходя из этих соображений они сами вольны выбирать какое подключение использовать и через какую версию ip.

У меня и на хромиуме, и на мозилле, quic.nginx.org проходит тест на QUIC, а cloudflare не проходит на обоих броузерах. Смотрим F12 и видим, что там первая часть запросов идет по http/2, вторая по http/3. Если убрать nfqws на провайдере с ТСПУ, то все запросы идут http/2

Мне сообщали, что на некоторых провайдерах (yota), они стали дешифровывать QUIC на DPI. Я не в курсе подробностей, но дело вот в чем. Сейчас у них задача - полностью забанить сам протокол QUIC, кроме white list ip (вконтакт, слишком серьезный клиент).
Если они научатся анализировать QUIC, они могут перестать банить сам протокол и убрать статистический анализ.

Собрал curl с quiche.
2 теста на провайдере с ТСПУ.
первый с nfqws, второй без

curll -4Iv --http3 https://cloudflare-quic.com
*   Trying 172.67.9.235:443...
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: none
* Connect socket 5 over QUIC to 172.67.9.235:443
* Sent QUIC client Initial, ALPN: h3,h3-29,h3-28,h3-27
*  subjectAltName: host "cloudflare-quic.com" matched cert's "cloudflare-quic.com"
* Verified certificate just fine
* Connected to cloudflare-quic.com () port 443 (#0)
* h2h3 [:method: HEAD]
* h2h3 [:path: /]
* h2h3 [:scheme: https]
* h2h3 [:authority: cloudflare-quic.com]
* h2h3 [user-agent: curl/7.83.0-DEV]
* h2h3 [accept: */*]
* Using HTTP/3 Stream ID: 0 (easy handle 0x7fffd6627210)
> HEAD / HTTP/3
> Host: cloudflare-quic.com
> user-agent: curl/7.83.0-DEV
> accept: */*
>
< HTTP/3 200
HTTP/3 200
< date: Sat, 26 Mar 2022 09:32:25 GMT
date: Sat, 26 Mar 2022 09:32:25 GMT
< content-type: text/html
content-type: text/html
< content-length: 109425
content-length: 109425
< expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
< server: cloudflare
server: cloudflare
< cf-ray: 6f1ee103ee899150-FRA
cf-ray: 6f1ee103ee899150-FRA
< alt-svc: h3=":443"; ma=86400, h3-29=":443"; ma=86400
alt-svc: h3=":443"; ma=86400, h3-29=":443"; ma=86400
* Connection #0 to host cloudflare-quic.com left intact
./curll -4Iv --max-time 5 --http3 https://cloudflare-quic.com
*   Trying 104.22.9.38:443...
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: none
* Connect socket 5 over QUIC to 104.22.9.38:443
* Sent QUIC client Initial, ALPN: h3,h3-29,h3-28,h3-27
* After 2499ms connect time, move on!
* connect to 104.22.9.38 port 443 failed: No error
*   Trying 104.22.8.38:443...
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: none
* Connect socket 6 over QUIC to 104.22.8.38:443
* Sent QUIC client Initial, ALPN: h3,h3-29,h3-28,h3-27
* After 1249ms connect time, move on!
* connect to 104.22.8.38 port 443 failed: No error
*   Trying 172.67.9.235:443...
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: none
* Connect socket 5 over QUIC to 172.67.9.235:443
* Sent QUIC client Initial, ALPN: h3,h3-29,h3-28,h3-27
* After 624ms connect time, move on!
* connect to 172.67.9.235 port 443 failed: No error
* Failed to connect to cloudflare-quic.com port 443 after 4374 ms: Couldn't connect to server
* Closing connection 0
curl: (28) Failed to connect to cloudflare-quic.com port 443 after 4374 ms: Couldn't connect to server

вывод nfqws --debug

packet: id=1 len=1228
IP4: 192.168.4.2 => 172.67.9.235 proto=udp sport=51710 dport=443
UDP: C6 00 00 00 01 10 71 F8 72 8E CC 61 C9 FD BB AB FA 94 E7 C1 A6 F1 14 90 55 4D 8B D1 44 3D E1 C9 ... : ......q.r..a............UM..D=.. ...
packet contains QUIC initial
hostname: cloudflare-quic.com
dpi desync src=192.168.4.2:51710 dst=172.67.9.235:443
sending fake request : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ... : ................................ ...
reinjecting original packet. len=1228 len_payload=1200
packet: id=1 drop

А можно еще собрать curl с openssl-quic от мелкомягких + nghttp3 + ngtcp2. Это будет альтернативная реализация, она используется в хромиуме. Только, это непросто :slight_smile:

Есть еще одна мысль. Недавно такая проблема вскрылась.
Если в linux настроен сложный policy routing. Несколько аплинков, таблиц маршрутизации, ip rule , основанные на source address, то пакеты, высланные через raw socket-ы могут уходить не с того интерфейса. Что от nfqws, что от nping. При этом обычные пакеты уходят нормально.
Если это ваш случай, то надо убедиться, что пакеты идут действительно в нужный интерфейс и правильно заменяется source адрес , если используется NAT.
Пока что самое эффективное лечение этой ситуации - вносить ip rule, базированные на fwmark, который выставляет nfqws

Skynet, СПб
Блочится http3 до инстаграмма и фейсбука, но до гугла пропускает. Похоже стали расшифровывать заголовки.
Причем по http2 пропускает тоже. К слову у skynet нет dpi, блок идет на транзитном провайдере, скорее всего МТС

http3 до инсты

./curl -4Iv -m 5 --http3 https://instagram.com
*   Trying 31.13.72.174:443...
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: none
* Connect socket 5 over QUIC to 31.13.72.174:443
* Sent QUIC client Initial, ALPN: h3,h3-29,h3-28,h3-27
* After 2493ms connect time, move on!
* connect to 31.13.72.174 port 443 failed: No error
* Failed to connect to instagram.com port 443 after 2507 ms: Couldn't connect to server
* Closing connection 0
curl: (28) Failed to connect to instagram.com port 443 after 2507 ms: Couldn't connect to server

http3 до инсты через vpn

./curl -4Iv -m 5 --http3 https://instagram.com
*   Trying 31.13.72.174:443...
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: none
* Connect socket 5 over QUIC to 31.13.72.174:443
* Sent QUIC client Initial, ALPN: h3,h3-29,h3-28,h3-27
*  subjectAltName: host "instagram.com" matched cert's "instagram.com"
* Verified certificate just fine
* Connected to instagram.com () port 443 (#0)
* h2h3 [:method: HEAD]
* h2h3 [:path: /]
* h2h3 [:scheme: https]
* h2h3 [:authority: instagram.com]
* h2h3 [user-agent: curl/7.83.0-DEV]
* h2h3 [accept: */*]
* Using HTTP/3 Stream ID: 0 (easy handle 0x560fda7c27d0)
> HEAD / HTTP/3
> Host: instagram.com
> user-agent: curl/7.83.0-DEV
> accept: */*
>
< HTTP/3 405
HTTP/3 405

http2 до инсты

./curl -4Iv -m 5 --http2 https://instagram.com
*   Trying 31.13.72.174:443...
* Connected to instagram.com (31.13.72.174) port 443 (#0)
* ALPN: offers http/1.1
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_128_GCM_SHA256
* ALPN: server accepted http/1.1

http3 до гугла

./curl -4Iv -m 5 --http3 https://google.com
*   Trying 64.233.162.100:443...
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: none
* Connect socket 5 over QUIC to 64.233.162.100:443
* Sent QUIC client Initial, ALPN: h3,h3-29,h3-28,h3-27
*  subjectAltName: host "google.com" matched cert's "google.com"
* Verified certificate just fine
* Connected to google.com () port 443 (#0)
* h2h3 [:method: HEAD]
* h2h3 [:path: /]
* h2h3 [:scheme: https]
* h2h3 [:authority: google.com]
* h2h3 [user-agent: curl/7.83.0-DEV]
* h2h3 [accept: */*]
* Using HTTP/3 Stream ID: 0 (easy handle 0x55ae3ae897d0)
> HEAD / HTTP/3
> Host: google.com
> user-agent: curl/7.83.0-DEV
> accept: */*
>
< HTTP/3 301
HTTP/3 301

Они писали на форуме, что на разных тарифах разные фильтры стоят. На lite “более сильный DPI”
А может быть от адреса подключения зависит
У меня тариф “Земля”. Все нормально и по v4, и по v6

Разработчики СКАТ DPI (VAS Experts) выпустили статью о работе с QUIC в своём блоге.

На мобильном мегафоне возможно тоже такое есть: пришлось сменить симку в модеме на сим с другим тарифом, и прошлые настройки zapret перестали работать

Kathrin Elmenhorst, an author of “Web Censorship Measurements of HTTP/3 over QUIC”, has a repository with further tests of QUIC blocking, derived from OONI measurements. One thread of investigation is about Russia, and it has some interesting observations.

In particular, on Yota (AS 31213), there appears to be a two-layer filter. One layer blocks all QUIC version 1, except to specific servers; and a second layer blocks QUIC version 1 with with specific SNI values, no matter the server. This is interesting because the second layer means they are decrypting the initial packet protection on the packet containing the Client Hello.

The evidence for this comes from observing what happens when accessing different servers using different SNI values.

condition result
Access foreign server with correct SNI blocked
Access foreign server with vk.com SNI blocked
Access vk.com server with vk.com SNI works
Access vk.com server with www.facebook.com SNI blocked

Всё ещё наблюдается?
Сколько пакетов пропускает до блокировки (на картинке сквозная нумерация, число неочевидно)? Сохранились дампы?

Наблюдается. Там хаотичное поведение. 1-2 сессии могут пройти, 3 - застрять
сколько пакетов я не считал
2 провайдера с ТСПУ дают одинаковое поведение

Система может расшифровать QUIC_V1 и декодировать SNI.

Если ограничиться тестовым набором или соблюдать тестовые размеры, тогда всё работает: SNI декодируется и принимается решение.

Система не может или не хочет достать SNI из пакета, который генерирует Firefox. Возможно дело в размерах (например для структур далее по стеку). Недоступный SNI триггерит блокировку.

Приведите пример пакетов, которые фильтруются и не фильтруются. Выложите pcap’ы или содержимое пакетов.

blocked_firefox.bin (1.3 KB)
works_quictls.bin (1.2 KB)