Сети для самых маленьких. Часть 2: Ethernet

В первой части мы разобрали, что сетевой стек — матрёшка из заголовков, и что самый внешний слой в ней, канальный, доставляет данные соседу по кабелю. Сегодня — о самом распространённом канальном протоколе: Ethernet. По нему работают и домашняя розетка, и порт коммутатора в стойке, и 400-гигабитные линки между дата-центрами, и даже Wi-Fi притворяется им перед операционной системой.

Ethernet придумал Роберт Меткалф в Xerox PARC в 1973 году для сети из одного толстого коаксиального кабеля, к которому подключались все машины офиса. С тех пор сменилось всё: кабель, скорость, топология, электроника. Не изменился только формат кадра, и это самое поучительное в его истории.


Кадр

Кадр Ethernet в том виде, в каком его видит tcpdump, выглядит так:

 байты:   6            6          2         46…1500        4
        ┌────────────┬──────────┬────────┬───────────────┬─────┐
        │ MAC        │ MAC      │ Ether  │ данные        │ FCS │
        │ получателя │ отправи- │ Type   │ (IP-пакет,    │     │
        │            │ теля     │        │ ARP, …)       │     │
        └────────────┴──────────┴────────┴───────────────┴─────┘
        └───── заголовок, 14 байт ───────┘

Перед кадром в кабель уходят ещё 8 байт, которых программы никогда не видят: преамбула — семь байт 10101010, по которым приёмник подстраивает тактовую частоту, и разделитель начала кадра (SFD) 10101011, последняя единица которого говорит «дальше заголовок». После кадра — межкадровый интервал (IFG) в 96 битовых интервалов, то есть пауза длиной в 12 байт, чтобы приёмник успел обработать принятое. На гигабите это 96 наносекунд.

По полям:

  • MAC получателя — первым, чтобы сетевая карта и коммутатор могли отбросить чужой кадр, не дочитывая его до конца.
  • MAC отправителя — его коммутатор использует для обучения, об этом ниже.
  • EtherType — то самое поле «что внутри» из первой части: 0x0800 — пакет IPv4 и 0x86DD — пакет IPv6, два поколения сетевого уровня (части 5–6 и 13); 0x0806 — ARP, запрос «какой MAC у этого IP-адреса» (часть 7); 0x8100 — тег VLAN с номером виртуальной сети (часть 17); 0x8863/0x8864 — PPPoE, сессия абонента у провайдера (часть 4). Есть тонкость: если значение меньше или равно 0x05DC (1500), это не тип, а длина данных, и перед нами кадр старого формата IEEE 802.3 с заголовком LLC внутри. Сегодня в таком формате ходят разве что служебные кадры STP. Всё остальное — Ethernet II с EtherType, который начинается с 0x0600.
  • Данные — от 46 до 1500 байт. Если IP-пакет короче 46 байт (а SYN без опций — это 40), сетевая карта дописывает нули до 46. Отсюда в tcpdump появляются кадры длиной 60 байт для пакетов, которым хватило бы 54: это паддинг. Верхняя граница 1500 — это и есть MTU Ethernet, о который мы будем спотыкаться всю серию.
  • FCS — контрольная сумма CRC-32 по всему кадру от заголовка до данных. Считает и проверяет сетевая карта. Если не сошлась, кадр молча выбрасывается: Ethernet не сообщает отправителю об ошибках и ничего не повторяет. Повторять — работа TCP, восемью частями позже. Единственный след — счётчик rx_crc_errors в статистике интерфейса; его стоит смотреть, когда «сеть работает, но медленно».

Итого минимальный кадр — 64 байта (14 + 46 + 4), максимальный — 1518 (14 + 1500 + 4). С VLAN-тегом, который вставляется между MAC отправителя и EtherType, на четыре байта больше. Некоторые сети разрешают jumbo-кадры с данными до 9000 байт, но это не стандарт, а договорённость: все устройства на пути должны быть настроены одинаково, иначе кадр пропадёт без следа.

Запомните число 64: ниже станет понятно, откуда оно взялось.


MAC-адрес

Шесть байт, записываются в шестнадцатеричном виде через двоеточие или дефис: 3c:52:82:1a:2b:3c. Адрес прошит в сетевую карту производителем и должен быть уникальным в мире, хотя на самом деле операционная система может подставить любой.

