
Оглавление
Введение
Для людей, кто знаком с термином DPI (Deep Packet Inspection), в большинстве случаев возникает неприятная ассоциация: блокировки, регуляторы, цензура, закручивание гаек, и всё в таком духе. На самом деле, DPI – это только название технологии, суть которой заключается в глубоком анализе трафика.
Сразу стоит оговориться, DPI смотрит на трафик «в разрезе» (inline), то есть для анализа он использует весь пакет (все уровни модели OSI), а не только полезную нагрузку (например HTTP слой, который, к слову, почти всегда зашифрован для стоящего в разрыве анализатора, если конечно он не занимается подменой сертификатов). Возвращаясь к тому, почему к DPI не стоит относиться плохо, просто потому что он:
в том или ином виде используется во всех решениях по информационной безопасности (NTA, NGFW, Network Monitors, NPF, и т. д.);
помогает системным администраторам мониторить события в сети;
позволяет мобильным операторам создавать выгодные тарифы для абонентов (ведь для этого требуется классификация трафика, чтобы не учитывать трафик, принадлежащий допустим мессенджерам, если по тарифу они безлимитные)
балансировать нагрузку на сеть, предоставляя абонентам в среднем равную скорость в местах с высокой загрузкой (например, футбольный матч или концерт – огромная нагрузка на соты, и лучше всем дать равные возможности мессенджеров и сёрфинга, при этом урезая скорость на условный YouTube).
В сети оператор DPI должен уметь не только классифицировать сетевые потоки, но и авторизировать абонента, получать политики и следить за их исполнением (урезать скорость, блокировать запрещенные ресурсы, ограничивать доступ только до внутренних сервисов, когда баланс на нуле). Задача классификации трафика является фундаментальной для полного DPI решения и относится к отдельному классу DPI Engine. Это связано с тем, что сервисы постоянно эволюционируют – меняются названия (пример Twitter -> X), появляются новые доменные имена, внедряются CDN, разрабатываются новые подходы к оптимизации работы сервиса (допустим голосовой или видео вызов), переходят на использование новых протоколов. На основе этого формируется отдельный сегмент задач, который не зависит от типа сети (Mobile core, ISP, Wi-Fi AP, и т. д.), но при этом, в каждой из них решение этих задач необходимо. По этой причине, глубокий анализ трафика и классификация сетевых потоков происходит именно в решениях класса DPI Engine.
Inline и Mirroring
DPI обычно имеет две схемы включения: в разрыве (inline) и зеркалирование (mirroring). Inline включение подразумевает, что DPI расположен между клиентом и внешней сетью и должен принимать решение о прохождении трафика в реальном времени, без отложенной обработки. В то же время при режиме зеркалирования, DPI принимает не реальный трафик, а его копию. С использованием зеркалирования DPI также может влиять на поведение сессии (например, разорвать её посылкой TCP-RST пакета), но при этом такое вмешательство не требует задержки всех остальных пакетов потока.
Что такое сетевой протокол, пакет, слой?
Для того чтобы описать как работает классификация трафика, нужно ввести базовые понятия. Для знающих людей они наверняка знакомы, но тем не менее.
Пакет (Packet) – это набор байт, сформированный определенным образом, полученный/переданный на сетевом интерфейсе.
Протокол (Protocol) – набор правил, следуя которым данные передаются между различными узлами в сети. Протоколы также описывают правила того, как данные должны быть сформированы, как обработаны, в каком порядке переданы, и так далее. Разные протоколы играют разные роли в передаче данных и отвечают за разные функции.
Слой (Layer) – часть сетевого пакета, относящаяся к определенному протоколу. Данная часть (набор байт) не может относиться более чем к одному протоколу.
Поле (Field) – часть слоя (набор байт), хранящая информацию для заранее известных нужд. Например, в IPv4 слое, который занимает 20 байт (без Options), по смещению 12 байт находится адрес отправителя (размером 4 байта), а по смещению 16 байт находится адрес получателя (также размером 4 байта).

OSI
OSI (Open Systems Interconnection model) – иерархичная, многоуровневая модель сетевых протоколов, каждый уровень которой имеет свою роль в рамках передачи данных.

Схема была взята здесь.
Немного упростив формализованные определения слоев OSI модели, можно сказать следующее:
Протоколы сетевого уровня, отвечают за передачу данных основываясь на IP адресах получателей.
Транспортные протоколы отвечают за то, чтобы при достижении данных конечного сервера, нужное приложение смогло эти данные обработать (основываясь на портах). То есть «навигируют» данные уже не между узлами в сети, а между приложениями на устройстве.
Протоколы уровня представления, отвечают за то, как данные будут переданы (шифрование, сжатие и прочее).
Протоколы прикладного уровня используются, чтобы формализовать данные между клиентским и серверным приложением, чтобы оба приложения смогли эти данные обработать. Например, в HTTP протоколе (уровень приложения) перед непосредственно самой порцией переданных данных (тело запроса или тело ответа) представлена информация о том, к какому ресурсу идет запрос, в каком формате представлена полезная нагрузка, используется ли сжатие, и т. д. То есть, протоколы прикладного уровня это то, что уже обрабатывают программы/сервисы на конечных устройствах.

