Если вы когда-нибудь подключали Wireshark к сегменту шины процесса реальной цифровой подстанции, то картина вам знакома: через некоторое время после старта захвата Wireshark подвисает и это известная проблема. Особенно остро эта проблема проявляется на подстанциях, где есть трафик Sampled Values.
Причина прозаична: один поток Sampled Values с частотой 4000 кадров в секунду (80 выборок за период, 50 Гц) даёт серьёзную нагрузку. В реальном сегменте шины процесса таких потоков десятки — плюс GOOSE, плюс PTP, плюс широковещательный и служебный трафик. Wireshark не столько «не справляется» с анализом — он не успевает записывать и отображать все эти пакеты в реальном времени.
В вики Wireshark на этот счёт есть прямая рекомендация:
Если вас интересуют не все пакеты, фильтр захвата, отбирающий только нужные вам пакеты, может сократить общее время обработки. Это связано с тем, что пакеты могут отбрасываться фильтром захвата ещё до записи в файл захвата, а в системах с фильтрацией захвата на уровне ядра — ещё до того, как они будут скопированы из ядра в Wireshark.
Ключевая мысль — фильтр захвата отсекает пакеты до того, как они попадут в Wireshark. В отличие от фильтра отображения, который работает уже с записанным трафиком. Поэтому если вы заранее знаете, что именно ищете, фильтр захвата — ваш лучший друг.
Фильтр захвата vs Фильтр отображения: не путайте их!
В Wireshark есть два типа фильтров, которые часто путают. Разница принципиальна:
| Параметр | Capture Filter | Display Filter |
|---|---|---|
| Когда применяется | До записи пакета | После записи пакета |
| Можно ли менять на лету | Нет (требует остановки захвата) | Да, в реальном времени |
| Синтаксис | BPF (как в tcpdump) |
Собственный синтаксис Wireshark |
| Влияние на производительность | Снижает нагрузку на диск и память | На захват не влияет |
| Что делает с данными | Безвозвратно отсекает | Просто скрывает, не удаляет |
Фильтр захвата решает, что попадёт в вашу «коллекцию» данных, а фильтр отображения — что вы будете разглядывать в уже собранной коллекции.
Для задач типа «посмотреть, а что там в сети» на нагруженном сегменте сети без фильтра захвата обойтись почти невозможно.
Как применить фильтр захвата в Wireshark
Есть три стандартных способа:
Способ 1. Стартовый экран.
На стартовом экране перед выбором сетевого интерфейса введите выражение для фильтра захвата.

Способ 2. Меню Capture.
Capture → Options → поле Capture Filter for selected interfaces напротив выбранного интерфейса.

