projects:qoe_analytics:unknown_discovering:start

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Next revision
Previous revision
projects:qoe_analytics:unknown_discovering:start [2026/07/28 13:17] – created evgeniyprojects:qoe_analytics:unknown_discovering:start [2026/07/28 15:24] (current) – [Маршрутизация к наборам сервисов] evgeniy
Line 1: Line 1:
-====== start ======+====== Unknown Discovering — распознавание QUIC/HTTPS 0-RTT без SNI ====== 
 + 
 +Точка входа для LLM при анализе выгрузок неопознанного трафика **без видимого SNI**. Метод не завязан на QUIC: он применим к любому трафику, где в ClientHello нет SNI — QUIC 0-RTT (''quic_unknown''), HTTPS/TLS 1.3 с 0-RTT (early data), session resumption, ECH. Здесь описан метод классификации IP-узлов CDN по их роли в инфраструктуре провайдера сервисов. Наборы признаков (сеты хостов) по провайдерам — в пространстве имён [[projects:qoe_analytics:unknown_discovering:services:start|:services]]. 
 + 
 +===== Когда применять ===== 
 + 
 +  * Задача — классифицировать трафик QUIC или HTTPS с 0-RTT, в котором отсутствует SNI (поле host пустое). 
 +  * На вход — выгрузки из внутренней системы QoE: агрегированный netflow (top hosts) и сырой netflow (raw flow). 
 +  * Цель — определить роль конкретного узла ''IP:443'' в инфраструктуре CDN и присвоить метку сервиса (например ''youtube'', ''google_play''). 
 + 
 +===== Общие принципы анализа ===== 
 + 
 +  * Работа поверх DPI: есть риск, что протокол уровня приложений распознан неверно. Особенно осторожно — с протоколами малого объёма: при недостатке данных ложные срабатывания чаще. 
 +  * В нераспознанный трафик часто попадают 0-RTT пакеты. Анализируй **окружение** неизвестного протокола, а не отдельный флоу. 
 +  * Оценивай вероятность наличия того или иного трафика на AS. Возможно, что под сервис маскируется другой тип трафика (в т.ч. через SNI). Если AS принадлежит крупной компании (например Meta) — ожидаемо увидеть трафик её сервисов. 
 +  * Проверяй однотипные выгрузки из **нескольких временных диапазонов** — это даёт стабильный набор признаков. 
 + 
 +===== Алгоритм классификации IP → сервис ===== 
 + 
 +Метод строит «землю» (ground truth) из флоу, где SNI виден, и применяет её к тому же IP там, где SNI отсутствует. 
 + 
 +==== Шаг 1. Кандидаты ==== 
 +Из агрегированного netflow получить top host IP by traffic за период, отфильтровав неопознанный трафик без SNI, сортировка по суммарному объёму в обе стороны. Фильтр зависит от того, что разбираем: для QUIC это ''application protocol = "quic_unknown …"'', для HTTPS 0-RTT — соответствующий неопознанный HTTPS/TLS-протокол. Это IP-кандидаты на классификацию. 
 + 
 +==== Шаг 2. Земля по SNI ==== 
 +По каждому IP-кандидату выгрузить сырой netflow, где поле host (SNI) **не пустое**. Это выборка: какие SNI реально жили на этом IP. 
 + 
 +Схема raw flow (важно): 
 +  * ''Host IP'' — канонический IP сервера, по нему группируем. 
 +  * ''Host'' — SNI/hostname, это и есть признак. 
 +  * Серверный порт двунаправленного флоу выводится из направления: если ''Host IP == Source IPv4'', то серверный порт = ''Source port'', иначе = ''Destination port''. Порт 443 попадает то в источник, то в назначение — прямой фильтр по ''Destination port = 443'' отрежет половину трафика. 
 + 
 +==== Шаг 3. Оценка попадания в сет ==== 
 +Для каждого IP посчитать долю записей, чей host попадает по маске в набор сервиса. Если доля превышает порог сервиса — присвоить метку узлу ''IP:443''
 + 
 +===== Правила матчинга ===== 
 + 
 +  * **Знаменатель** — все записи с непустым host на данном IP. 
 +  * **Доля** — записи, чей host совпал с любой маской набора, к знаменателю (по числу записей, не по объёму и не по уникальным FQDN). 
 +  * **Семантика ''*''** — fnmatch: звёздочка = любая подстрока, **включая точки**. ''*storage.googleapis.com'' ловит и ''storage.googleapis.com'', и ''firebasestorage.googleapis.com'', и ''bucket.storage.googleapis.com''
 +  * **Регистр** — матчинг регистронезависимый. 
 +  * **Порог** — у каждого сервиса свой (см. страницы в :services). Если порог берут несколько сервисов на одном IP — метка по максимальной доле. 
 +  * **Доп. фильтры набора.** Помимо масок host, набор может нести дополнительные фильтры (например по ''Host AS'' — номеру автономной системы). Записи, не проходящие фильтр, не учитываются ни в числителе, ни в знаменателе набора. Фильтры смотри на странице провайдера; они могут задаваться индивидуально для каждого набора. 
 + 
 +===== Выделение и дополнение сетов ===== 
 + 
 +Отдельная задача: не проверить IP против готового набора, а **построить набор** масок для нового сервиса или дополнить существующий. Это обратный ход алгоритма. 
 + 
 +==== Что запросить ==== 
 +Сырой netflow для изучаемого ''Host IP'', где поле ''Host'' **не пустое** (та же схема, что на шаге 2: ''Host IP'', ''Host'', ''Source/Destination IPv4'' + порт). Инструмента автоматической выгрузки пока нет — запросить выгрузку у аналитика (ручной ввод xlsx/csv). 
 + 
 +==== Процедура ==== 
 +  - Сгруппировать по ''Host IP''; взять записи с непустым ''Host''; серверный порт вывести как на шаге 2. 
 +  - Построить распределение FQDN: число записей на каждый ''Host'' (при необходимости — по eTLD+1). Отдельно посмотреть распределение по объёму (''Octet delta'') и отметить, если оно расходится с распределением по числу записей. 
 +  - Определить доминирующее семейство сервиса на IP. Проверить, что это не сторонний контент на общей инфраструктуре (см. мультитенантность). 
 +  - Обобщить FQDN в маски: свернуть случайные лейблы в wildcard (''rr1---sn-xxx.googlevideo.com'' → ''*.googlevideo.com''); предпочитать суффиксные маски / eTLD+1; ''*'' по fnmatch (подстрока с точками). Маски держать достаточно узкими, чтобы не пересекались с наборами других сервисов. 
 +  - Выбрать порог: высокий (напр. 80%) для сервисов с высоким риском загрязнения общими фронтами (как youtube среди GFE), 50% для более «чистых» выделенных семейств. Обосновать выбор. 
 +  - Валидировать: прогнать маски по этой же выгрузке и, желательно, по выгрузкам из **других временных диапазонов** — набор стабилен, если те же IP/FQDN проходят порог в разных окнах. 
 +  - Зафиксировать на странице провайдера в :services в стандартном формате (метка, порог, маски) + заметка о причине порога и о рискованных масках. 
 + 
 +==== Предостережения ==== 
 +  * Не строить наборы по netblock / AS / IPv6-сигнатурам — это только подсказки для discovery, проверять по SNI-«земле». 
 +  * Одна выгрузка = одно временное окно; подтверждать набор по нескольким окнам до финализации. 
 +  * Следить за update/ad-инфраструктурой (''gvt1'', ''2mdn'', ''doubleclick''), которая может завышать долю сервиса — решать таксономию явно. 
 + 
 +===== Важные предостережения ===== 
 + 
 +  * **Мультитенантность.** Принадлежность IP netblock'у провайдера ≠ сервис. Пример: ''35.241.31.20'' — адрес Google Cloud (35.x), но по «земле» на 100% отдаёт ''h3.market.xiaomi.com'' (магазин Xiaomi поверх GCP). Метку ''google_cloud'' ставить нельзя — ни одна googleapis-маска не совпадает, и это правильный результат. Классифицируй по SNI-«земле», а не по владельцу адреса. 
 +  * **Общие фронты (GFE).** На общих фронтах Google один IP отдаёт play + maps + cloud + прочее, и ни одна доля не дотягивает до порога — IP остаётся без метки. Это ожидаемо; не занижай порог, чтобы «дотянуть». 
 +  * **Объём vs число записей.** Доля считается по числу записей. Несколько тяжёлых видеосессий доминируют по объёму, но по числу host'ов картина обратная — не путай метрики. 
 +  * **IPv6 и anycast.** Среди кандидатов бывают IPv6 и anycast-адреса; «земля» протухает при ротации. Согласуй период шага 1 и шага 2. 
 + 
 +===== Маршрутизация к наборам сервисов ===== 
 + 
 +Наборы масок по провайдерам — в [[projects:qoe_analytics:unknown_discovering:services:start|:services]]: 
 + 
 + 
 +===== DSL декларация ===== 
 + 
 +Формальная нотация для описания правил классификации. Основные конструкции: ''SELECT'' (выборка кандидатов, шаг 1), ''SET'' (декларация набора), ''RULE'' (правило классификации, шаг 3); плюс ''DISCOVER'' — обратный ход (выделение набора). 
 + 
 +Общие соглашения: 
 +  * Маски host — семантика fnmatch: ''*'' = любая подстрока, включая точки. 
 +  * Доля совпадения считается **только по числу записей** (by records). Знаменатель — записи с непустым host на IP, прошедшие фильтры. 
 +  * Комментарии — ''//'' до конца строки. 
 +  * Строковые значения с пробелами/числами — в кавычках (напр. ''"quic_unknown 49293"''). 
 + 
 +==== SELECT — выборка кандидатов (шаг 1) ==== 
 +Отбор IP-кандидатов из агрегированного netflow. Здесь же задаётся фильтр по AS — он применяется ко всей выборке. 
 + 
 +<code> 
 +CANDIDATES = 
 +  SELECT host_ip, total_volume 
 +  FROM   top_ip_host_agg 
 +  WHERE  application_protocol = "quic_unknown 49293" 
 +     AND host_as IN (28917)            // Fiord 
 +  ORDER BY total_volume DESC 
 +  LIMIT  30 
 +</code> 
 + 
 +  * ''WHERE'' — условия отбора; ''host_as IN (...)'' — список AS (под несколько значений). 
 +  * Фильтр по AS обычно живёт здесь, на шаге 1, и применяется ко всему пулу кандидатов. Пример: кеш-сервер Instagram развёрнут в Fiord (''AS28917''), поэтому AS фильтруется на выборке, а не в наборе. 
 + 
 +==== SET — декларация набора ==== 
 +Именованный набор: маски + порог. Фильтр — опционально. 
 + 
 +<code> 
 +SET instagram { 
 +  masks: 
 +    instagram.*.fbcdn.net 
 +    *.cdninstagram.com 
 +  threshold: 90% 
 +
 +</code> 
 + 
 +  * ''masks'' — многострочный список масок (fnmatch). 
 +  * ''threshold'' — порог на набор; единственный источник истины по порогу. 
 +  * ''ttl'' — необязательный срок годности метки (напр. ''30d'', ''60d'', ''90d''). По истечении узел требует повторной проверки, т.к. IP в CDN ротируются. Задаётся на набор. 
 +  * ''filter'' — необязательное поле. Обычно опускается, потому что фильтр по AS задаётся на шаге 1 (в ''SELECT''). Указывается только когда конкретному набору нужен доп. фильтр, отличный от выборки шага 1 (см. наборы Google, где ''Host AS'' задан пофакторно). 
 + 
 +Пример с фильтром на уровне набора (наборы Google): 
 +<code> 
 +SET google_play { 
 +  masks: 
 +    play.googleapis.com 
 +  filter: host_as IN (15169)          // основная AS Google 
 +  threshold: 50% 
 +
 +</code> 
 + 
 +==== RULE — правило классификации (шаг 3) ==== 
 +Развёрнутая форма: ссылается на объявленный ''SET'', порог берётся из набора. 
 + 
 +<code> 
 +RULE instagram { 
 +  for ip in RAW 
 +  match SET instagram 
 +  if match_share_by_records > instagram.threshold 
 +  then LABEL (ip:443) -> instagram 
 +
 +</code> 
 + 
 +  * ''for ip in RAW'' — перебор IP из сырого netflow (записи с непустым host). 
 +  * ''match SET instagram'' — сверка host каждой записи с масками набора. 
 +  * ''match_share_by_records'' — доля по числу записей (единственная метрика в этом алгоритме). 
 +  * ''LABEL (ip:443) -> instagram'' — присвоение метки узлу. 
 +  * Если у набора задан ''filter'', он применяется здесь же: записи вне фильтра не идут ни в числитель, ни в знаменатель. 
 + 
 +==== DISCOVER — выделение набора (обратный ход) ==== 
 +Не присваивает метку; возвращает распределение FQDN на IP, из которого строятся ''masks''. Соответствует разделу «Выделение и дополнение сетов». 
 + 
 +<code> 
 +DISCOVER SET candidate 
 +  FROM  RAW where host_ip = "35.241.31.20" and host is not null 
 +  GROUP BY host 
 +  RANK  BY records                     // по числу записей 
 +</code> 
 + 
 +===== Планируемое ===== 
 + 
 +  * Автоматизированные инструменты для рутинных выгрузок (top hosts, raw flow по IP). 
 +  * Discovery новых сетов и источников такого типа трафика — пополнение страниц в :services. 
 + 
 +===Не актуально (нет инструмента для выгрузки) ==== 
 +  * При выгрузке из QoE ограничивать размер через limit; запрещено превышать limit 1000. 
 +  * При выгрузке RAW netflow не выставлять большие временные диапазоны (несколько минут), это сильно нагружает систему. 
  • projects/qoe_analytics/unknown_discovering/start.1785237472.txt.gz
  • Last modified: 2026/07/28 13:17
  • by evgeniy