Схема была взята здесь.
Что такое сетевой поток?
Сетевой поток (Network flow или просто flow) — это абстракция над сетевыми пакетами, чтобы разделять их по группам. Для примера можно рассмотреть сеть Интернет, которую можно представить как большое количество пакетов, передающихся между разными участниками сети. Пакеты являются минимальной единицей сетевого обмена. И если пакет — это более физическая абстракция (информация, набор байт, передающийся по каналу связи), то поток скорее логическая сущность, помогающая распределять пакеты по группам. Например, если IP адрес отправителя и получателя совпадают, то это IP поток. Или другой пример: когда совпадают не только IP адрес отправителя и получателя, но еще и TCP порты. Такой поток можно отнести к отдельному TCP соединению. TCP соединений (и не только TCP) между отправителем и получателем может быть несколько. Таким образом, хаос из пакетов внутри сети приобретает более структурированный вид, когда пакеты разделены по группам. Для лучшего понимания легче представить поток как трубу, в которой летают пакеты. От адресата к получателю летят по одной трубе (прямой поток), а обратно по соседней (обратный поток). То есть в данном примере, труба является примером группировки пакетов.
Важно различать сетевой поток и поток исполнения. Сетевой поток – flow, поток исполнения – thread.

Пакеты нужно разделять по потокам по многим причинам. Например, для ведения статистики (количество обработанных пакетов, байт, временных меток (timestamps), и т. д.), которая может быть полезна для классификации трафика или для шейпинга (ограничение скорости).
Прямой и обратный поток связываются между собой (ссылаются друг на друга), образуя сессию. Сессия – это набор общей информации для связанных потоков. Например, для двух TLS потоков (образующих сессию) после завершения рукопожатия такие параметры как supported_version, application_layer_protocol_negotiation, session_id, tls_cipher_suite, compression_method являются общими для обоих потоков.
На схеме 4, представлен контекст сессии как общей сущности, которая объединяет информацию характерную для обоих потоков.
Когда пакет поступает на обработку, DPI Engine должен соотнести пакет к потоку. Для этого нужно рассчитать ключ/идентификатор потока на основе данных из этого пакета. Ключ рассчитывается по-разному, в зависимости от структуры пакета. Потоки бывают разных типов, но наиболее распространённые:
Тип | Описание | Ключ |
Tuple3 | IPv4/IPv6 фрагментированный поток | { src_ip, dst_ip, id } |
Tuple5 | IPv4/IPv6 поток с привязкой к портам и транспортному протоколу | { src_ip, src_port, dst_ip, dst_port, protocol } |
Tuple6 | Тоннельный поток (VLAN-C-TAG, GRE, …) | { src_ip, src_port, dst_ip, dst_port, protocol, tag } |
То есть чтобы рассчитать ключ, нужно добраться до IP слоя, извлечь IP адреса, проверить, что поток не фрагментирован (проверка offset-ов и MF флага). Если пакет IP фрагментирован – взять поле ID и собрать Tuple3 ключ. Если не фрагментирован – дойти до следующего за IP слоя, извлечь порты (если это транспортный слой, и они есть) и собрать Tuple5 ключ.

На схеме выше поле идентификатор представлено, чтобы можно было ссылаться на его значения дальше по тексту. На деле поле key является единственным ключом для поиска.
Выше были рассмотрены виды потоков с группировкой по типу ключа. Такой вид типизации полезен, чтобы контролировать контекст потока (буферизировать данные, вести статистику, и т. д.), но это не единственное существующее разделение потоков на виды. Потоки могу разделяться на Control Plane и User Plane (или Data Plane).
Control Plane поток – сетевой поток, в котором ходят пакеты, содержащие «управляющую информацию». Под управляющей информацией стоит понимать информацию, которая не относится к передаче пользовательских данных, а служит для того, чтобы согласовать условия передачи и подготовить данные.
Для примера, можно рассмотреть FTP протокол. После установления TCP соединения, пользователь вводит учетные данные, ходит по каталогам, запрашивает размер файлов – всё это относится к управляющей (служебной) информации (Control Plane). В то же время когда пользователь решает скачать какой-то файл, в рамках FTP сессии происходит обмен сообщениями, где анонсируется сокет на стороне сервера, к которому клиент должен обратиться, чтобы получить этот файл. Затем клиент делает обращение к этому сокету и происходит скачивание файла, но уже в соседнем потоке. Этот поток уже будет являться User Plane.
Направление потоков
Направление потока принято разделять на клиент-сервер (Client-To-Server; CTS) и сервер-клиент (Server-To-Client; STC). Для того чтобы понять, какая сторона является клиентом, нужно определить, кто инициировал соединение. Например, если пакет содержит TCP слой только с флагом SYN, то это первый пакет в сессии, и его инициатором является src_ip:src_port сокет, а направление пакета Client-To-Server. Перевернув IP адреса и порты, получим сокет для направления Server-To-Client.
На схеме 5 отображены прямой и обратный потоки – идентификаторы 1 и 2. То есть, адрес 192.168.1.33 является сервером с запущенным на нем SSH службой на порту 22.
Что такое Uplink/Downlink и почему это не одно и то же что CTS/STC?
В сетевых технологиях также применяются понятия Uplink и Downlink для сетевых интерфейсов. Таким образом, все пакеты, снятые с интерфейса Uplink, всегда принадлежат абоненту, а все, которые приняты с Downlink – внешней сети. Вроде бы кажется, что направление потока всегда легко определить, исходя из того, с какого интерфейса был снят пакет (снят с Uplink – CTS, с Downlink – STC), но на самом деле это не совсем верно. Допустим, абонент развернул у себя мини-сервер (а на нём какое-нибудь файловое хранилище с фильмами), запросил статический IP адрес у провайдера, поехал на 2 недели в отпуск и из условной Грузии заходит на свой сервер. Таким образом, для DPI провайдера трафик от пользовательского сервера к пользователю (STC) будет Uplink, а от пользователя к серверу (CTS) – Downlink.

