Перейти до вмісту

Ethernet і комутація

Ви підключили другий комп’ютер до тієї ж коробки, що й перший, дали обом адреси з однієї підмережі, і вони одразу бачать один одного. Ніхто не налаштовував, куди йти кадрам. Коробка (комутатор) не має IP-адреси, не читає заголовків IP, а все одно доставляє кадр саме тому, кому він адресований. Потім хтось вмикає в неї ще один кабель між двома її портами, і через хвилину вся мережа офісу лягає, а індикатори на портах блимають без упину.

Обидві ситуації пояснює один рівень: канальний (link layer). Пакети IP самі кудись поїхати не можуть, їх завжди везе кадр (frame) якоїсь канальної технології, і найпоширеніша з них — Ethernet. Без цього рівня незрозуміло, що насправді робить ping у локальній мережі, чому кадр розміром 1501 байт може не дійти, і навіщо в лабораторних курсу міст зібрано з простору імен та кількох пар veth.

Передумови. Що таке рівні й інкапсуляція — модуль 1. Простір імен мережі і пара veth — модуль 17 курсу «Операційні системи». Шлях кадра від мережевої карти до ядра — модуль 3.

Ethernet розв’язує просту задачу: багато пристроїв, підключених до спільного середовища, мусять передавати один одному блоки байтів і при цьому розуміти, кому призначений блок. Для цього перед даними ставлять адресу отримувача, адресу відправника й позначку, що лежить усередині.

Кадр Ethernet II: адреса призначення 6 байтів, адреса відправника 6, EtherType 2, дані від 46 до 1500 байтів, контрольна сума FCS 4 байти. Необовʼязковий тег VLAN 4 байти стоїть перед EtherType.преамбула + SFD: 8 Б, кадром не рахуютьсяпризначення6 БMAC-адресавідправник6 БMAC-адресатег 802.1Q4 БнеобовʼязковийEtherType2 Б0x0800 = IPv4корисне навантаження (пакет IP тощо)46–1500 Бверхня межа — MTUFCS4 БCRC-32заголовок 14 Ббез тегу: від 64 до 1518 Б разом із FCS; з тегом до 1522 Б
Кадр Ethernet II. Заголовок займає 14 байтів; тег VLAN, якщо він є, вставляється між адресою відправника й EtherType (про нього в модулі 5). Ширина блоків не пропорційна розмірам.

Поле EtherType каже, що вкладено в кадр, щоб приймач знав, якому протоколу віддати вміст: 0x0800 — IPv4, 0x86DD — IPv6, 0x0806 — ARP, а 0x8100 означає, що далі йде тег VLAN. Значення до 1500 у цьому місці в старому форматі IEEE 802.3 означали довжину, тому EtherType починається з 0x0600, і одне й те ж поле однозначно читається обома способами.

Наприкінці стоїть FCS (frame check sequence), контрольна сума CRC-32 усього кадру. Мережева карта перераховує її при прийомі й мовчки викидає кадр, якщо сума не збіглася. Тому tcpdump зазвичай FCS не показує: до ядра пошкоджені кадри не доходять, а для здорових карта її зрізає. Преамбула й початковий обмежувач кадру (SFD) слугують для синхронізації приймача. Вони передаються перед кадром, але кадром не вважаються.

Мінімальний кадр разом із FCS має 64 байти, тобто корисне навантаження не менше 46; коротші дані карта добиває нулями. Верхня межа навантаження без тега — 1500 байтів, і саме це число називається MTU.

На канальному рівні пристрій має адресу завдовжки 48 біт. Її пишуть як шість байтів у шістнадцятковому вигляді: 5e:4d:22:ee:23:64. Її записують у мережеву карту виробник, а програмно вона легко змінюється.

  • Перші три байти — OUI (organizationally unique identifier), який IEEE видає виробникові. За ним у базах видно, чия це карта.
  • Два молодших біти першого байта мають особливе значення. Молодший біт (I/G) відрізняє одиночну адресу (0) від групової (1). Наступний біт (U/L) показує, хто призначив адресу: виробник (0) чи адміністратор або програма (1). Такі адреси називаються локально адміністрованими.
  • ff:ff:ff:ff:ff:ff — широкомовна адреса: кадр отримають усі пристрої сегмента. Решта групових адрес дозволяють звертатися до підмножини: IPv4-мультикаст мапиться на 01:00:5e:…, IPv6 на 33:33:….

Перевірити біти можна на будь-якій адресі. Перший байт 5e у двійковому записі 01011110: молодший біт нульовий, тобто адреса одиночна, а другий з молодших одиничний, тобто адреса локально адміністрована. Так виглядають адреси, які Linux випадково генерує для veth. Так само вчиняють телефони, що підставляють випадкову MAC-адресу під час підключення до Wi-Fi, щоб мережа не бачила справжню.