Есть еще способы, но этих двух достаточно для начала :)
Если в фильтре есть синтаксическая ошибка, Wireshark подсветит поле красным и не даст запустить захват. Зелёный цвет означает, что фильтр синтаксически валиден — но это не гарантия, что он делает то, что вы задумали. Проверять всё равно придётся.
Несколько особенностей BPF, о которых нужно помнить
Выражения для фильтров захвата пишутся на языке BPF — том же самом, что и в tcpdump. Синтаксис компактный, но при работе с подстанционным трафиком есть пара неочевидных моментов.
VLAN ломает смещения
Ключевое слово vlan в BPF-фильтре — это не простой предикат «есть VLAN-тег». Это директива, которая сдвигает все последующие смещения в фильтре так, как будто VLAN-тег отсутствует. То есть ether proto 0x88b8 до директивы vlan обеспечивает захват нетегированных кадров, а после — тегированных.
Классическая ситуация: вам нужно поймать весь GOOSE-трафик, и часть кадров идёт с VLAN-тегом, часть — без. Интуитивный вариант
(vlan and ether proto 0x88b8) or ether proto 0x88b8
работает не так, как ожидается. Нетегированный трафик в него не попадает, потому что после vlan смещения уже сдвинуты, и вторая ветка ищет EtherType в неправильном месте.
Правильный порядок — сначала нетегированный случай, потом тегированный:
ether proto 0x88b8 or (vlan and ether proto 0x88b8)
Универсальное правило: все выражения без VLAN должны идти до первого упоминания vlan в фильтре.
Частичный MAC-адрес
Конструкция ether host xx:xx:xx:xx:xx:xx требует полный MAC. Но если вам нужно отловить, например, трафик от всех устройств производителя с OUI 00:0C:22, то приходится писать byte-offset фильтр:
(ether[0:4] & 0xffffff00 = 0x000c2200) or (ether[6:4] & 0xffffff00 = 0x000c2200)
Здесь ether[0:4] — четыре байта начиная со смещения 0 (поле MAC назначения), ether[6:4] — четыре байта начиная со смещения 6 (поле MAC источника). Оператор : в BPF принимает размер только 1, 2 или 4 — трёх байт выбрать нельзя, поэтому мы берём четыре и маской 0xffffff00 обнуляем лишний четвёртый.
Альтернативно можно сравнивать побайтно — длиннее, но нагляднее:
(ether[0]=0x00 and ether[1]=0x0c and ether[2]=0x22) or (ether[6]=0x00 and ether[7]=0x0c and ether[8]=0x22)
Шпаргалка: фильтры захвата для МЭК 61850
Теперь то, ради чего всё затевалось. Ниже — готовые рецепты для типовых задач на цифровой подстанции.
GOOSE
Весь GOOSE (без VLAN):
ether proto 0x88b8
Весь GOOSE с учётом VLAN-тегированных кадров:
ether proto 0x88b8 or (vlan and ether proto 0x88b8)
GOOSE от конкретного IED (фильтр по MAC-источника):
ether proto 0x88b8 and ether src 00:0c:22:12:34:56
GOOSE на конкретный мультикастный адрес:
ether proto 0x88b8 and ether dst 01:0c:cd:01:00:01
GOOSE от всех устройств одного производителя (по OUI, например 00:0C:22):
ether proto 0x88b8 and (ether[6:4] & 0xffffff00 = 0x000c2200)
GOOSE с конкретным APPID (например, 0x1000).
APPID — это два байта сразу после EtherType, смещение 14–15 в нетегированном кадре:
ether proto 0x88b8 and ether[14:2] = 0x1000
С учётом VLAN:
(ether proto 0x88b8 and ether[14:2] = 0x1000) or (vlan and ether proto 0x88b8 and ether[14:2] = 0x1000)
Диапазон GOOSE APPID (например, от 0x1000 до 0x10FF):
ether proto 0x88b8 and ether[14:2] & 0xff00 = 0x1000
Мультикаст GOOSE по диапазону назначения:
ether proto 0x88b8 and (ether[0:4] & 0xffffff00 = 0x010ccd00) and (ether[4:2] & 0xff00 = 0x0100)
Sampled Values
Все SV (без VLAN):
ether proto 0x88ba
Все SV с учётом VLAN:
ether proto 0x88ba or (vlan and ether proto 0x88ba)
SV от конкретного MU (по MAC-источника):
ether proto 0x88ba and ether src 00:0c:22:aa:bb:cc
SV на конкретный мультикастный адрес:
ether proto 0x88ba and ether dst 01:0c:cd:04:00:01
SV с конкретным APPID (например, 0x4000):
ether proto 0x88ba and ether[14:2] = 0x4000
SV от всех MU одного производителя (по OUI):
ether proto 0x88ba and (ether[6:4] & 0xffffff00 = 0x000c2200)
SV из диапазона 01:0C:CD:04:xx:xx:
ether proto 0x88ba and (ether[0:4] & 0xffffff00 = 0x010ccd00) and (ether[4:2] & 0xff00 = 0x0400)
Про фильтрацию по goID и svID
Здесь приходится разочаровать: надёжно отфильтровать GOOSE по goID или SV по svID средствами фильтра захвата нельзя. Причина в том, что эти поля живут внутри BER-кодированного APDU, и их смещение от начала кадра зависит от длины предыдущих полей (gocbRef/svID-заголовка и так далее). BPF же умеет работать только с фиксированными смещениями.
Что можно сделать на практике:
- Использовать APPID как прокси. У «правильно» сконфигурированного публикатора APPID уникален и соответствует одному
gocbRef/svCB. Фильтр по APPID — самый надёжный способ поймать конкретный поток на уровне захвата. - Использовать MAC-адрес назначения групповой рассылки. Тоже рекомендуется иметь его уникальным для конкретного потока в нормально спроектированной и налаженной системе.
- Сначала захватить весь трафик определенного типа, потом отфильтровать фильтром отображения. Если поток не слишком большой — вариант рабочий. Для SV на нагруженной шине процесса — идея так себе.
- Написать byte-offset фильтр под конкретный кейс. Захватываете несколько кадров нужного потока без фильтра, смотрите, на каком смещении лежит интересующая вас строка, пишете фильтр вида
ether[N:4] = 0xXXXXXXXX. Метод рабочий, но крайне хрупкий — любое изменение в предыдущих полях APDU сдвинет смещение, и фильтр перестанет ловить.
В 90 % случаев комбинация «EtherType + APPID» или «EtherType + dst MAC» закрывает задачу.
Исключающие фильтры — когда проще отсечь лишнее
Отдельный полезный приём — захватить всё, кроме одного-двух особо шумных потоков. Например, снять весь процесс-бас без двух конкретных SV-потоков, чтобы Wireshark не завис:
not (ether proto 0x88ba and ether dst 01:0c:cd:04:00:01) and not (ether proto 0x88ba and ether dst 01:0c:cd:04:00:02)
Или, наоборот, захватить GOOSE и PTP, но не SV:
ether proto 0x88b8 or ether proto 0x88f7
Для случаев, когда хочется посмотреть «всё, кроме MMS-коммуникаций с конкретным сервером»:
not (tcp port 102 and host 192.168.1.10)
Несколько практических советов напоследок
- Всегда начинайте с фильтра захвата, если работаете на шине процесса. Попытка «посмотрю, что там, а потом разберусь» закончится зависанием Wireshark.
- Держите шпаргалку рядом. BPF не тот язык, который запоминается с первого раза. Таблица с готовыми выражениями экономит десятки минут на каждой отладке.
- Для диагностики реальных проблем с GOOSE/SV чаще работает связка фильтр захвата по EtherType + фильтр отображения. Грубый фильтр на захвате отсекает 95% шума, а точечный фильтр отображения позволяет быстро переключаться между потоками в уже собранном трейсе.
BPF не самый дружелюбный язык, но освоить десяток конструкций из этого материала достаточно, чтобы закрыть практически все задачи по захвату трафика МЭК 61850. А заодно перестать наблюдать, как Wireshark борется за жизнь и умирает на шине процесса.