На схеме 6 представлено два устройства, которые подключены к точке доступа (домашний роутер), которые в свою очередь связаны с ISP (Internet Service Provider) уже по проводу. Весь трафик, приходящий от планшета и ноутбука, для роутера и ISP, это Uplink. А весь трафик, идущий к этим устройствам, – Downlink.
Что такое reassembling?
Пакеты в сети можно сравнить с вагончиками, которые таскают уголь. Но не всегда в вагон влезает все, что требуется отправить. Например, из точки А в точку Б надо отправить 180 тонн угля, а вагон вмещает только 60. То есть нужно 3 вагона, чтобы доставить весь необходимый объем. Примерно то же самое и происходит в сети, например когда пользовательский браузер формирует HTTP запрос к YouTube, который, предположим, имеет размер 2048 байт. Данный запрос делегируется для отправки в TCP стеку ядра ОС, где ядро берет на себя обязательства доставить запрос до сервера YouTube. Запрос может быть отправлен сразу в одном пакете, а может быть разбит на сегменты и отправлен частями. Это называется TCP сегментация, а процесс пересборки сообщения – reassembling.
Reassembling является неотъемлемой частью работы DPI. Для того чтобы извлекать доменные имена из пакетов/сообщений, расшифровывать полезную нагрузку, нужно иметь на входе собранный пакет/сообщение, а для этого нужно отловить все его сегменты, склеить их и только затем проанализировать.
Сегментация/Фрагментация бывает на разных уровнях модели OSI. Наиболее распространенные протоколы, где она используется это:
IPv4/IPv6
TCP
DTLS
OpenVPN
QUIC
HTTP/2
Следует заметить, что если в сети работает IPv4/IPv6 фрагментация, то задача для reassembler-а усложняется, потому что ему сначала приходится собирать сообщение на уровне IP (сетевой уровень), а уже только потом собирать сообщение на транспортном уровне, и, если это необходимо (бывают такие протоколы), то и на уровне приложения.

На схеме 7 показан очень упрощенный пример сборки сообщения (без порядковых номеров, размера пакета, пакетов, подтверждающих получение, и пр.). На стадии 0 видно, что с устройства пользователя уходит 4 TCP сегмента на AP (Access Point), но уже с AP уходят только 3 сегмента. 1 сегмент задержался по какой-то причине. В свою очередь, ISP тоже в силу каких-то причин смог сразу передать только два сегмента на DPI. DPI установлен в разрыве (inline), то есть ему нужно в режиме реального времени принимать решение о пропуске трафика. Но информации, полученной по этому потоку недостаточно, чтобы принять решение о том, пропускать пакеты во внешнюю сеть или нет.
На стадии 2 AP досылает 1-ый сегмент на ISP, а ISP досылает застрявший у него на предыдущем шаге 3-ий сегмент. И снова получается ситуация, при которой DPI не может вынести вердикт для данного потока: пропускать или не пропускать.
На стадии 3 DPI в конечном итоге получает недостающий 1-ый сегмент и уже может проанализировать сообщение целиком, после чего принять решение по потоку. Ресурс вполне легитимный – пропускаем.
Как reassembling может влиять на классификацию трафика?
Сегментирование – один из самых простых способов повлиять на классификацию трафика (после цирка с регистром в запрашиваемом доменном имени, например, YoUTubE.COM). Почему это срабатывает? Потому что если пользователь выставит для своей ОС размер сегмента в 1 байт, а запрос к ресурсу имеет размер 2048 байт, то выходит, что DPI в худшем случае классифицирует поток на 2048-м пакете (мы рассматриваем пример, когда Hostname присутствует в пакете в незашифрованном виде). На деле это произойдет быстрее: как только DPI доберется до значений server_name (TLS протокол) или host/authority (HTTP/HTTP2). Пусть это будет 100-й байт для примера. То есть на 100-м пакете DPI поймет, что это поток к сервису YouTube. Что происходит внутри DPI: engine, пакет за пакетом, складывает байты сообщения в буфер и пытается извлечь из них доменное имя. Это очень накладно для таких высоконагруженных систем как DPI. То есть мало того, что размер буфера заранее неизвестен и его приходится «растягивать» (ну или сразу выделять размер с запасом). Что не просто влияет на производительность, так еще в довесок приходится на каждый пакет перепроверять буфер на предмет того, не появился ли в нем уже наконец hostname. Также буферизация данных влияет на потребление памяти, что весьма существенно для таких систем как DPI. Поэтому чтобы сохранить систему работоспособной, DPI должен себя балансировать: иметь лимит на буферизацию и лимит на количество пакетов, после которого поток помечается как неклассифицированный (для него назначается одна из политик по умолчанию, а буферы сбрасываются).
Другой разговор, что уже выделяются целые кластеры IP адресов для крупных сервисов, и тут доменное имя может не иметь ключевое значение, а классификация произойдет по IP адресу.
Что такое сервис?
В предыдущих разделах уже были использованы термины «сервис» и «протокол». Но нужно немного прояснить эти понятия, чтобы всё стало совсем понятно.
Сервис – это программа, которая работает на сервере (или виртуальной машине), принимает входящие соединения от пользователей, отдает/принимает данные. Например, Telegram это сервис для мгновенного обмена сообщениями (IM), YouTube – это медиа сервис для загрузки и просмотра видео контента, и т. д. У сервиса нет RFC, в отличии от протоколов. То есть сервис – это программа, которая принимает и отдает данные. И не важно в каком формате (JSON, XML, и т. д.).
Если главная цель сервиса — это обмен данными таким образом, чтобы пользователь смог их прочитать (наоборот тоже верно), то роль протоколов сводится к тому, как эти данные доставить или представить.
Например, протоколы IPv4/IPv6 ответственны за передачу данных между серверным оборудованием: маршрутизаторами и/или серверами. Данные передаются от одного устройства к другому, основываясь на IP адресах.
Коммутаторы
Есть еще коммутаторы, которые ответственны за передачу данных между разными сегментами сети (к другим коммутаторам), но они это делают на базе VLAN-ов (числовых идентификаторов), а не на базе IP адресов. VLAN-ы не играют роли при классификации трафика в DPI Engine.
Транспортные протоколы (TCP, UDP, QUIC) ответственны за передачу сообщения от клиента к серверу на уровне программ. Также выбор транспортного протокола определяет, насколько критична потеря данных. Например, TCP протокол гарантирует доставку сообщения, а UDP не гарантирует. Чтобы лучше понять, что значит «передача на уровне программ», рассмотрим пользователя (с IP адресом 192.168.12.22), который открыл на телефоне приложение WhatsApp и YouTube. Так вот, на смартфоне пользователя открываются два порта (пусть это будут 4444 и 5555). Теперь пакеты идут от пользователя к серверу WhatsApp с порта 4444, а на YouTube с порта 5555. И когда мобильный телефон пользователя получает пакет, в котором порт получателя (destination port) указан 4444, ОС смартфона отдает полученные данные приложению WhatsApp. Аналогично с портом 5555 для YouTube.