Комутатор навчається сам

Section titled “Комутатор навчається сам”

Найпростіше об’єднати кілька пристроїв — повторювач (hub), який кожен отриманий сигнал копіює на всі порти. Усі пристрої тоді ділять одне середовище й, коли двоє починають говорити одночасно, кадри псуються. Це колізія, і класичний Ethernet боровся з ними алгоритмом CSMA/CD: слухати канал, не починати, поки він зайнятий, а якщо колізія сталася, почекати випадковий час і повторити. Область, у якій кадри можуть зіткнутися, називається доменом колізій.

Комутатор (switch) усе це прибрав. Кожен порт у нього — окремий канал з окремою парою проводів на кожен напрямок (повний дуплекс), і колізіям на ньому просто нізвідки взятися. Формально кожен порт сам є доменом колізій з двома учасниками, портом і пристроєм за ним, тож термін «домен колізій» у сучасних мережах здебільшого історичний. Натомість лишилося поняття широкомовного домену: множини портів, до яких дійде кадр з адресою ff:ff:ff:ff:ff:ff. Комутатор його не розділяє.

На який порт віддати кадр, комутатору ніхто не налаштовує: він підглядає. Кожен кадр, що надходить на порт, містить адресу відправника, тож комутатор записує в таблицю MAC (у Linux вона називається FDB, forwarding database), що ця адреса знаходиться за цим портом. Для кожного кадра комутатор діє так:

  1. Запам’ятати або оновити запис «адреса відправника — порт, звідки прийшов».

  2. Якщо адреса призначення групова або широкомовна, розіслати кадр на всі порти, крім того, звідки він прийшов.

  3. Якщо адреса призначення є в таблиці, надіслати кадр на потрібний порт (а якщо цей порт і є вхідним, кадр відкинути: адресат на тому боці так само його вже бачив).

  4. Якщо адреси немає в таблиці, вчинити як зі широкомовним: розіслати на всі решту портів. Це flooding.

Навчання таблиці MAC. Кадр від A до B надходить на порт 1: комутатор запамʼятовує, що A за портом 1, а B ще невідомий, тому розсилає кадр на порти 2 і 3. Відповідь B надходить на порт 2: комутатор запамʼятовує B і, знаючи A, надсилає кадр лише на порт 1.1. A → B, адреса B невідомаswitch1A2B3Cтаблиця MACAпорт 1flooding2. B → A, адресу A вже знаємоswitch1A2B3Cтаблиця MACAпорт 1Bпорт 2лише порт 1
Два кадри поспіль. У першому адреса B комутатору невідома, і кадр розсилається на порти 2 і 3, тож C теж його бачить. У відповіді B комутатор уже знає A і надсилає кадр лише на порт 1. Заодно він дізнається, що B за портом 2.

Запис у таблиці не вічний: якщо від адреси давно нічого не надходило, запис старіє й видаляється (в Linux за замовчуванням через 300 секунд). Переставлений на інший порт комп’ютер за кілька секунд знову навчить комутатор, а той, що відключився, з таблиці зникне.

Слабке місце цієї схеми — розмір таблиці. Апаратні комутатори тримають її в обмеженій пам’яті. Якщо засипати комутатор кадрами з тисячами вигаданих адрес відправника, таблиця переповниться, і багато моделей у такому разі починають розсилати кадри на всі порти, як повторювач. Це відома атака MAC flooding, і від неї захищають обмеженням кількості адрес на порту.

Комутатор кадр не змінює: адреси в ньому лишаються тими, що виставив відправник, скільки б комутаторів кадр не пройшов. Новий заголовок Ethernet з’являється лише тоді, коли пакет переходить з мережі в мережу через маршрутизатор (модуль 7).

Між двома комутаторами часто навмисно кладуть два кабелі, для резервування. Широкомовний кадр вийде з першого комутатора обома кабелями, другий комутатор прийме кожну копію і розішле на всі решту портів, зокрема назад другим кабелем. У заголовку Ethernet немає нічого схожого на TTL, тож кадр більше ніколи не помре, а кількість копій зростає лавиною. За секунди канали заповнені, таблиці MAC безперервно перебудовуються, бо та сама адреса приходить то з одного порту, то з іншого. Це широкомовний шторм.

Розв’язок — протокол STP (spanning tree protocol, IEEE 802.1D). Комутатори обмінюються службовими кадрами, обирають один із себе кореневим, а решта вибирає найкоротший шлях до кореня. Надлишкові порти переводяться в стан блокування: канал підключений, але кадрів користувачів не пропускає. Якщо активний кабель відмовить, блокований порт увімкнеться. Пізніша версія RSTP робить це за секунди замість пів хвилини. Сучасні комутатори вміють і її, і кілька інших схем, і всі вони залишають у мережі дерево без циклів.