Первые три байта — OUI (Organizationally Unique Identifier), код производителя, который тот покупает у IEEE: 3c:52:82 — Hewlett Packard, 00:50:56 — VMware, b8:27:eb — Raspberry Pi. По OUI Wireshark пишет рядом с адресом имя производителя, а по нему же можно понять, что за устройство появилось в сети. Оставшиеся три байта производитель раздаёт сам.

Два младших бита первого байта имеют особый смысл:

первый байт адреса:  x x x x x x U I
                                 │ │
                                 │ └── I/G: 0 — индивидуальный, 1 — групповой
                                 └──── U/L: 0 — глобальный (от производителя), 1 — локальный
  • I/G. Если младший бит 1, это групповой адрес, и кадр предназначен не одной карте, а всем, кто подписан. Частный случай — широковещательный ff:ff:ff:ff:ff:ff: его принимают все. Групповые адреса IPv4-multicast начинаются с 01:00:5e, IPv6 — с 33:33; с ними встретимся в частях про ARP и Neighbor Discovery.
  • U/L. Если второй бит 1, адрес назначен локально, а не производителем. На практике это значит, что вторая шестнадцатеричная цифра — 2, 6, a или e: 02:42:ac:11:00:02 — контейнер Docker, 52:54:00:… — виртуальная машина QEMU, а телефоны и ноутбуки со случайным MAC-адресом в Wi-Fi тоже ставят этот бит.

У каждого интерфейса машины свой MAC-адрес. Посмотреть: ip link на Linux, ipconfig /all на Windows. Сменить на Linux можно одной командой, ip link set eth0 address 02:00:00:00:00:01, и это законный приём, например, когда провайдер привязал услугу к MAC-адресу старого роутера.


Откуда взялись 64 байта: разделяемая среда и коллизии

Исходный Ethernet — один коаксиальный кабель на всех. Передаёт одна станция, слышат все, и две станции одновременно передавать не могут: сигналы наложатся и получится мусор. Нужен способ делить кабель. Он называется CSMA/CD — Carrier Sense Multiple Access with Collision Detection, и работает так:

  1. Хочешь передавать — послушай кабель. Занят — жди, пока освободится, плюс межкадровый интервал.
  2. Свободен — передавай и продолжай слушать. Если принимаешь не то, что передаёшь, значит, кто-то начал одновременно с тобой: коллизия.
  3. Заметив коллизию, передай ещё 32 бита мусора (jam), чтобы её наверняка заметили все, и прекрати.
  4. Подожди случайное время и повтори с шага 1. Случайное время — это r слот-таймов, где r выбирается от 0 до 2ⁿ − 1, n — номер попытки (не больше 10). После 16 попыток кадр выбрасывается. Это называется экспоненциальной отсрочкой: чем больше столкновений, тем шире разброс и тем меньше шанс столкнуться снова.

А теперь главное. Коллизию можно заметить, только пока ты ещё передаёшь. Если станция A на одном конце кабеля закончила передавать кадр раньше, чем до неё дошёл сигнал станции B с другого конца, A решит, что всё прошло удачно, а кадр на самом деле испорчен. Значит, передача должна длиться не меньше, чем сигнал идёт до самого дальнего конца кабеля и обратно.

Для Ethernet на 10 Мбит/с максимальный диаметр сети с повторителями задали около 2500 метров, двойной пробег сигнала с задержками на электронике уложили в 512 битовых интервалов, это и есть слот-тайм, 51,2 микросекунды. 512 бит — 64 байта. Кадр короче 64 байт дополняется нулями не из любви к круглым числам, а чтобы отправитель гарантированно успел заметить коллизию.

На 100 Мбит/с битовый интервал в десять раз короче, и диаметр сети пришлось сократить до ~200 метров. На гигабите в полудуплексе кадр пришлось бы растягивать до 512 байт, поэтому гигабитный полудуплекс не прижился совсем. Сегодня коллизий не существует нигде, кроме старого оборудования, и всё же число 64 живёт во всех стандартах Ethernet, потому что менять формат кадра дороже, чем носить с собой историю.

Хабы, которые ещё можно встретить в старых шкафах, — это тот же общий кабель в коробке: сигнал с одного порта повторяется на все остальные. Все устройства за хабом — один домен коллизий.