Протоколы SSL/TLS ответственны за шифрование данных, верификацию сервера (и/или клиента, если это необходимо). То есть обеспечивают безопасность передачи данных.
Протоколы HTTP/HTTP2 (и прочие протоколы уровня приложения) ответственны уже непосредственно за передачу исходного сообщения. В частности, протоколы HTTP/HTTP2 берут на себя обязанность сформировать исходное сообщение так, чтобы каждый из участников соединения (клиент или сервер) смогли его прочитать. Например, в HTTP запросе есть поле URI, которое помогает сервису понять, какие именно данные запрашивает пользователь. Или, например, HTTP имеет заголовок Content-Type, который указывает тип передаваемых данных (text/html, audio/mpeg, image/gif, …).

На схеме выше отображено то, как срезаются слои пакета, перед тем как приложение получает данные. Пакет приходит на NIC (Network Interface Card), где срезается Ethernet слой, и данные передаются ядру ОС. Ядро уже проверяет прослушиваемые сокеты (ip:port). Если установлено приложение, которое ожидает данные по этому сокету, то срезаются сетевой и транспортный слой. Далее данные передаются приложению.
Подводя итог, сервис – это программа. Протокол – способ передачи данных или представления данных.
Более одного сервиса на одном сервере
Вполне обыденная ситуация, когда на одном сервере работает множество сервисов. Например, пользователь арендовал себе виртуальную машину с IPv4 адресом 90.156.176.56 и развернул на нем YouTrack и GitLab. Купил домен (privatezone.com), SSL сертификат, настроил DNS записи. Получается, что два сервиса на одном IP адресе. Если запускать сервисы с параметрами по умолчанию (HTTP порт 80, TLS 443), то после запуска первого, не запустится второй (и наоборот), потому что порты по умолчанию уже заняты. Тогда нужно запустить сервисы на разных портах. Для примера: YouTrack 9000 и GitLab 9001. Теперь, когда пользователь вобьет адрес privatezone.com в браузер, он увидит ошибку, потому что на сервере не запущена программа на порту 443 (TLS) или 80 (HTTP), к которым браузер по умолчанию формирует запрос. Чтобы запросить YouTrack, нужно вбить privatezone.com:9000, а GitLab - privatezone.com:9001. Это весьма неудобно – запоминать порты для каждого сервиса. Поэтому нужно дополнительно добавить в DNS две записи, которые будут вести на этот же IPv4 адрес (90.156.176.56), но при этом из названия домена должно быть понятно, к какому сервису происходит обращение. Например, в нашем случае DNS записи будут выглядеть вот так:
Домен | Поддомен | Тип | Адрес |
privatezone.com | @ | A | 90.156.176.56 |
privatezone.com | gitlab | A | 90.156.176.56 |
privatezone.com | youtrack | A | 90.156.176.56 |
Таким образом, когда пользователь запросит в браузере gitlab.privatezone.com, браузер отправит TLS с указанием домена gitlab.privatezone.com в поле server_name (SNI). Но сервер по-прежнему ничего не ответит, так как никакой программы на сервере на порту 443 не запущено. Для того чтобы сервер маршрутизировал входящие соединения между YouTrack и GitLab, нужно установить Nginx (или любой другой веб-сервер) и сконфигурировать его таким образом, чтобы запросы по адресу youtrack.privatezone.com он переводил на YouTrack (127.0.0.1:9000), а gitlab.privatezone.com – на Gitlab (127.0.0.1:9001). 127.0.0.1:9000 и 127.0.0.1:9001 означают, что запрос маршрутизируется на локальной машине (127.0.0.1) на порт 9000/9001, где уже обрабатывается нужным приложением.