MTU (maximum transmission unit) задає найбільше корисне навантаження, яке інтерфейс візьме в один кадр; для Ethernet це 1500 байтів. Пакет IPv4 разом із заголовком не може бути більшим за 1500, інакше він не влізе (що з ним тоді відбувається, розглядає модуль 6).

Кадри з навантаженням до 9000 байтів називаються jumbo. У них менша частка службових даних на кожен байт корисних і менше кадрів на секунду, а значить, менше роботи для обробника переривань. Але вмикати jumbo можна лише всюди одночасно: усі карти й порти комутаторів широкомовного домену мають підтримувати однаковий розмір. Комутатор, який не вміє приймати великі кадри, викине їх мовчки, без жодного повідомлення відправнику. Нерівний MTU в одному сегменті дає чи не найпідступнішу поломку: дрібні пакети проходять, великі зникають, а ping і вхід через ssh працюють.

Зберемо той самий комутатор, що на рисунку: три «хости» і міст. Усе в просторах імен, система не постраждає.

Terminal window
sudo ip netns add sw
for i in 1 2 3; do
sudo ip netns add h$i
sudo ip link add p$i type veth peer name q$i
sudo ip link set p$i netns h$i
sudo ip link set q$i netns sw
sudo ip netns exec h$i ip addr add 192.0.2.$i/24 dev p$i
sudo ip netns exec h$i ip link set p$i up
sudo ip netns exec sw ip link set q$i up
done
sudo ip netns exec sw ip link add br0 type bridge
for i in 1 2 3; do sudo ip netns exec sw ip link set q$i master br0; done
sudo ip netns exec sw ip link set br0 up

Це комутатор: br0 — міст у ядрі, q1…q3 — його порти, а p1…p3 — карти трьох хостів. Адреси 192.0.2.0/24 зарезервовані для документації, тож у прикладах ними можна користуватися спокійно. Подивимося на адреси карт:

Terminal window
sudo ip netns exec h1 ip -br link show p1
p1@if7 UP 5e:4d:22:ee:23:64 <BROADCAST,MULTICAST,UP,LOWER_UP>

Ваші адреси будуть іншими, бо ядро генерує їх випадково. У кожної адреси перший байт закінчується двійковим 10: адреса одиночна й локально адміністрована.

Таблиця MAC свіжого моста порожня, крім службових записів permanent — його власних адрес. Пінгуємо й дивимося знову:

Terminal window
sudo ip netns exec h1 ping -c1 192.0.2.2
sudo ip netns exec sw bridge fdb show br br0 | grep -v permanent
5e:4d:22:ee:23:64 dev q1 master br0
ae:a4:25:33:0b:d9 dev q2 master br0

Міст навчився двох адрес з двох портів: з ARP-запиту (про нього в модулі 5) і відповіді на нього. Команда bridge -s fdb show додає час, що минув від останнього кадра з цієї адреси; за налаштованим ageing_time запис зникає. ip -d link show br0 виводить самі параметри (значення ageing_time 30000 вимірюється в сотих частках секунди, тобто це 300 секунд).

Тепер побачимо flooding. На h3 запускаємо tcpdump -e (-e показує заголовки Ethernet), а на h1 підкидаємо статичний запис для неіснуючої MAC-адреси, щоб ARP не знадобився:

Terminal window
sudo ip netns exec h3 tcpdump -l -n -e -i p3 &
sudo ip netns exec h1 ip neigh flush all
sudo ip netns exec h1 ping -c1 192.0.2.2
sudo ip netns exec h1 ip neigh replace 192.0.2.99 lladdr 02:00:00:00:00:99 dev p1 nud permanent
sudo ip netns exec h1 ping -c1 -W1 192.0.2.99
… 5e:4d:22:ee:23:64 > ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 42: Request who-has 192.0.2.2 tell 192.0.2.1, length 28
… 5e:4d:22:ee:23:64 > 02:00:00:00:00:99, ethertype IPv4 (0x0800), length 98: 192.0.2.1 > 192.0.2.99: ICMP echo request, id 1746, seq 1, length 64

Перший рядок — широкомовний ARP-запит про 192.0.2.2; він має дійти до всіх, тож h3 його бачить. Відповідь h2 адресована одному h1, і h3 її не бачить: міст уже знає, де h1. У другому рядку кадр адресований 02:00:00:00:00:99, якої не існує. Міст не знає, де вона, і розсилає кадр на всі порти, тож h3 цей кадр бачить, хоч він і не для нього. Якби міст знав адресу, h3 цього кадра не отримав би.

Перевірити MTU можна за допомогою ping із забороною фрагментації:

Terminal window
sudo ip netns exec h1 ip link set p1 mtu 1400
sudo ip netns exec h1 ping -c1 -M do -s 1400 192.0.2.2
ping: local error: message too long, mtu=1400

Розмір -s — це число байтів даних ICMP, до яких додається 8 байтів заголовка ICMP і 20 байтів заголовка IPv4. Щоб влізти в MTU 1400, значення має бути не більшим за 1372; тоді відповідь приходить. Повернути 1500 можна тією ж командою. Значення 9000 на veth теж приймається, а от фізична мережева карта погодиться лише на те, що підтримує її драйвер.

Нарешті петля. Створіть другий міст і з’єднайте два мости двома парами veth, увімкнувши STP на обох (ip link set br0 type bridge stp_state 1). Спочатку порти проходять стани listening і learning, і через двічі по forward_delay (у ядра типово 15 секунд, тож близько 30 секунд) bridge link показує чотири порти, з яких один у стані blocking:

26: l1@if25: … master br0 state forwarding priority 32 cost 2
28: l3@if27: … master br0 state forwarding priority 32 cost 2
25: l2@if26: … master br0 state forwarding priority 32 cost 2
27: l4@if28: … master br0 state blocking priority 32 cost 2

Один із чотирьох кінців двох кабелів заблоковано. Без stp_state 1 така конструкція породжує шторм; запускати це на хості, де ви не готові його перезавантажити, не варто.

Прибрати за собою: sudo ip netns delete h1 (і так само h2, h3, sw). Разом з простором імен зникають і їхні інтерфейси.

Типові помилки розуміння

Section titled “Типові помилки розуміння”

«Комутатор працює з IP-адресами». Звичайний комутатор рівня 2 не дивиться далі заголовка Ethernet. IP-адреси вивчають маршрутизатори; комутатор навіть не знає, чи всередині IPv4, IPv6 чи щось інше.

«У кадрі стоїть MAC-адреса кінцевого сервера, тож усі пристрої на шляху його бачать». MAC-адреси діють лише в межах одного широкомовного домену. Щойно кадр проходить маршрутизатор, той знімає заголовок Ethernet і будує новий для наступного сегмента, з іншими адресами. Через інтернет MAC-адреса вашого комп’ютера нікуди не доходить.

«Комутатор нічого не розсилає, у нього є таблиця». Розсилає кожного разу, коли адреса призначення невідома або групова. Тому нову адресу видно на всіх портах, поки комутатор її не вивчив.

«Колізії — головна проблема сучасного Ethernet». У мережі з комутаторами й повним дуплексом колізій немає. Якщо ethtool або лічильники показують колізії, це ознака невдалого узгодження дуплексу (один кінець у половинному, другий у повному) або старої апаратури.

«MTU 9000 можна ввімкнути на одному кінці, решта підлаштується». Не підлаштується. Кадр, більший за те, що приймає порт, відкидається без повідомлення, і зв’язок ламається вибірково: малі пакети проходять, великі ні.

«Ethernet сам не дасть кадру ходити колом». У заголовку немає лічильника переходів, тож петля ніколи не закінчується сама. Єдиний захист — STP чи його аналоги, або відсутність надлишкових зв’язків.

Перевір себе

1. Комутатор отримав кадр, адреси призначення якого немає в його таблиці MAC. Що він зробить?
2. З якого поля кадра комутатор дізнається, за яким портом стоїть відправник?
3. Перший байт MAC-адреси дорівнює 0x02. Що це означає?
4. Між двома комутаторами покладено два кабелі, STP вимкнений. Чому мережа лягає від першого ж широкомовного кадра?
5. На хості один інтерфейс має MTU 9000, решта в сегменті 1500. ping з малим пакетом проходить, scp великого файлу зависає. Чому?
6. Що таке домен колізій у сучасній мережі на комутаторах з повним дуплексом?

B1. Міст і VLAN руками будується з того самого набору команд: два хости, міст, потім VLAN. У ній flooding і навчання треба побачити в tcpdump на власні очі.

A3. Розбір пакетів із pcap починається з розбору заголовка Ethernet вручну: шість байтів, шість байтів, EtherType. Треба прочитати кадри з файлу й щоразу точно знати, де закінчується один заголовок і починається наступний.

  • Kurose, Ross, Computer Networking: A Top-Down Approach, розділ про канальний рівень
  • Peterson, Davie, Computer Networks: A Systems Approach, розділ про Ethernet і комутатори
  • IEEE 802.3 (Ethernet), IEEE 802.1D (мости й STP), IEEE 802.1Q
  • man 8 bridge, man 8 ip-link, man 8 ip-netns
  • Документація ядра Linux: Documentation/networking/bridge.rst