Коммутатор

Коммутатор (switch) заменил общий кабель. Каждый порт — отдельный кабель до одного устройства, и коммутатор пересылает кадр только туда, куда нужно. Чтобы знать, куда нужно, он ведёт таблицу MAC-адресов: соответствие адреса и порта, за которым этот адрес видели.

Алгоритм помещается в четыре правила:

  1. Обучение. Пришёл кадр на порт — запомнить: MAC-адрес отправителя живёт за этим портом. Запись живёт по умолчанию 300 секунд с последнего кадра от этого адреса, потом удаляется (aging).
  2. Пересылка. Посмотреть MAC получателя в таблице. Нашёлся — отправить кадр в тот порт, и только в него.
  3. Флуд. Не нашёлся (unknown unicast), или адрес широковещательный, или групповой — отправить во все порты, кроме того, с которого кадр пришёл.
  4. Фильтрация. Если адрес получателя живёт за тем же портом, с которого кадр пришёл, не пересылать никуда.

Проследим на примере. Три машины подключены к портам 1, 2 и 3, таблица пуста, коммутатор только что включён.

                      ┌─────────────────┐
  A  aa:…:01 ── 1 ────┤                 ├──── 3 ── C  cc:…:03
                      │   коммутатор    │
  B  bb:…:02 ── 2 ────┤                 │
                      └─────────────────┘
Шаг Событие Таблица после Куда ушёл кадр
1 A шлёт кадр для C aa:…:01 → порт 1 адрес C неизвестен: флуд в порты 2 и 3
2 C отвечает A aa:…:01 → 1, cc:…:03 → 3 адрес A известен: только в порт 1
3 A снова шлёт C без изменений только в порт 3
4 B шлёт широковещательный кадр добавлен bb:…:02 → 2 во все порты, кроме 2

Машина B на шаге 1 получила чужой кадр, а на шаге 3 — уже нет. Это и даёт коммутатору его главные свойства: каждый порт — свой домен коллизий, два разговора между разными парами портов идут одновременно, и чужой unicast-трафик на порт не приходит. А широковещание по-прежнему приходит всем: коммутатор не делит домен широковещания, этим занимаются роутеры и VLAN (часть 17).

Пара технических деталей, которые пригодятся:

  • Коммутатор, который принимает кадр целиком, проверяет FCS и только потом пересылает, называется store-and-forward; это норма. Cut-through начинает пересылать, прочитав только MAC получателя: быстрее на доли микросекунды, но пропускает битые кадры дальше.
  • Таблица MAC-адресов конечна: от нескольких тысяч записей в домашних устройствах до сотен тысяч в стоечных. Если её переполнить (старая атака MAC flooding — засыпать коммутатор кадрами с случайными адресами отправителя), коммутатор перестаёт обучаться и флудит всё подряд, то есть превращается в хаб.
  • Коммутатор не меняет в кадре ничего. Ни MAC-адреса, ни EtherType, ни данные. Это главное отличие от роутера, который снимает заголовок Ethernet целиком.

На Linux коммутатор есть в ядре, это bridge, и его таблицу можно посмотреть:

$ bridge fdb show br br0
3c:52:82:1a:2b:3c dev eth0 master br0
02:42:ac:11:00:02 dev veth1 master br0
33:33:00:00:00:01 dev eth0 self permanent

Записи permanent — адреса самого моста и групповые адреса, которые он обязан принимать. На Cisco то же самое — show mac address-table, на MikroTik — /interface bridge host print.


Дуплекс и автосогласование

Витая пара, пришедшая на смену коаксиалу, решила проблему коллизий радикально: в кабеле есть отдельные пары проводов для передачи и приёма. У 10BASE-T и 100BASE-TX их две (контакты 1–2 передают, 3–6 принимают), и если на другом конце не хаб, а коммутатор или вторая машина, обе стороны могут передавать одновременно. Это полный дуплекс: CSMA/CD выключен, коллизий нет по построению, скорость в каждую сторону полная.

Но у 100-мегабитных карт есть и полудуплекс, для совместимости с хабами, и обе стороны линка должны договориться. Для этого есть автосогласование: при поднятии линка каждая сторона посылает пачки импульсов (FLP, fast link pulses), в которых закодировано, что она умеет: 10 или 100 Мбит/с, полу- или полный дуплекс. Выбирается лучший общий вариант.