Из вышеописанного примера становится понятно, что если на сервере/виртуальной машине запущен не один сервис, то классификация по IP адресу не может быть выполнена. Обязательно необходима проверка доменного имени для определения сервиса. Это важно понимать. Например, если условный Google держит большой пул IP адресов, и не найдено (не получено от сервиса) информации что, например, пул 142.250.221.0/24 зарезервирован исключительно для сервиса Gmail, то классификация по IP адресу невозможна и нужна дополнительная проверка доменного имени (если конечно не рассматривать корпорацию Google, как сервис). Google может развернуть в этом пуле YouTube, Cloud и так далее без всякого предупреждения, что сломает IP классификацию.
Классификация трафика
Классификация сетевого потока – это определение того, к какому сервису он относится. Классификация складывается из следующих этапов, некоторые из которых могут быть пропущены в зависимости от протокола или техники классификации:
Определение протоколов, представленных в пакете
Диссекция полей, необходимых для классификации (IP адреса, порты, доменное имя, и пр.)
Пересборка или reassembling (IPv4, TCP, QUIC, HTTP/2, и т. д.)
Распаковка (decompression) полей (например, DNS Name состоит из label-ов)
Дешифрование или decryption (например, QUIC Initial)
Применение техник для определения сервиса сетевого потока
Применение техник для классификации характера сетевого потока
Методы для определения протокола
С точки зрения пользователя, который развернул у себя на сервере OpenVPN и Nginx всё просто: он знает, что на 1194 порт он отправляет/получает OpenVPN, а на 443 запрашивает свой сайт визитку. То есть OpenVPN сервер (openvpn-server), который крутится на сервере пользователя на 1194 UDP порту, точно знает, что данные в пакете сформированы согласно спецификации OpenVPN. Если данные не соответствуют этой спецификации, то их обрабатывать не нужно (нужно оповестить об ошибке или просто проигнорировать). Для openvpn-server всё просто, но не для сетевого анализатора.
Протоколы в пакете можно представить как кирпичики разного цвета, идущие друг за другом. Определить цвет следующего кирпичика, может быть легко: допустим, когда в первом синем кирпичике сказано, что следующий за ним красный. Этот пример применим к таким протоколам, как Ethernet, VLAN, IPv4, IPv6 и т. д. В одном из полей таких протоколов есть указание, какой протокол идет за ним. Сложности возникают при определении протокола, следующего за транспортным (TCP/UDP). Протоколы UDP и TCP не несут в себе информацию о том, какой протокол идет следующим, поэтому определение следующего протокола возможно только эвристически.
Можно выделить следующие способы определения протокола в пакете:
Explicit – следующий протокол заранее известен (явное определение)
Port-based – подсказка, какой протокол наиболее вероятен
Patterns – паттерны или маркеры, появление которых указывает на принадлежность к определенному протоколу. Примеры протоколов, которые можно определить по паттернам: HTTP, SSDP, SIP, и некоторые другие
Try-dissect – проверка структуры протокола (размер сообщения, значения полей, и пр.). Могут быть коллизии с другими протоколами, если структура сообщений между ними похожа.
Session correlation – метод анализа пакетов, при котором отслеживаются управляющие потоки, содержащие анонсы новых соединений. Эта информация кешируется и используется для точного определения протокола в последующих потоках, принадлежащих одной логической сессии, что помогает избежать коллизий.
Логично предположить, что определить протокол достаточно легкая задача: нужно просто взять порт и на его основе уже разбирать следующий протокол. 80 – HTTP, 443 – TLS, и так далее. На самом деле такое предположение ошибочно, и порт служит лишь указанием того, какой протокол стоит проверить (на паттерны или структуру) первым. Если полагаться только на порт, то сервисы, которые работают на нестандартных портах, будут определяться неправильно. Например, представим, что QUIC по UDP едет к серверу не на 443 порт, а на 1194 (порт по умолчанию для OpenVPN протокола). В таком случае DPI попытался бы разобрать QUIC как OpenVPN, потерпел бы неудачу и пометил бы протокол как нераспознанный. Простая подмена порта влияла бы на определение протокола и, как следствие, на классификацию сервиса (из первого QUIC сообщения можно собрать фрагменты, дешифровать их, получить TLS слой и извлечь SNI).
Дополнительную сложность представляют протоколы с высокой энтропией данных, например такие как OpenVPN или RTP/SRTP. В таких протоколах совсем небольшой объем байт, которые можно проверить на соответствие структуре протокола. Большая же часть сообщения таких протоколов — это полезная нагрузка, которую бесполезно анализировать, так как она не представляет из себя хоть что-то прогнозируемое. Для таких протоколов обычно требуется больше, чем один пакет, чтобы иметь возможность сравнить два или более сообщения и установить корреляцию по определённый полям или просто удостовериться, что структура протокола для второго пакета уже не соответствует протоколу, который DPI Engine предполагал для первого пакета (т. е. метод исключения).
Для некоторых протоколов, (в частности, для RTP/SRTP) можно применить подход, основанный на корреляции сессий. Дело в том, что RTP потоки стартуют после того, как сессия была проинициализирована между клиентом и сервером. Такая инициализация проходит с использованием протокола SIP, который является HTTP-like. SIP легко определить по паттернам, извлечь из него информацию о предстоящих сессиях и закешировать. После этого, когда DPI Engine сталкивается с трудностью определения протокола (например, первый пакет в потоке похож на RTP и OpenVPN), он может проверить свой кеш на предмет предстоящих сессий и если ожидалась RTP сессия, то сразу выставить для пакета RTP как финальный протокол. Таким образом, не нужно будет ждать второго пакета по потоку для устранения коллизии.

