1. Методология захвата данных: Работа с интерфейсами и синтаксис фильтров захвата в tcpdump
Методология захвата данных: Работа с интерфейсами и синтаксис фильтров захвата в tcpdump
Представьте, что вы подключились к магистральному маршрутизатору с интерфейсом 10 Гбит/с, чтобы отловить специфичную аномалию в TCP-рукопожатиях, возникающую раз в несколько минут. Если вы просто запустите команду захвата всего проходящего трафика с записью в файл, вы будете сохранять на диск более гигабайта данных каждую секунду. Менее чем через минуту сервер либо исчерпает свободное место, либо «захлебнётся» от нехватки производительности подсистемы ввода-вывода (I/O), так и не решив исходную задачу. Искусство сетевого анализа начинается не с умения читать дампы в Wireshark, а с хирургически точного захвата исключительно тех байтов, которые имеют значение для расследования.
Сетевые интерфейсы и барьер аппаратной фильтрации
Сетевая карта (NIC) сервера по умолчанию работает как строгий фильтр. Кадры, летящие по физической среде (медному кабелю или оптике), достигают порта, после чего контроллер проверяет MAC-адрес назначения. Если MAC-адрес в заголовке кадра не совпадает с аппаратным адресом самой сетевой карты и не является широковещательным (broadcast), контроллер молча отбрасывает этот кадр. Центральный процессор сервера и операционная система даже не узнают о существовании этого пакета.
Для анализа транзитного трафика, который проходит через коммутатор (например, при использовании SPAN-порта) или при прослушивании среды с разделяемой средой передачи, такое поведение неприемлемо. Анализатору нужно видеть всё.
Здесь в игру вступает Promiscuous mode (неразборчивый режим). При запуске утилиты захвата, такой как tcpdump, она отправляет ядру ОС запрос на перевод указанного сетевого интерфейса в этот режим. В promiscuous mode аппаратный фильтр MAC-адресов отключается, и сетевая карта начинает передавать в операционную систему копии абсолютно всех кадров, достигающих физического порта.
!Сравнение обработки пакетов сетевой картой в Normal и Promiscuous режимах
В Linux-системах утилита tcpdump позволяет явно указывать интерфейс для прослушивания с помощью флага -i.
tcpdump -i eth0 — захват на конкретном физическом интерфейсе.tcpdump -i lo — захват на loopback-интерфейсе (локальный трафик внутри самого хоста, например, от локального сервиса к локальной базе данных).tcpdump -i any — специальный псевдо-интерфейс в Linux, который собирает трафик со всех активных интерфейсов системы. Важный нюанс: при захвате с any используется специальный тип канального заголовка (Linux cooked capture, SLL), что меняет структуру первых байтов пакета и может сломать фильтры, опирающиеся на жесткие смещения MAC-адресов.Архитектура BPF: Фильтрация в пространстве ядра
Главная проблема высоконагруженных сетей — стоимость копирования данных. Когда пакет попадает в сетевую карту, он копируется в оперативную память, в пространство ядра (kernel space). Если программа-анализатор (например, tcpdump или Wireshark), работающая в пространстве пользователя (user space), захочет проанализировать пакет, ядру придётся скопировать этот пакет из своей памяти в память программы. Операция копирования (context switch и memory copy) требует процессорного времени. Если копировать миллионы пакетов в секунду, процессор будет занят только обслуживанием захвата.
Чтобы решить эту проблему, в 1992 году была разработана архитектура BPF (Berkeley Packet Filter).
Идея BPF гениальна в своей простоте: вместо того чтобы копировать все пакеты в user space и позволять программе решать, нужны они ей или нет, программа компилирует правила фильтрации в специальный байт-код и загружает его прямо в ядро ОС. В ядре работает крошечная, невероятно быстрая виртуальная машина. Она применяет этот байт-код к каждому приходящему пакету до того, как он будет скопирован в пространство пользователя. Если пакет не проходит проверку BPF, он просто отбрасывается на уровне ядра, экономя колоссальные ресурсы.
!Процесс фильтрации пакета виртуальной машиной BPF в ядре операционной системы
Именно поэтому синтаксис фильтров в tcpdump (Capture Filters) и синтаксис фильтров в строке поиска Wireshark (Display Filters) кардинально различаются. Capture Filters транслируются в низкоуровневый код BPF для ядра, а Display Filters работают уже с захваченными данными в комфортном пространстве пользователя, где можно позволить себе сложные строковые сравнения и сборку фрагментированных сессий.
Синтаксис примитивов: Построение фильтров захвата
Синтаксис BPF-фильтров строится из примитивов, которые состоят из идентификатора (имени или числа) и предшествующих ему квалификаторов. Квалификаторы делятся на три типа:
host (узел), net (подсеть), port (порт), portrange (диапазон портов). Если тип не указан, по умолчанию подразумевается host.src (источник), dst (назначение), src or dst, src and dst. По умолчанию подразумевается src or dst.ether, ip, ip6, arp, tcp, udp.Комбинируя эти квалификаторы, мы создаем базовые условия. Рассмотрим несколько примеров:
dst host 192.168.1.50 — захватить только те пакеты, где IP-адрес назначения равен 192.168.1.50.src net 10.0.0.0/8 — захватить трафик, исходящий из подсети 10.0.0.0/8.tcp dst port 443 — захватить только TCP-сегменты, направляющиеся на 443 порт.Для создания сложных логических конструкций примитивы объединяются операторами:
and или &&or или ||not или !Рассмотрим практическую задачу. Нам нужно проанализировать веб-трафик (порты 80 и 443) к нашему серверу, но мы хотим исключить из дампа весь административный SSH-трафик (порт 22), который генерируют инженеры из подсети управления 10.5.5.0/24.
Команда будет выглядеть так:
Обратите внимание на одинарные кавычки вокруг фильтра. Они критически важны при работе в терминале Linux, так как символы скобок () и амперсандов && интерпретируются командной оболочкой (bash/zsh) как управляющие символы. Кавычки заставляют оболочку передать строку в tcpdump в неизменном виде.
Хирургия на уровне байтов: Смещения и битовые маски
Базовые примитивы отлично справляются с IP-адресами и портами. Но что, если нам нужно заглянуть глубже? Например, поймать только пакеты установки соединения или пакеты определенного размера. Для этого BPF предоставляет механизм прямого доступа к памяти пакета по смещению (offset).
Синтаксис выглядит так: proto[offset:size].
proto — протокол, от начала заголовка которого мы считаем байты (например, ip, tcp, udp, icmp).offset — количество байтов от начала заголовка. Нумерация начинается с нуля.size — количество байтов для чтения (1, 2 или 4). Если не указано, читается 1 байт.Самый классический пример использования смещений — фильтрация по флагам TCP. (Детальную структуру заголовка TCP и состояния сессий мы разберем в отдельной главе, сейчас нас интересует механика работы с байтами).
Флаги TCP находятся в 13-м байте (смещение 13) от начала TCP-заголовка. Этот байт содержит 8 бит, 6 из которых отвечают за основные флаги:
Если пакет представляет собой запрос на установку соединения (только флаг SYN), значение 13-го байта в десятичной системе будет равно . Фильтр tcp[13] == 2 поймает чистые SYN-пакеты.
Однако в реальной сети пакет может нести несколько флагов одновременно. Например, ответ сервера (SYN-ACK) содержит установленные флаги SYN () и ACK (). Значение байта будет . Фильтр tcp[13] == 2 проигнорирует такой пакет.
Чтобы проверить, установлен ли конкретный бит независимо от состояния других битов, применяется побитовая операция И (bitwise AND, обозначается как &). Логика операции такова: мы накладываем «маску» на проверяемый байт. Если бит в маске равен 1, мы смотрим на соответствующий бит в байте. Если бит в маске равен 0, мы его игнорируем.
Маска для флага SYN равна (в двоичном виде ). Проверим пакет с флагами SYN-ACK (значение , или ):
Результат равен .
Проверим пакет только с флагом ACK (значение , или ):
Результат равен .
Следовательно, универсальный фильтр для захвата абсолютно всех пакетов, в которых присутствует флаг SYN (будь то чистый SYN или SYN-ACK), выглядит так:
Этот подход применим к любому протоколу. Например, тип ICMP-сообщения хранится в самом первом байте заголовка ICMP (смещение 0). Эхо-запрос (ping) имеет тип . Фильтр icmp[0] == 8 оставит в дампе только запросы ping, отбросив ответы и сообщения об ошибках маршрутизации.
Управление процессом захвата: Снапшоты и кольцевые буферы
Даже при идеальной фильтрации BPF, запись полных пакетов (которые могут достигать 1500 байт в стандартном Ethernet) быстро расходует дисковое пространство. Если ваша задача — анализ сетевой маршрутизации или факта установки соединений, вам не нужно содержимое пакета (payload). Вам нужны только заголовки (Ethernet, IP, TCP/UDP).
Параметр snaplen (Snapshot Length), задаваемый флагом -s, указывает tcpdump, сколько байт от начала каждого пакета нужно сохранить.
Команда tcpdump -s 96 -w headers.pcap обрежет каждый пакет до 96 байт. Этого с запасом хватит, чтобы вместить заголовки L2, L3 и L4, но при этом размер итогового файла дампа уменьшится в десятки раз по сравнению с сохранением полных пакетов. Значение -s 0 в современных версиях утилиты означает захват пакета целиком.
Для долгосрочного мониторинга используется механизм кольцевых буферов. Вместо записи в один бесконечно растущий файл, tcpdump может разбивать дамп на части и перезаписывать старые файлы по кругу.
-C (заглавная C) задает максимальный размер одного файла в мегабайтах.-W задает максимальное количество файлов.
Эта команда создаст файлы capture.pcap0, capture.pcap1 и так далее. Как только размер текущего файла достигнет 100 МБ, начнется запись в следующий. Когда будет записано 5 файлов (суммарно 500 МБ), tcpdump вернется к первому файлу и перезапишет его. Это гарантирует, что захват никогда не переполнит диск сервера, при этом у вас всегда будет доступ к последним 500 МБ трафика.
Понимание механизмов работы интерфейсов, архитектуры BPF и битовой математики — это фундамент. Инструменты вроде tcpdump лишь предоставляют интерфейс к этим механизмам. Логика побитовых масок и смещений, которую мы рассмотрели на примере TCP-флагов, лежит в основе написания высокопроизводительных правил для межсетевых экранов и создания первых сигнатур для систем глубокого анализа трафика (DPI), к которым мы перейдем на следующих этапах.