Изучаем Ethernet-фреймы с помощью осциллографа

10 октября 2018

Практически в любой книге по компьютерным сетям вы без труда найдете описание Ethernet-пакетов, ровно как и пакетов IP, TCP, UDP и прочих. Но в тех источниках, что мне встречались, описание это было довольно высокоуровневым, в терминах байтов, которые как-то передаются по витой паре. Но как конкретно единички и нолики представляются при помощи напряжения или тока? Давайте выясним!

Разобраться в происходящем нам поможет, конечно же, осциллограф. В рамках этой заметки мы будем рассматривать исключительно 10-и мегабитный Ethernet, или 10baseT. Более высокоскоростные стандарты 100baseT и 1000baseT работают схожим образом. Однако для их анализа нужен осциллограф с широкой полосой пропускания, которым, как правило, простые радиолюбители вроде нас с вами не обладают.

Если я просто возьму ноутбук и соединю его витой парой со моим роутером, то устройства договорятся использовать стандарт 100baseT, как наиболее быстрый из понимаемых и тем, и другим. Поэтому нужно прямо указать, что интерфейсу следует использовать 10baseT. В Linux это можно сделать, сказав:

sudo ethtool -s enp0s25 speed 10 autoneg off

Проверить, на какой скорости работает интерфейс, можно командой:

ethtool enp0s25

Далее нам нужно как-то увидеть передаваемый сигнал на осциллографе. Для этого я снял изоляцию с витой пары, нашел в ней оранжевый и бело-оранжевый провод, и снял изоляцию с них. Провода оказались многожильными. Чтобы в таком виде витая пара прослужила дольше, провода были залужены.

Теперь к ним можно подключиться щупом осциллографа:

Подключение щупа осциллографа к витой паре

В Ethernet используется дифференциальные сигналы. Это означает, что земли в витой паре как таковой нет. Сигнал передается по паре проводов. По одному проводу идет некий сигнал, а во второму — обратный к нему, инвертированный сигнал. Такой способ передачи информации обеспечивает повышенную устойчивость к помехам. Дифференциальные сигналы используются не только в Ethernet, но также и в CAN, PCI Express, USB, DVI и SATA.

Неудобство при работе с дифференциальными сигналами заключается в том, что подключив щуп осциллографа, как изображено на фото, мы сможем посмотреть сигнал только на одной паре проводов. Связано это с тем, что земля на всех каналах осциллографа общая. Подключив второй щуп, мы соединим через общую землю две несвязанные между собой пары проводов, и тем самым превратим сигнал в тыкву. Для решения этой проблемы существуют специальные устройства, дифференциальные пробы. Самый дешевый дифференциальный проб, который мне удалось найти, называется Micsig DP10013 и продается на eBay примерно за 130$. При этом для каждого канала нужен свой дифференциальный проб. Может оказаться дешевле купить второй осциллограф.

Fun fact! Оранжевая пара проводов используется для передачи сигнала от компьютера к роутеру, а зеленая пара — для передачи от роутера к компьютеру. Все четыре пары начинают использоваться, начиная только с гигабитного Ethernet’а (1000baseT). Именно поэтому встречаются Ethernet-кабели, имеющие лишь две пары проводов.

Итак, если все было сделано правильно, на осциллографе мы начнем видеть Ethernet-фреймы:

Как выглядит Ethernet-пакет в осциллографе

Пакеты большие, поэтому рассматривать их в осциллографе не очень удобно. К счастью, все данные можно сохранить на флешку в формате CSV, перекинуть на компьютер, и затем импортировать в Audacity. Единственная сложность заключается в том, что осциллограф пишет напряжение в вольтах, а Audacity при импорте нужны относительные значения от -1 до 1. Но эта проблема решается в сорок строк на Python. Код скрипта вы найдете в исходниках к посту, в файле rigol_to_audacity.py.

Для импорта данных в Audacity говорим Generate → Sample Data Import… и выбираем текстовый файл, полученный благодаря скрипту. Вот что мы увидим в итоге:

Ethernet-фрейм в Audacity

Здесь используется манчестерское кодирование. В отличие от всяких SPI, I2C и UART, кодирующих единички и нолики высоким и низким напряжением, в манчестерском кодировании используется не напряжение, а восходящие и нисходящие фронты сигнала. Такой подход интересен тем, что он не нуждается в отдельном проводе для передачи тактового сигнала. Все, что нужно для синхронизации передающей и принимающей стороны, содержится в самом сигнале.

Тот же скриншот, только с единичками и ноликами:

Манчестерское кодирование

Примечание: Поскольку существует ровно два способа подключить проб осциллографа к двум проводам, у вас сигнал может получиться либо точно таким же, либо перевернутым.

Обратите внимание, что единички и нолики передаются с фиксированной скоростью (расстояние между всеми стрелочками на изображении одинаковое). Таким образом, если передающей стороне для передачи следующего бита нужно предварительно повысить или понизить напряжение, она может спокойно это сделать между передачей двух битов. Принимающей стороной такой переход будет проигнорирован.

Поскольку искать единички и нолики в Audacity довольно утомительно, был написан еще один скрипт, делающий за нас эту рутинную работу. Его вы найдете в исходниках к этой заметке под именем ethernet_decode.py. Благодаря скрипту мы можем увидеть весь Ethernet-фрейм в бинарном виде:

# Преамбула занимает 8 байт. Помимо прочего, она может быть
# использована для калибровки часов на принимающей стороне.
10101010
10101010
10101010
10101010
10101010
10101010
10101010
10101011

# MAC-адрес получателя: E4:95:6E:43:42:7F
# Важно! Используется порядок бит lsb-first.
00100111
10101001
01110110
11000010
01000010
11111110

# MAC-адрес отправителя: 0E:18:77:10:72:DC
00000111
00011000
11101110
00001000
01001110
00111011

# Тип пакета: 08 00, означает IPv4
# https://en.wikipedia.org/wiki/EtherType#Examples
00010000
00000000

# Полезная нагрузка: от 46 до 1500 байт,
# в данном случае - 84 байта.
10100010
00000000
00000000
00101010
# ... и так далее, см payload.dat

# Контрольная сумма фрейма:
00010001 # 0x88
10100110 # 0x65
00101001 # 0x94
00000111 # 0xE0

Контрольная сумма является самым обыкновенным CRC32 и вычисляется от всего фрейма за исключением преамбулы и, понятно, самой контрольной суммы. Скрипт ethernet_decode.py сохраняет соответствующие данные в отдельном бинарном файле, благодаря чему контрольную сумму очень легко проверить:

$ sudo pacman -S perl-archive-zip
... пропущено ...
$ crc32 frame.dat
e0946588

Как видите, контрольная сумма сходится, только порядок байт в Ethernet используется обратный.

Также скрипт сохраняет в отдельном файле полезную нагрузку фрейма. Его при желании можно открыть в Wireshark. Для этого полезную нагрузку нужно перевести из бинарного формата в текстовый:

od -Ax -tx1 -v payload.dat > payload.hex

… а затем воспользоваться диалогом File → Import from Hex Dump. В выпадающем списке Encapsulation type выбираем Raw IPv4. В данном случае полезной нагрузкой был ICMP-пакет, посланный командой ping mail.ru.

Итак, теперь мы знаем, как именно единички и нолики передаются по витой паре! Если вам хочется пойти дальше, и узнать о форматах IP, TCP, UDP и прочих пакетов, по этой теме можно найти информацию в Википедии. Кроме того, можно порекомендовать книгу «Компьютерные сети: принципы, технологии, протоколы» за авторством Виктора и Натальи Олифер. Из инструментов вам пригодятся уже упомянутый Wireshark, а также tcpdump и libpcap.

Все исходники к этой заметке вы найдете в этом репозитории на GitHub.

Дополнение: Снифинг Ethernet-трафика с платой Throwing Star

Метки: , , .


Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.