Таким образом, для DPI Engine важно не только уметь определять протокол, зная только один тип сообщения (допустим TLS Client Hello), который влияет на итоговую классификацию сервиса. Для него важно знать структуру каждого типа сообщения, поддерживаемого протоколом (например, TLS Alert, Change Cipher Spec, Application Data, и далее по списку), по той причине, что умение распознать любой тип сообщения для протокола уменьшает риск false positive и увеличивает шанс определения протокола с первого пакета. Другими словами, чем больше протоколов поддержано и чем большее количество типов сообщений для этого протокола умеет разбирать DPI, тем надежнее классификация.
Также бывают вырожденные случаи, когда с логической точки зрения определение протокола уже дело решенное, а на самом деле нет. В качестве примера можно привести ICMP с полезной нагрузкой. Дело в том, что протокол ICMP достаточно простой, важный, разрешен в любой сети (ну или почти в любой) и является финальным (в идеальном мире), но при этом позволяет передавать полезную нагрузку. Учитывая это, полезную нагрузку для ICMP пакета не анализируют должным образом (в принципе правильно – зачем нагружать систему для того, что в 99% случаев не нужно), а там может быть ICMP тоннель. На хабре о таком виде тоннелей можно почитать здесь.
Методы классификации интернет-сервисов
DPI Engine использует различные техники для классификации сервисов. Главная цель классификации – как можно скорее вынести вердикт о сервисе, чтобы сетевой поток мог быть выгружен (offload), что снизит нагрузку на ПО (пакеты по этому потоку больше не будут анализироваться). Даже существует понятие FPC (First Packet Classification), но не все техники могут гарантировать FPC. Ниже приведены наиболее популярные подходы для классификации:
IP Database
Domain Name Patterns
Data Patterns
Data Structure
Cache
Domain Fronting
Tag Chain
SPID (Statistical Protocol IDentification)
ML/AI
Съёмщик трафика/Пакетный фильтр
DPI не только анализирует трафик, но также пропускает, ограничивает скорость или блокирует сетевые пакеты. Пакеты с сетевого интерфейса захватываются отдельным DPI модулем, который называется съёмщиком трафика или пакетным фильтром. Такой модуль обычно расположен на уровне ядра ОС (Kernel space). Именно он ответственен за ограничения, накладываемые на поток. Популярные библиотеки для захвата трафика: DPDK, PF_Ring.
До момента, пока поток не был классифицирован, пакеты потока поступают на анализ в DPI Engine, код которого обычно исполняется в User space. После того как поток был классифицирован, анализ пакетов более не имеет смысла – нужно просто вести статистику, чтобы после завершения потока, отправить эту статистику на биллинг. Для этого съемщику трафика отправляется политика, (указания, что делать с пакетами потока: пропустить, ограничить скорость, заблокировать), после получения которой, съёмщик трафика больше не кладёт в очередь на обработку пакеты по этому потоку, а просто ведет статистику по нему или отбрасывает пакеты, если поток был заблокирован.
IP Database является достаточно базовым сценарием для классификации. Особенно для ситуаций, когда целый пул IP адресов зарезервирован за сервисом. В качестве примера можно привести Telegram, который публикует список CIDR на своем сайте. То есть если IP адрес пакета попал в интервалы из этих CIDR, то можно вынести вердикт, что это Telegram.
Domain Name Patterns также является стандартным сценарием для классификации. Если удается извлечь имя хоста из пакета (характерно для таких протоколов как HTTP, HTTP/2, SSDP, SIP, TLS, QUIC+TLS), то DPI Engine проверяет по списку доменных имен и выносит свой вердикт. Примеров для такого метода великое множество. Например, для того же Телеграма, можно выделить как минимум три домена: telegram.org, t.me, telegram.me.

Data Patterns – метод, который анализирует специфичные части полезной нагрузки, для которой не был определен финальный протокол. Например, в пакете удалось определить такую цепочку протоколов: ethernet.ipv4.tcp.payload. payload означает что финальный (в данном случае прикладной) протокол определить не удалось. Поэтому в таком случае подключается анализатор полезной нагрузки, который проверяет отдельные ее части, чтобы вынести вердикт. В качестве примера можно привести поиск «магических чисел» (Magic Numbers) в теле полезной нагрузки по специфичным для них смещениям.