Классическая ошибка, которую стоит знать даже сегодня, — duplex mismatch. Админ прибил на коммутаторе порт жёстко в 100/full, а сервер оставил в auto. Сервер не получает FLP, по форме сигнала определяет скорость 100 Мбит/с (parallel detection), а вот дуплекс узнать ему неоткуда, и стандарт велит в этом случае выбирать полудуплекс. Линк поднимается, ping ходит, а под нагрузкой сервер видит коллизии там, где их быть не может, обрывает передачу, коммутатор получает обрубки кадров и считает их ошибками CRC. Симптомы: скорость в десятки раз ниже ожидаемой, растут late collisions на одной стороне и CRC errors/runts на другой. Лечение одно: либо auto с обеих сторон, либо жёстко с обеих.

Гигабит эту историю закрыл: в 1000BASE-T автосогласование обязательно, полудуплекс практически не реализуют, и ethtool покажет такую картину:

$ ethtool eth0
Settings for eth0:
        Supported link modes:   10baseT/Half 10baseT/Full
                                100baseT/Half 100baseT/Full
                                1000baseT/Full
        Speed: 1000Mb/s
        Duplex: Full
        Auto-negotiation: on
        Link detected: yes

Если здесь неожиданно Speed: 100Mb/s на гигабитной карте, это чаще всего кабель: для гигабита нужны все четыре пары, и достаточно одного плохого контакта в разъёме, чтобы автосогласование откатилось на 100 Мбит/с по двум парам.

В полном дуплексе есть свой механизм притормаживания — flow control: приёмник, не успевающий разгребать буфер, шлёт кадр PAUSE на групповой адрес 01:80:c2:00:00:01 с EtherType 0x8808 и временем, на которое отправитель должен замолчать. Домашние коммутаторы обычно включают его по умолчанию, в дата-центрах чаще выключают: пауза останавливает весь порт, а не одно соединение, и один медленный получатель тормозит всех.


Физика: что в кабеле

Чтобы ориентироваться в названиях. Формат имени — <скорость>BASE-<среда>: BASE означает передачу в основной полосе без модуляции на несущую, буквы после дефиса — среда.

Стандарт Среда Пары Дальность Как кодируется
10BASE-T витая пара Cat3 2 100 м манчестерский код, 1 бит = 1 переход
100BASE-TX Cat5 2 100 м 4B/5B + MLT-3, три уровня сигнала
1000BASE-T Cat5e 4, обе стороны по каждой 100 м PAM-5, пять уровней, 250 Мбит/с на пару
2.5G/5GBASE-T Cat5e/Cat6 4 100 м как 10GBASE-T на меньшей частоте
10GBASE-T Cat6a 4 100 м PAM-16, кодирование LDPC
1000BASE-SX / 10GBASE-SR многомодовое волокно — 300–550 м лазер 850 нм
1000BASE-LX / 10GBASE-LR одномодовое волокно — 10 км лазер 1310 нм

На гигабите интересный трюк: все четыре пары передают в обе стороны одновременно, и каждая сторона вычитает из принятого сигнала свой собственный (эхоподавление). Поэтому для гигабита нужен весь кабель, а для 100 Мбит/с хватало половины — именно на этом держались старые «разветвители на два компьютера из одного кабеля».

Второй трюк гигабита — auto MDI-X: порт сам определяет, какие пары у соседа передающие, и при необходимости меняет их местами. Перекрёстные кабели (crossover) для соединения двух компьютеров напрямую ушли в прошлое вместе со 100 Мбит/с.

Оптика в коммутаторах живёт в сменных модулях SFP (1 Гбит/с), SFP+ (10), SFP28 (25), QSFP28 (100): в порт можно вставить модуль под нужное волокно и расстояние, и Ethernet от этого не меняется. Для дома и офиса достаточно знать: витая пара до 100 метров, дальше или оптика, или ещё один коммутатор.


Петли и STP

