
Исследовательская статья, август–сентябрь 2026. Все адреса абонентов и часть публичных адресов в примерах обезличены. Это не инструкция по обходу ограничений, а описание эксплуатационной диагностики сети оператора.
Есть довольно мерзкий тип сетевой аварии: почти всё, что обычно смотрит мониторинг, зелёное. Линк поднят, маршрутизация есть, DNS отвечает, пинг идёт, speedtest иногда даже показывает вполне приличную скорость. Но при этом Windows пишет «без доступа к Интернету», телефон решает, что Wi‑Fi плохой, игра не может авторизоваться, IoT теряет облако, часть HTTPS-сайтов открывается, часть висит до тайм-аута, а некоторые системные connectivity-check вообще перестают проходить.
Для пользователя диагноз простой:
Интернет сломался.
Для оператора всё значительно веселее: по классическим метрикам он как будто не сломался.
Мы впервые плотно столкнулись с этим летом 2026 года. Началось всё с одного домашнего подключения и Wireshark, а к сентябрю выросло в отдельный операторский контур из NetFlow v9, GoFlow2, ClickHouse, Grafana и собственной системы проверок доступности. Вместе с контуром поменялся и главный вопрос. Сначала он звучал так:
Почему у одного клиента «нет Интернета», когда пинг есть?
Теперь так:
Можно ли на уровне оператора увидеть сетевой след фильтрации, понять, какой публичный CGNAT уже деградирует, и найти внутри него абонента, который генерирует особенно аномальный трафик?
Спойлер: прочитать внутреннее правило ТСПУ по NetFlow нельзя. Но увидеть последствия происходящего — вполне.
С чего всё началось: июньский Wireshark
Вот один из дампов, снятых ещё в июне.