Data Structure – метод по своему смыслу очень похож на метод определения протокола (не сервиса) Try-Dissect, который верифицирует структуру полезной нагрузки в соответствии со спецификацией этого протокола. Но бывает так, что сервисы используют свои собственные протоколы и не делятся их спецификацией и/или реализацией с миром. В таком случае исследователям (или ИИ) приходится анализировать снятые дампы для таких потоков с целью выявления каких-то закономерностей. Например, первый байт/два байта указывают длину пакета, при этом размер сообщения колеблется в диапазоне 124–256. Или, при размере пакета от 130 до 180, первый байт указывает длину сообщения, а 24-й байт лежит в диапазоне от 0–8. И так далее. Такая техника используется не часто и является не очень надежной. Любое изменение протокола на стороне сервиса нигде не анонсируется и может сломать классификацию. Более того, если протокол был изменен, требуется его повторный анализ исследователями, а затем изменения в коде (в отличие от классификации на основе IP адресов или доменных имен), что отнимает много времени.
Cache – один из наиболее часто используемых способов классификации, после базовых (IP, Domain Names). Суть его заключается в том, чтобы сохранить информацию из одного потока, чтобы помочь классифицироваться другому. Существует несколько видов кеширующих техник, например:
DNS Cache
TLS/DTLS Session ID Cache
QUIC Cache
В качестве примера рассмотрим DNS Cache. DNS Cache даёт возможность выполнить FPC, то есть сервис будет классифицирован с первого пакета. Суть DNS Cache подхода в том, чтобы сохранить полученные IP адреса из DNS ответа для определенного сервиса конкретного пользователя. Перед обращением к ресурсу по доменному имени, в большинстве случаев от клиента сначала отправляется DNS запрос и получается DNS ответ со списком IP адресов для запрошенного домена. Таким образом, DPI Engine анализирует DNS ответ, проверяет доменное имя и, если оно ему интересно (то есть доменное имя есть в списках для классификации и привязано к конкретному сервису), то кеширует список полученных IP адресов. После этого от клиента ожидается новое соединение к одному из этих IP адресов, и, если оно появляется, DPI Engine выносит вердикт о классифицируемом сервисе.

Если коротко затронуть TLS/DTLS Session ID Cache, то такая техника используется для того, чтобы контролировать Connection Migration (для TLS называется TLS Session Resumption). Миграция TLS соединения – это сценарий, в котором при первом соединении в TLS Client Hello присутствует SNI, а в повторном соединении – нет. То есть клиент запрашивает youtube.com в TLS Client Hello при первом соединении, сервер отвечает ему Server Hello, в котором есть поле Session ID (идентификатор соединения). Затем, по тем или иным причинам, соединение закрывается и открывается новое. Но уже в новом соединении SNI отсутствует, а клиент передает Session ID, который использовался в первом соединении, и прошлая сессия восстанавливается.

Стоит заметить, что кеширование полезно, когда сервис использует IP адрес не из заранее известного списка (IP Database).
DNS и TLS Cache – это не единственные, но наиболее часто используемые виды кеширования.
Domain Fronting – метод для обхода блокировок, который использует сокрытие домена запрашиваемого ресурса в цепочке клиент->CDN->сервер. Подробнее можно прочитать в этой статье на Хабре. Если коротко описать принцип – в мире, где почти весь трафик ходит через TLS, запрашиваемый ресурс (хост) указывается два раза: в TLS Client Hello (если не используется ECH – Encrypted Client Hello) и HTTP Host.
При таком сценарии клиентское приложение устанавливает TLS соединение с CDN сервером, но при этом домены в TLS Client Hello и HTTP Host отличаются. В TLS Client Hello фигурирует фиктивный (но легитимный с точки зрения цензора) домен, в то время как зашифрованный TLS-ом HTTP запрос направлен к домену отличному от того, что указан в Client Hello. Для наглядности можно рассмотреть следующую иллюстрацию:

Иллюстрация была взята здесь.
Как видно, клиент запрашивает незаблокированный домен у DNS сервера, адрес которого совпадает с адресом заблокированного ресурса. Затем TLS соединение легко устанавливается с сервером, так как для цензора виден только SNI, а HTTP Host домен зашифрован под TLS слоем.
Определять Domain Fronting без дешифрования TLS достаточно трудно. Дешифрование TLS может использоваться в продуктах информационной безопасности, но для телекомов это не осуществимо. По этой причине определение Domain Fronting для конкретных сервисов (то есть нужно заранее понимать, какие сервисы используют такую технику), лежит на стыке сбора “мусорных” доменов (которые являются прикрытием для основного домена) и выявления поведенческих паттернов, характерных для таких сессий.
Сбор доменов можно осуществить с помощью мониторинга CDN провайдеров, которые используются сервисом, отлавливанием TLS соединений, где в Client Hello был запрошен один домен, а сертификат от сервера возвращается с другим (характерным для исследуемого сервиса). Возможно прибегание к реверсу или полуавтоматическому сбору доменов, генерируемых таким приложением (например, написание сниффера для сбора доменов, к которым обращается приложение).
Domain Fronting стал неактуальным для разработчиков сервисов с большой аудиторией (вроде Telegram, WhatsApp, и прочих), так как крупные CDN провайдеры стали контролировать использование своих узлов, чтобы не допускать обхода блокировок через их серверы (Google, AWS, Cloudflare).
Tag Chain – подход в классификации, подразумевающий анализ уже классифицируемых для потока сервисов. Такой метод играет скорее оптимизационную роль, чем практическую, так как с его помощью «срезаются» недостижимые варианты, и их проверка не происходит. В качестве примера можно привести классификацию YouTube. YouTube принадлежит Google и использует Google CDN для доставки трафика. Таким образом, если по IP Database был классифицирован Google, а затем также по IP был классифицирован Google CDN, то значит следующий по цепочке сервис будет либо принадлежать Google, либо какой-то другой сервис, который использует Google Cloud. Это значит, что можно уже не проверять сервисы Microsoft, VKontakte, Telegram и другие, которые не пользуются Google CDN. Сокращается количество вариантов для проверки, что влияет на скорость классификации.
SPID (Statistical Protocol Identification) – метод, основанный на анализе статистических характеристик потока, например, таких как средний размер пакета, битрейт, IAT (Inter Arrival Time) и т. д. Данный метод является не очень надежным для определения сервисов и преимущественно используется, чтобы классифицировать характер потока (workflow), например, передачу файла, аудио/видео вызов и т. д.
ML/AI – метод, основанный на анализе различных характеристик потока (в том числе и статистических) и который классифицирует сервис с определенной вероятностью. Данный метод используется, когда однозначно вынести вердикт не представляется возможным. В отличии от SPID, используемые для классификации значения/интервалы могут меняться в процессе обработки трафика (модель самообучается), в то время как в SPID они всегда статические. Основная потребность в ML/AI классификации — это адаптация к изменчивости трафика. Метод используется для классификации VPN сервисов, которые невозможно определить по IP адресам или верификации протокола, а также для определения характера потока (будет описано в следующем параграфе).
Выше описаны наиболее популярные техники для классификации трафика. Существуют и другие, которые могут базироваться на достаточно специфичных критериях, характерных для отдельных сервисов (или группы сервисов), но которые требуют более глубоко анализа, например, с помощью ИИ, анализа исходного кода или реверс-инжиниринга.
Классификация характера потока (workflow)
Классификация самого сервиса дело важное, но не всегда этого достаточно. Допустим, мобильный оператор может использовать скорость передачи файлов в мессенджерах как конкурентное преимущество для одного из своих тарифов. Другими словами, возникает потребность установить, какой работой поток занимается (workflow). Можно выделить несколько самых популярных типов такой работы:
Чат
Аудио вызов
Видео вызов
Передача файла
Такого рода классификация обычно базируется на анализе статистических характеристик потока (SPID).
Классификация на основе статистики весьма чувствительна к изменению в работе сервиса (например, выпуск новой версии). Можно, например, рассмотреть ситуацию, когда какой-либо мессенджер начал использовать новый кодек для аудио, исходя из чего, статистические метрики изменились, что повлияло на классификацию.
Есть ещё такое понятие как «Комфортный шум», который также может в отдельных ситуациях влиять на определение workflow.
Как бы то ни было, workflow весьма полезная информация как для телеком решений, так и для продуктов в области информационной безопасности. В качестве примера можно привести сценарий, при котором абонент, имея активный голосовой вызов, открывает сессию в какой-либо интернет-банк. Интересная связка! Или другой пример: при расследовании инцидента, можно увидеть, что на участвующем во вторжении устройстве, было сделано несколько аудио вызовов, потом принят файл, а через N минут устройство было заражено.
Почему сложно?
Главные критерии, предъявляемые к продуктам класса DPI Engine: качество и скорость классификации. Качество особенно критично для классификации сервисов и протоколов, которые подлежат блокировке. Неверная классификация таких сервисов (или точнее ее отсутствие) может привести к штрафам со стороны регуляторов.
Производительность важна, так как нагрузка в сетях, где используется DPI обычно весьма велика, и анализ каждого пакета в потоке не представляется возможным. Поэтому цель – как можно скорее вынести вердикт по сервису, чтобы новые пакеты больше не приходили на анализ и не тратили ресурсы DPI.
В вопросах производительности важную роль играет то, насколько быстро DPI Engine извлекает доменные имена, ищет совпадения в базе данных IP адресов и доменных имен, выполняет поиск по таблице потоков, чтобы восстановить контекст сессии; насколько грамотно принимает решение о том, какую информацию нужно закешировать и быстро её достаёт в нужный момент, чистит устаревшие данные в кеше, и ещё много всего другого.
Что касается качества классификации, здесь важно поддерживать в актуальном состоянии список IP адресов и доменных имен для сервисов, вовремя их обновлять, уметь выгружать закешированные данные через API, чтобы DPI мог передать их на другие кластеры в сети.
Что еще интересного делает DPI Engine?
Применение DPI Engine весьма многогранно. Можно привести следующие примеры его использования:
Attribute Extraction – извлечение атрибутов по протоколам полезно для многих целей. Например, внутри сети можно мониторить протоколы, которые не используют шифрование (FTP, HTTP, POP3 и др.), извлекать из них чувствительные данные и формировать отчеты о том, какие пользователи, какие узлы в сети и в каком процентном соотношении от общего трафика используют небезопасные соединения. Или другой пример – анализ шифров используемых в TLS соединениях (cipher_suites; Cipher Suite) и/или версий TLS для детектирования устаревших версий протоколов или уязвимых алгоритмов шифрования.
File Extraction – для незашифрованных протоколов возможна выгрузка файлов, если файл присутствует в пакете. В качестве примера можно привести выгрузку изображений из HTTP или FTP потоков. Выгруженные файлы могут пойти на дополнительный анализ, например, в DLP.
Dataset Producing – для DPI Engine можно сконфигурировать набор параметров для выгрузки (например src_ip, dst_ip, src_port, dst_port, hostname, protocol_tree, service_id, byte_count, bitrate и т. д.), после чего загрузить в него pcap дампы для анализа, а на выходе получить запрошенный набор параметров в формате JSON по каждому пакету. Такой набор может быть использован для тренировки ИИ и выявления новых метрик для классификации. Другой пример: можно генерировать схожий набор данных, только не по пакету, а по потоку, и использовать как IPDR.
Tethering Detection – определение факта использования мобильного телефона пользователя в качестве точки доступа.