Соедините два порта одного коммутатора кабелем. Или два коммутатора — двумя кабелями «для надёжности». Первый же широковещательный кадр уйдёт в оба кабеля, вернётся через второй, снова уйдёт в оба, и так до бесконечности: у Ethernet нет поля TTL, и кадр, попавший в петлю, будет ходить по ней, пока не выдернут кабель. Через секунду в петле крутятся тысячи копий, порты светятся сплошным светом, а таблица MAC-адресов сходит с ума: один и тот же адрес отправителя видят то на одном порту, то на другом (MAC flapping). Сеть лежит целиком. Это называется широковещательный шторм, и это самый простой способ уронить офис одним кабелем.

Защита от петель — протокол STP (Spanning Tree Protocol, 802.1D) и его быстрая версия RSTP (802.1w). Коммутаторы обмениваются служебными кадрами BPDU раз в две секунды (те самые кадры формата 802.3 с LLC), выбирают корневой коммутатор с наименьшим идентификатором, вычисляют кратчайший путь от каждого коммутатора до корня и блокируют все остальные порты между коммутаторами. Получается дерево без циклов; если рабочий линк падает, заблокированный порт открывается, у STP на это уходит 30–50 секунд, у RSTP — секунды. Резервные кабели между коммутаторами — это нормально, но только когда STP включён и настроен; на домашних коммутаторах без управления его обычно нет, и там петля — просто петля.


Смотрим руками

Статистика интерфейса, в том числе счётчики ошибок:

$ ip -s link show eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 3c:52:82:1a:2b:3c brd ff:ff:ff:ff:ff:ff
    RX:  bytes packets errors dropped  missed   mcast
    9876543210 8765432      0       0       0  123456
    TX:  bytes packets errors dropped carrier collsns
    1234567890 2345678      0       0       0       0

Ненулевые errors на приёме, collsns на передаче на полнодуплексном линке или растущие rx_crc_errors в ethtool -S eth0 — повод смотреть на кабель, разъём и настройки дуплекса.

Кадры с заголовком Ethernet:

$ sudo tcpdump -i eth0 -n -e -c 3
12:00:00.000001 3c:52:82:1a:2b:3c > ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 42: Request who-has 192.168.1.1 tell 192.168.1.10, length 28
12:00:00.000250 d8:47:32:0a:0b:0c > 3c:52:82:1a:2b:3c, ethertype ARP (0x0806), length 60: Reply 192.168.1.1 is-at d8:47:32:0a:0b:0c, length 46
12:00:00.000300 3c:52:82:1a:2b:3c > d8:47:32:0a:0b:0c, ethertype IPv4 (0x0800), length 74: 192.168.1.10.51234 > 203.0.113.10.443: Flags [S], ...

Здесь всё, о чём была статья: широковещательный адрес получателя в первом кадре, EtherType, который говорит «внутри ARP», и длина 60 во втором кадре — это паддинг до минимальных 64 байт (tcpdump не считает FCS, поэтому показывает 60). Первый кадр показан длиной 42, потому что захвачен на отправителе до того, как сетевая карта дописала нули.

В Wireshark раскройте строку Ethernet II любого кадра: там адреса с именами производителей по OUI, биты I/G и U/L прямо в разборе адреса и EtherType с расшифровкой.


Что запомнить

  • Кадр: 14 байт заголовка (два MAC-адреса и EtherType), 46–1500 байт данных, 4 байта CRC. Минимум 64 байта, максимум 1518. Данные больше 1500 — MTU — в кадр не помещаются.
  • MAC-адрес: 3 байта производителя и 3 байта номера; младший бит первого байта отличает групповой адрес, следующий — локально назначенный. ff:ff:ff:ff:ff:ff принимают все.
  • 64 байта — наследие общего кабеля и CSMA/CD: кадр должен длиться дольше двойного пробега сигнала, чтобы заметить коллизию.
  • Коммутатор учит таблицу по адресам отправителей, пересылает по адресам получателей, флудит неизвестное и широковещание, не меняет в кадре ни байта.
  • Полный дуплекс убрал коллизии; duplex mismatch — когда одна сторона в auto, другая жёстко.
  • Петля без STP кладёт сеть: у кадра нет TTL.

Дальше — Wi-Fi: почему радио не может работать как Ethernet, CSMA/CA и подтверждения, каналы и помехи, как клиент подключается к точке и что происходит при четырёхстороннем рукопожатии WPA2.

Оставить комментарий могут только зарегистрированные пользователи.

Войдите на сайт или зарегистрируйтесь, чтобы оставить комментарий.