На этом скриншоте интереснее всего не отдельная строка, а соседство совершенно разных картин. В одних TCP-сессиях видны нормальные ACK и keepalive, внизу живёт DNS — то есть интерфейс не отвалился, IP-маршрутизация не исчезла, стек TCP в целом работает. А рядом к нескольким другим адресам на TCP/443 идут:
SYN →
...
SYN Retransmission →
...
SYN Retransmission →
И сессия так и не переходит в нормальный handshake. Нет ни ожидаемого:
SYN →
← SYN-ACK
ACK →
ни явного отказа:
SYN →
← RST
Просто тишина и повторные SYN.
Наблюдение оказалось важным: оно объясняло, почему «пинг есть» вообще ничего не гарантирует. ICMP может прекрасно проходить, DNS может отвечать, часть уже установленных соединений может жить, а новые TCP-сессии к определённым направлениям при этом не устанавливаются. Для приложения это выглядит как обычный сетевой тайм-аут: оно ждёт, повторяет попытку, снова ждёт. Для пользователя — как «Интернет то есть, то нет». Для простого ICMP-мониторинга — как отсутствие аварии.
Именно этот июньский дамп потом оказался полезен второй раз: в сентябре мы увидели почти тот же симптом, только уже не в PCAP одного клиента, а массово в NetFlow оператора.
Из той истории появился connect-check
Прежде чем строить операторскую аналитику, пришлось научиться фиксировать сам факт деградации со стороны клиента. Ручной перебор сайтов и curl довольно быстро перестал быть полезным: если не открывается один зарубежный сервис, это ещё ничего не говорит, а если не открывается ресурс, который и должен быть ограничен, — тем более.
Нужен был набор независимых проверок разных классов:
- connectivity-check Android, Windows и Apple;
- российские сайты;
- банки и государственные сервисы;
- зарубежные HTTPS-ресурсы;
- игровые платформы;
- AI-сервисы;
- DoH и DoT;
- QUIC;
- NTP;
- MQTT и IoT;
- push;
- CDN;
- почтовые сервисы.
Так появился наш connect-check:
https://github.com/cooler58/connect-check
Он выполняет сотни проверок и складывает результат в один HTML-отчёт. Но для нынешнего исследования особенно важна одна вещь: в каталоге есть признак expected_block.
Условно:
expected_block = 1
означает: недоступность этого ресурса сама по себе сейчас не доказывает локальную проблему нашего egress.
А:
expected_block = 0
означает: ресурс в нормальной ситуации должен быть доступен, и его неожиданный fail уже интересен как внешняя фактура деградации.
Это избавляет нас от очень удобной, но неправильной логики: «увидели высокий score в NetFlow — значит, ТСПУ заблокировал адрес». Высокий score — только гипотеза. Событием для калибровки становится независимое наблюдение: например, на известном публичном egress одновременно перестают проходить обычные российские сервисы и системные проверки, которые падать не должны.
Почему домашнего tcpdump стало мало
PCAP отлично отвечает на вопрос, что происходит с конкретным клиентом прямо сейчас. Но у оператора задача другая: за одним публичным IPv4 через CGNAT могут сидеть десятки или сотни абонентов:
10.20.55.10 ─┐
10.20.55.11 ─┤
10.20.55.12 ─┤
10.20.55.13 ─┤
... ├── CGNAT ── PUBLIC IPv4 ── Internet
10.20.55.184 ─┤
... │
10.20.55.240 ─┘
Для внешнего мира вся эта компания — один адрес. И отсюда вырастает уже операторский вопрос:
Если внешний фильтр, WAF, антифрод или какая-то другая stateful-система принимает решение на уровне публичного IPv4, может ли один особенно «шумный» inside испортить жизнь соседям по NAT?
Сам по себе такой collateral effect давно известен и вообще не уникален для ТСПУ. Например, Cloudflare в 2025 году показал, что CGNAT-адреса попадали под rate limiting примерно втрое чаще обычных IP, хотя медианный bot-rate у CGNAT и non-CGNAT был почти одинаковым. Причина банальна: за одним адресом смешивается поведение множества разных людей и устройств.
Источник: Cloudflare — One IP address, many users: detecting CGNAT to reduce collateral effects.
Поэтому наша гипотеза с самого начала была шире формулировки «ТСПУ банит NAT». Нас интересует shared-IP collateral: может ли аномальная активность одного или нескольких insides менять сетевую судьбу всего публичного egress.
Операторский контур: NetFlow вместо тотального PCAP
Можно было зеркалировать весь трафик и писать PCAP. На небольшой лаборатории это прекрасная идея, а на операторской сети она довольно быстро превращается в отдельный проект по хранению, производительности, приватности и поиску по терабайтам захватов. Поэтому первый массовый слой мы сделали на NetFlow v9.
Схема получилась такой:
MikroTik / edge routers
│
│ NetFlow v9
▼
GoFlow2
│
▼
Python ingest
batch + disk spool
│
▼
ClickHouse
raw + 1m/5m aggregates
│
├── threat views
├── TSPU/silent-drop views
├── connect-check views
└── BEFORE/EVENT/AFTER
│
▼
Grafana
Kafka на первом этапе не добавляли: чем меньше компонентов в исследовательском стенде, тем проще понять, кто именно врёт. Raw flow при этом сохраняем отдельно с TTL, и это оказалось принципиально важно — заранее мы несколько раз ошиблись в том, какая именно метрика окажется полезной. Если бы остались только готовые минутные агрегаты, каждую новую гипотезу пришлось бы проверять только на будущих данных.
Что NetFlow видит, а чего не видит
NetFlow не превращается в DPI только потому, что рядом появился ClickHouse. Мы не видим:
- содержимое TLS;
- HTTP payload;
- точное SNI каждой сессии;
- последовательность отдельных TCP-пакетов;
- retransmission так же подробно, как в PCAP;
- внутреннюю метку ТСПУ «эта сессия заблокирована»;
- конкретное правило, принявшее решение.
Зато видим форму трафика:
src / dst
src_port / dst_port
protocol
tcp_flags
packets
bytes
duration
NAT fields
exporter
timestamp
А из неё уже можно считать:
- flows/sec;
- SYN flows;
- SYN без ACK;
- RST flows;
- долю очень коротких flows;
- число уникальных destination;
- число destination ports;
- число внутренних адресов за NAT;
- UDP/443 и TCP/443;
- изменение всего перечисленного относительно обычного baseline.
И вот этого неожиданно хватило, чтобы увидеть очень характерные профили.
Нюанс, на котором легко сломать всю аналитику: TCP flags
В нашем NetFlow tcp_flags — это OR флагов за жизнь flow. Поэтому мы сознательно говорим:
SYN flow, а не SYN packet.
Если в записи присутствует SYN, это значит, что внутри flow был SYN, но не обязательно, что запись состояла из одного-единственного SYN-пакета. На первый взгляд мелочь, а на практике без этой оговорки очень легко нарисовать красивую, но физически неправильную историю.
Первая серьёзная ошибка: мы смотрели на inside, а надо было на egress
Когда жалуется клиент 10.x.x.x, рука сама тянется строить аналитику вокруг него. Для CGNAT это неправильная единица исследования: внешняя система не знает, какой именно 10.20.55.xxx сейчас создаёт соединение, она видит публичный SNAT. Поэтому модель пришлось перевернуть:
PUBLIC NAT
│
├── inside A
├── inside B
├── inside C
└── inside N
Сначала мы отвечаем, что происходит с публичным egress, и только потом — кто внутри внёс максимальный вклад в его профиль. Это, пожалуй, самый важный архитектурный вывод всего эксперимента.
Вторая ошибка: postNATSource не всегда означает наш NAT
Стоило начать считать по публичным адресам, как у MikroTik NetFlow v9 обнаружилась занятная ловушка. NAT-поля присутствуют и на входящем трафике, поэтому без дополнительной проверки в топы nat_ip внезапно начинали попадать чужие внешние peer-адреса. Получалось очень убедительно и совершенно бессмысленно.
Реальный egress пришлось определять жёстче:
src = private / CGNAT
nat_src = public
nat_src != src
nat_src принадлежит нашей адресации
Только после этого аналитика по публичным NAT стала пригодна для сравнения.
Третья ошибка: BitTorrent прекрасно притворяется сканером
Следующая ловушка ждала уже в самих эвристиках. Наивный скан-детектор выглядит примерно так:
uniq_dst ↑
uniq_dst_port ↑
flows ↑
Проблема в том, что хороший активный P2P-клиент выглядит примерно так же: сотни peers, множество портов, масса коротких UDP flows, постоянные новые destination. Если просто поставить порог на fan-out, половина «злоумышленников» окажется торрентами. Поэтому в threat-профиль пришлось добавить отдельный p2p_like и dampen: если трафик похож на BitTorrent/DHT, высокий fan-out сам по себе не превращается в port-scan.
Отсюда общее правило для подобных исследований:
Одна высокая метрика почти никогда ничего не доказывает.
Что говорят открытые материалы про ТСПУ
Здесь нужна важная оговорка. У нас нет доступа к внутренним логам ТСПУ или ЦСУ. Для понимания возможной архитектуры мы используем открытый проект DanielLavrushin/tspu-docs — подробную реконструкцию по материалам лекции. Мы не считаем её официальной нормативной документацией и используем только как источник технических гипотез. Но несколько деталей из неё очень хорошо рифмуются с нашими наблюдениями.
1. Для protocol-block описан send RST off
В главе 17 описан параметр send RST off: при его выключенном состоянии TCP Reset при блокировке распознанных протоколов не отправляется, соединение молча отбрасывается и клиент дожидается тайм-аута. Мотивация там тоже понятна: немедленный RST заставляет приложение быстро создавать новую попытку и может породить ещё больше сессий.
Для нас это важно не как «доказательство внутренней настройки конкретной площадки», а как объяснение, почему отсутствие RST вообще не противоречит модели фильтрации. То, что в июне выглядело как повторные SYN в Wireshark, а в сентябре — как высокий syn_no_ack в NetFlow, физически вполне согласуется с silent drop.
2. Для протоколов описана двухстадийная схема
В главе 8 описана схема:
DPI recognition
behavior = ignore
│
▼
protocol logs
│
▼
SPFS / ЦСУ
очистка false positives
│
▼
IP + port lists
│
├── filter: block
└── Eco Highway: L3/L4 block
То есть предварительное распознавание шифрованного протокола само по себе не обязано сразу рвать сессию. Сначала результаты собираются, затем централизованно очищаются, после чего формируются списки IP+порт. Там же для тестовой зоны приведён ориентир полного цикла порядка 5–15 минут — от обнаружения нового протокольного endpoint до загрузки очищенного списка. Для нашей аналитики это подсказка смотреть не только момент уже случившегося fail, но и 5–30 минут до него.
3. Разные механизмы могут оставлять разные следы
Открытая реконструкция описывает и DPI-фильтры, и второй эшелон, где готовые IP+port-списки могут применяться на L3/L4. Для NetFlow это означает неприятную, но полезную вещь:
не надо требовать одного универсального «отпечатка блокировки».
В разных сценариях мы можем получить:
- RST-heavy;
- silent SYN drop;
- короткие оборванные flows;
- изменение TCP/443 ↔ UDP/443;
- частичную, а не полную деградацию.
Именно это мы в итоге увидели на живых данных.
Июнь и сентябрь: один симптом в двух масштабах
Июньский PCAP показывал локально:
новый TCP/443
SYN
тишина
повторный SYN
тишина
В сентябре на части публичных NAT мы увидели то же самое уже статистически:
syn ≈ syn_no_ack
short3 ↑
RST ≈ low
То есть большая доля TCP flows содержит SYN, но не показывает признаков нормального развития handshake, а сами записи очень короткие. Мы назвали этот класс:
silent_syn_drop
Это название наблюдаемого эффекта, а не утверждение «мы доказали конкретное внутреннее правило ТСПУ».
Фактура 8 сентября: один особенно интересный CGNAT
Первое событие, где статистика и клиентская фактура сошлись по времени, мы получили 8 сентября — от абонента за одним из публичных NAT. В публикации адрес обезличим.
inside: 10.20.55.xxx
egress: 203.0.113.xxx
Сводка connect-check:
FAIL 209
WARNING 113
OK 198
То есть это не «чёрный экран и полный обрыв Интернета». Почти двести проверок продолжали работать — и именно поэтому проблема выглядела так неприятно: часть Интернета есть, часть нет, часть отвечает нестабильно. Среди неожиданных fail были ресурсы с expected_block=0, в том числе Госуслуги и Ведомости; параллельно массово ломались системные connectivity-check и разные зарубежные HTTPS/IoT-сервисы. Это уже хорошая метка времени: на данном egress действительно наблюдалась заметная частичная деградация, которая не сводилась к обычному списку ожидаемо ограниченных ресурсов.
Теперь смотрим NetFlow того же публичного NAT примерно в тот же период. В одном из срезов:
| Метрика |
Наблюдение |
| внутренних адресов за NAT |
~186 |
| short flows до 3 пакетов |
~88% |
| RST |
~0.4% |
| уникальных destination |
~2682 |
| уникальных destination ports |
~790 |
| flow rate |
~266 flows/s |
На raw-окне порядка 15 минут только TCP/443 было около 145 тыс. flows примерно к 3,8 тыс. destination. Отдельно бросался в глаза fan-out по TCP/22: около 1,8 тыс. flows примерно к 322 адресам назначения.
Сами по себе цифры «виноват ТСПУ» не доказывают, но профиль откровенно шумный и по составу совпадает с описанным выше silent-классом — теперь на масштабе целого NAT. И одновременно независимый connect-check говорит: на этом же публичном адресе уже плохо работают сервисы, которые должны быть доступны. Вот такое совпадение двух независимых слоёв интереснее любого отдельно взятого score.
Но у этого кейса есть неприятная деталь — и мы её не прячем
В HTML connect-check того же клиента присутствовала проблема локальной среды: тест показывал 100% loss до Wi‑Fi gateway с некорректно определённым 0.0.0.0. Если бы нашей целью была эффектная история, эту строчку проще всего было бы не замечать, но для исследования это плохая привычка. Поэтому кейс нельзя честно назвать однозначным доказательством блокировки именно ТСПУ.
Он сильно поддерживает рабочую модель, потому что одновременно есть:
- unexpected fail обычных ресурсов;
- известный публичный egress;
- silent SYN pattern;
- очень высокий short-flow ratio;
- низкий RST;
- около 186 insides на одном NAT;
- выраженный fan-out.
Но для чистого подтверждения нужен контрольный connect-check с Ethernet либо другой клиент на том же egress. Без таких оговорок очень быстро получается не исследование, а коллекция подтверждений заранее любимой теории.
А RST всё-таки бывает
После июньского PCAP и первых сентябрьских данных можно было легко уйти в другую крайность и решить, что RST нам вообще не интересен и всё всегда silent. Тоже нет. На другом публичном NAT в сегодняшнем срезе обнаружился совершенно иной профиль:
RST ≈ 43%
short3 ≈ 99%
То есть почти учебниковая RST-heavy картина. Конкретный адрес мы сознательно не публикуем, но сам факт важен:
на одной и той же операторской сети существуют как минимум два наблюдаемых класса деградации.
Условно:
Класс A — silent
syn_no_ack ↑
short3 ↑
RST low
Класс B — RST-heavy
RST ↑↑
short3 ↑↑
Это хорошо отрезвляет: искать один универсальный индикатор «ТСПУ=1» бессмысленно.
Масштаб: это не один странный NAT
Оставался вопрос, насколько сентябрьский кейс единичен. После того как детектор silent-drop перестроили с RST-first на syn_no_ack + short3, он начал находить похожие публичные egress по всей выборке.
На снимке 8 сентября около 16:40:
~65 публичных NAT
попадали в v_silent_drop_nat, и порядка:
~67 NAT
были помечены connect-check-ориентированным view как кандидаты на тихую деградацию.
Здесь очень важно слово «кандидаты»: порог пока исследовательский, а confirmed-фактуры у нас значительно меньше. Именно поэтому следующим этапом мы не собираемся «повышать чувствительность алерта», а хотим получить несколько независимых connect-check с разных NAT и сравнить их с контрольной группой.
Может ли «шумный» inside мешать соседям?
Это один из самых интересных вопросов всего проекта. Возьмём тот же CGNAT:
┌── обычный абонент A
├── обычный абонент B
├── обычный абонент C
│
PUBLIC IPv4 ◄──────────┼── noisy inside
│ ├── много SYN
│ ├── сотни dst
│ ├── сотни ports
│ └── short flows
│
├── обычный абонент Y
└── обычный абонент Z
Для любого внешнего механизма, который опирается на IP reputation, rate, поведенческий профиль или состояние большого числа сессий, всё это — один источник, и технически идея collateral damage на CGNAT совершенно реалистична (см. упомянутые выше данные Cloudflare). Но применительно именно к нашим событиям ТСПУ причинность пока не доказана.
Мы видим корреляцию:
аномальный NAT
+
noisy insides
+
неожиданные fail
+
характерный TCP-след
Чтобы сказать:
«вот этот конкретный inside своей активностью вызвал деградацию публичного адреса»
нужна повторяемая временная последовательность. Именно поэтому теперь основная единица анализа — не EVENT, а BEFORE → EVENT → AFTER.
BEFORE / EVENT / AFTER: как не перепутать причину со следствием
Высокий SYN может означать две противоположные вещи.
Вариант 1. SYN был причиной аномального профиля
Например, устройство действительно:
- сканирует сеть;
- перебирает сервисы;
- заражено;
- создаёт огромное количество новых соединений.
Вариант 2. SYN уже является следствием проблемы
Соединения перестали устанавливаться, приложения начинают ретраи, и количество SYN растёт после начала деградации. Если смотреть только на EVENT, эти два сценария легко перепутать.
Поэтому каждое подтверждённое внешним тестом событие теперь режем на три окна:
BEFORE EVENT AFTER
───────────────┬─────────────────────┬────────────────
до fail │ connect-check fail │ после события
───────────────┴─────────────────────┴────────────────
Для каждого окна считаем:
flows_sec
syn_sec
syn_no_ack_pct
rst_pct
short3_pct
uniq_dst
uniq_dst_port
uniq_inside
tcp443
udp443
scan_score
amp_score
p2p_like
Самое интересное окно — BEFORE. EVENT показывает, как выглядит уже пострадавший egress, а BEFORE потенциально позволяет найти, что происходило до того, как клиент заметил проблему.
Почему здесь интересны 5–15 минут
Ширину этого окна нам фактически подсказал уже упомянутый ориентир полного цикла из открытой реконструкции. Мы не знаем, совпадает ли этот интервал с конкретными реальными событиями нашей сети в 2026 году, но как исследовательская гипотеза он очень удобен.
Если в будущем несколько независимых кейсов будут выглядеть так:
T-20m на NAT появляется необычный fan-out
T-15m резко растут новые сессии
T-10m выделяется один noisy inside
T0 expected_block=0 начинает FAIL
T+... профиль меняется или доступность восстанавливается
это будет уже гораздо сильнее простой одновременной корреляции. Если такой последовательности не окажется — значит, гипотеза не выдержала проверку. Тоже полезный результат.
Что именно считаем «шумным» профилем
Мы постепенно пришли к трём основным осям.
1. Порты
uniq_dst_port
Много разных destination ports за короткий период может быть признаком port-scan, сервисного перебора или просто очень необычного приложения. Сам по себе показатель слабый, но вместе с высоким SYN и short-flow ratio уже интереснее.
2. Destination
uniq_dst
uniq_dst_pair
Насколько широко источник размазывает соединения по Интернету. Современный браузер, CDN и P2P могут давать большой fan-out совершенно легально, поэтому контекст обязателен.
3. Незавершённые TCP-сессии
syn_no_ack_pct
short3_pct
Особенно интересна комбинация:
syn_no_ack high
short3 high
RST low
Именно она лучше всего соответствует нашему наблюдаемому silent-классу.
Дополнительные оси:
flows_sec;
tcp443 / udp443;
- amp-порты вроде 53/123/1900/11211;
- P2P dampen;
- отклонение от собственного baseline.
Baseline важнее абсолютного числа
Последняя ось из списка на практике оказалась главной. Допустим, мы видим:
100 SYN flows/sec
Само это число ни о чём не говорит: для офиса на десять компьютеров подозрительно, а для большого CGNAT, хостинга или игрового сегмента — возможно, совершенно нормально. Поэтому абсолютные пороги остаются только первым слоем, а для каждого объекта полезнее хранить собственную историю:
average
median
p95
p99
max
stddev
И смотреть не только:
сейчас = 100
а:
сейчас = 100
обычно = 8
p99 = 17
Вот это уже настоящая аномалия конкретного объекта.
Почему мы пока не делаем auto-ban
Когда на графике красиво видно port-scan-ish, очень хочется сразу автоматизировать «лечение». Мы этого сознательно не делаем, потому что под тем же профилем могут скрываться:
- легитимный vulnerability scanner клиента;
- мониторинг;
- Kubernetes;
- P2P;
- игровой launcher;
- backup;
- CDN;
- корпоративный proxy;
- заражённый хост.
NetFlow-эвристика должна сначала привести человека к правильному месту расследования, а не сама вынести приговор. Поэтому сейчас workflow выглядит так:
NetFlow
↓
метрики + score
↓
публичный NAT
↓
вкладчики inside
↓
hourly digest / Grafana
↓
человек
↓
connect-check / PCAP / контакт с абонентом
Это скорее инструмент NOC и security operations, чем автоматическая IDS.
Отдельно пришлось мониторить сам мониторинг
Ещё одна прекрасная возможность обмануть себя — потерять NetFlow на collector и принять дырку в данных за сетевое событие. На первом варианте установки ingest, ClickHouse, Grafana и остальные компоненты жили почти вместе; после подключения нескольких реальных exporters нагрузка быстро стала неприятной, так что роли пришлось разнести и добавить disk spool.
Теперь отдельно смотрим:
- состояние GoFlow2;
- UDP receive;
- exporter sequence gaps;
- задержку ingest;
- ClickHouse inserts;
- CPU/RAM/disk.
Правило простое:
Если у коллектора sequence gap, сначала чините коллектор. ТСПУ подождёт.
Что получилось в Grafana
Вместо одного гигантского дашборда мы разделили задачи.
Почему этому NAT плохо?
Публичный NAT как ключ:
silent drop?
RST?
fan-out?
P2P?
сколько inside?
что происходило до события?
Scanners
Port-scan-ish и host-scan-ish профили.
Amplifiers
Активность по типичным UDP amplification ports.
Malware-ish
Долго живущие или регулярно повторяющиеся аномальные inside. Это именно эвристика, а не диагноз «найден вирус».
Infra
Здоровье самого NetFlow-контура.
Самой полезной частью в итоге оказался не красивый общий score, а возможность провалиться:
problem NAT
↓
insides behind NAT
↓
кто дал SYN?
↓
кто дал fan-out?
↓
кто дал port sweep?
То есть перейти от «плохо всему адресу» к конкретным кандидатам внутри CGNAT.
Июнь 2026 вообще был показательным
Наш локальный Wireshark-скриншот не существовал в вакууме. В июне публично обсуждались проблемы с доступом к российским облачным сервисам: Habr, ссылаясь на сообщения отрасли и публикации РБК, писал о подтверждённых сбоях у ряда российских хостеров и о том, что решения могли приниматься по косвенным признакам зашифрованных соединений — в том числе диапазонам IP и частоте подключений.
Источник: Habr — «РКН устроил проблемы с доступом российским облачным сервисам и сайтам».
Мы не используем эту публикацию как доказательство конкретно нашего июньского дампа. Но она важна как фон: collateral damage от поведенческих и IP-ориентированных механизмов в 2026 году уже наблюдался публично далеко не только нами.
QUIC тоже нельзя забывать
Современный HTTPS — это уже не только TCP/443: браузеры и приложения активно используют QUIC на UDP/443, и если UDP/443 становится недоступен или деградирует, клиент может откатиться на TCP. Поэтому мы отдельно смотрим:
udp443
tcp443
udp443 / tcp443
Пока данных недостаточно, чтобы считать это сильным индикатором. Но резкое изменение отношения на проблемном NAT вполне может оказаться полезным дополнительным признаком.
NetFlow, PCAP и connect-check отвечают на разные вопросы
В результате сложилась довольно удобная трёхслойная модель.
NetFlow
Показывает, где среди тысяч абонентов вообще стоит искать проблему.
connect-check
Показывает, что в этот момент реально видел клиент и какие классы сервисов перестали работать.
PCAP
Показывает, что физически происходило с конкретными сессиями.
Условно:
NetFlow
"кажется, проблема вот на этом egress"
│
▼
connect-check
"да, здесь в 15:11 уже падают unexpected ресурсы"
│
▼
PCAP
"а теперь посмотрим конкретный handshake"
Июньский Wireshark и сентябрьский NetFlow как раз замыкают эту историю.
Что мы уже можем говорить достаточно уверенно
1. «Пинг есть» больше не является достаточной проверкой доступности
Частичная TCP/TLS-деградация прекрасно сосуществует с живым ICMP, DNS и частью установленных соединений.
2. NetFlow способен показать след такой деградации
Не внутреннее решение DPI, а статистический результат на edge.
3. На нашей площадке silent SYN-drop оказался важнее RST
Рабочей оказалась silent-комбинация, описанная выше. Но RST-heavy кейсы тоже существуют, поэтому единственного fingerprint нет.
4. Для CGNAT главная сущность — публичный egress
Inside нужен для attribution, а не как первичный ключ внешнего события.
5. P2P обязательно нужно отделять от сканирования
Иначе top offenders очень быстро превращаются в «список людей с торрентами».
6. Калиброваться надо от внешней фактуры
connect-check expected_block=0 FAIL с известным egress полезнее любого самостоятельно придуманного anomaly score.
7. Самое интересное — BEFORE
Именно поведение перед событием может когда-нибудь дать нам предиктивный сигнал.
Чего мы пока не можем утверждать
Вот здесь лучше быть скучными. Мы не знаем машинного порога вида:
N SYN = блок
Мы не можем по одному NetFlow доказать, какое именно правило сработало внутри ТСПУ, и не можем утверждать, что каждый silent_syn_drop — это ТСПУ: причиной могут быть и другие middlebox, серверная сторона, маршрутизация, локальные проблемы клиента и ошибки измерительного контура.
Мы пока не доказали причинную связь:
noisy inside → решение внешнего фильтра → collateral для всего CGNAT.
Она выглядит правдоподобно и хорошо рифмуется с общей проблемой shared-IP reputation, но для конкретного ТСПУ нам нужны десятки повторяемых labeled events. И мы специально не называем «малварью» любой высокий scan_score — это только кандидат для расследования.
Что будем делать дальше
Сейчас ценнее не ещё десять панелей в Grafana, а фактура. Нужны разные классы реальных событий:
- Silent drop — описанный выше класс.
- RST-heavy — чтобы понять другой тип поведения.
- P2P control — высокий fan-out без деградации.
- Настоящие scanners — для отделения от P2P.
- Чистые CGNAT с сопоставимой нагрузкой, где connect-check полностью здоров.
Для каждого подтверждённого случая:
когда началось
какой public NAT
что fail
что OK
какие insides сидели за NAT
что происходило BEFORE
что происходило EVENT
что изменилось AFTER
После нескольких десятков таких кейсов уже можно считать распределения и квантили, а не спорить о красивых порогах вручную. Например, сравнить:
P(degradation | syn_no_ack, fanout, ports, baseline_delta)
и понять, существует ли вообще устойчивый предиктивный профиль. Если существует — получим ранний сигнал для NOC. Если нет — тоже прекрасно: значит, ещё одна красивая гипотеза умерла от данных, как и положено.
Возможно, главный результат проекта вообще не про ТСПУ
Начиналось всё с попытки понять странную фильтрацию. Но даже если завтра убрать из системы слово «ТСПУ», контур всё равно остаётся полезным. Он уже позволяет видеть:
- аномальные публичные NAT;
- массовые незавершённые TCP-сессии;
- scanners;
- amp-port activity;
- P2P;
- необычный fan-out;
- noisy insides внутри CGNAT;
- отклонение от собственного baseline;
- момент, когда пользовательская доступность расходится с зелёным ICMP.
То есть из узкой попытки разобраться с одним странным сетевым эффектом получился довольно универсальный инструмент эксплуатации CGNAT.
Вместо заключения
В июне мы смотрели на Wireshark и видели повторные SYN к :443 среди совершенно живого остального трафика. В сентябре тот же класс симптомов появился уже на операторской статистике:
syn_no_ack
short flows
fan-out
публичный NAT
сотни insides
Потом рядом нашёлся и другой класс — RST-heavy. То есть простого ответа вида:
TSPU_BLOCKED = true
скорее всего, никогда и не будет. Зато можно сделать кое-что полезнее — не гадать по жалобе «опять ТСПУ или у клиента роутер завис», а собрать несколько независимых слоёв:
реальный fail клиента
+
известный public egress
+
NetFlow до/во время/после
+
вклад конкретных insides
+
при необходимости PCAP
По отдельности каждый признак слабый. Вместе они превращают фразу:
«кажется, Интернет опять как-то странно работает»
в вполне измеримое сетевое событие. А с измеримыми событиями уже можно работать.
Ссылки
Предыдущая часть истории — «Две недели “интернета нет” при зелёных пингах»:
https://articles.clr58.ru/dve-nedeli-net-interneta
connect-check:
https://github.com/cooler58/connect-check
Открытая реконструкция архитектуры ТСПУ:
https://github.com/DanielLavrushin/tspu-docs
Про send RST off:
https://github.com/DanielLavrushin/tspu-docs/blob/main/chapters/17.md
Про двухстадийное формирование списков:
https://github.com/DanielLavrushin/tspu-docs/blob/main/chapters/08.md
Cloudflare про collateral effect CGNAT:
https://blog.cloudflare.com/detecting-cgn-to-reduce-collateral-damage/
Habr про проблемы российских облачных сервисов в июне 2026:
https://habr.com/ru/amp/publications/1046025/
Дисклеймер. Материал описывает эксплуатационные наблюдения на стороне оператора связи и анализ доступных сетевых метаданных. Он не является официальным описанием ТСПУ, инструкцией по обходу ограничений или доказательством того, что конкретное сетевое событие вызвано конкретным правилом Роскомнадзора. Открытые материалы по архитектуре ТСПУ используются как источник гипотез и требуют соответствующей оговорки. Аномальный NetFlow-профиль сам по себе не является основанием обвинять или автоматически блокировать абонента.
#network #monitoring #netflow