Организационное
Единого чата на весь курс не будет — чатики только по практическим группам, чтобы не компостировать мозги вторым большим чатом. Актуальные версии практических выдадут на практиках.
В закреплённом сообщении телеграм-канала лежит навигатор, а в нём — раздел по телекоммуникационным системам и технологиям: презентации, видеозаписи лекций и все лабы прошлых лет (записаны в 2024–2025). Сюжет дисциплины будет примерно такой же, но каждый год дорабатывается, так что что-то будет новое, в том числе правки по лабораторным.
По учебным вопросам можно обращаться в любое время — телефон ночью всё равно включён.
Зачем разработчику разбираться в сетях
Вопрос закономерный: мы позиционируемся как разработчики, а есть сетевые инженеры, есть server group в организациях, есть cloud-инженеры — пусть они и мучаются.
Ответ в том, что через телеком наше приложение вынуждено взаимодействовать через технологическую среду, которую мы не контролируем. Данные могут потеряться, скорость может скакать, данные могут переупорядочиваться — происходить может всё что угодно. Это не идеальная среда, и специфику взаимодействия через стек надо учитывать в архитектуре приложения: понимать природу ограничений, которые накладываются на коммуникацию, что можно ожидать, а что нельзя, какую функциональность имеет смысл отдать стеку, а какую реализовать самому.
Вторая значимая цель курса — инструментальные навыки: настроить себе сетевое окружение, диагностировать сетевые проблемы (разобраться, что же у меня не работает), проанализировать, что происходит на низком уровне, когда это важно для приложения. То есть стать более независимым от сетевых инженеров — по крайней мере в своём контуре. Плюс если дело дойдёт до стартапа, там обычно с людьми плохо: два-три человека, один CEO, другой маркетолог и финансовый директор, и оба по ночам пишут код. Заниматься приходится всем, почему бы не заниматься и этим.
В чём проблематика сетевой коммуникации
С виду всё просто: есть компьютеры, связали их телекоммуникационной сетью, передаём данные. Мы живём в мире достаточно развитых IT-технологий, всем этим пользуемся и никаких проблем не замечаем. А они есть — просто они уже решены внутри телекоммуникационного стека.
Системы с разной архитектурой
Взаимодействовать приходится системам с разной архитектурой, и это нетривиально. Даже межпроцессное взаимодействие в разных операционных системах устроено по-разному: в Linux — процессные пайпы (та самая мозголомная лаба из курса операционных систем), в Windows — отдельный компонент, который реализует IPC. А нам надо взять два процесса из разных ОС с разными механиками межпроцессного взаимодействия и заставить их работать вместе. Сюда же — разная разрядность систем (адресация, представление данных) и кодировки.
Зоопарк промежуточных систем
Взаимодействие идёт через целый зоопарк промежуточных технологических систем. Ноутбук идёт по Wi-Fi до местной точки доступа, от неё проводной Ethernet через коммутатор до местного роутера, потом роутер здания, потом провайдерские мрачные MPLS/VPN, потом по интернету, потом какая-нибудь оптика до сервера. И все эти аппаратные решения должны работать согласованно.
Проблема не только в разной скорости (здесь условные 150 Мбит/с, а реально где-нибудь 60; опорная сеть может быть гигабитной, дальше где-то медленнее, где-то быстрее), но и в разных алгоритмах работы и разных форматах сообщений. Иллюстрация: в заголовке обычного проводного Ethernet-кадра два адреса — отправитель и получатель. А в кадре Wi-Fi адресных полей четыре: отправитель, получатель, точка доступа и ретранслятор. Заголовки разные, и это тоже должно как-то работать вместе.
Масштабирование: соборы и broadcast
Чисто системная проблема: архитектурное решение, которое хорошо работает на небольших объёмах узлов и данных, при простом линейном масштабировании (сохранили паттерны, иерархии и увеличили количество узлов) может потерять качество или вообще не работать.
Исторический пример — соборы Московского Кремля. Это не первый вариант, который там строили. Сначала пригласили отечественных мастеров: белокаменные храмы в России строили прекрасно, а тут попросили такой же, но большой. Мастера взяли свой план, стянули его, как в фотошопе, и построили. Собор упал — потому что есть сопромат, на больших объёмах линейное масштабирование материалов не работает, там другие нагрузки. Потом приехали итальянцы с лучшей математикой и опытом каменного строительства и построили большое.
В айтишечке та же история. Простая плоская Ethernet-сеть с IPv4 на небольшом количестве узлов работает великолепно, всех всё устраивает. Попробуем плоско масштабировать её на пару тысяч узлов — а это реальный кейс, например интернет вещей: здоровенный завод, увешанный датчиками. На IPv4 такая история будет работать очень плохо, потому что гигантское количество необходимого broadcast просто пожирает производительность системы. Что делать: либо структурировать сеть, изолируя отдельные локальные участки — физически или логически (по VLAN будет лаба), либо переходить на IPv6, где broadcast нет и механизмы координации взаимодействия узлов устроены иначе.
Проклятая физика
«Гравитация — бессердечная сволочь» — это из «Теории большого взрыва», где герои тащат по лестнице огромный матрас, он падает и придавливает их. Физика передачи сигналов так же бессердечна: задержки из-за особенностей распространения сигнала, потери пакетов, помехи. Особенно в радиосетях — мобильных, Wi-Fi, — там это вообще большая проблема.
В пределах одной аппаратной платформы этого не видно: шина PCI Express прекрасно работает, все жуткие вещи, характерные для Wi-Fi, ей не интересны, передача идёт с очень высокой степенью достоверности. В компьютерных сетях достоверность конечна.
Отсутствие гарантированной полосы
Каналы связи в основном разделяются во времени, и это очень похоже на квантование времени процессора: создаётся иллюзия, что несколько процессов обрабатываются параллельно, а на самом деле они выполняются квантами по очереди. К обслуживающей аудиторию точке Wi-Fi подходит один Ethernet-кабель, и потоки данных со всех ноутов идут по нему в перемешку, по пакету.
Отсюда нет гарантированной пропускной способности линии связи — она плавающая и зависит от количества клиентов, характеристик и состояния оборудования, десятка процессов, связанных с маршрутизацией и балансировкой. Заранее сказать, насколько стабильным будет соединение, нельзя: какое-то ожидание есть, а гарантии нет.
Плюс угрозы безопасности, которые могут проявляться, а могут не проявляться, и проблемы конфигурационной совместимости.
Литература
- Таненбаум, «Компьютерные сети» — фундаментальная книга. Маленьких книг Таненбаум не пишет, это такой кирпич, как и по операционным системам. Очень подробная; кто хочет разобраться в кодировании — там изложено неплохо и понятно.
- Олифер и Олифер, «Компьютерные сети» — отечественный учебник, не менее хороший, в каком-то смысле даже лучше. Есть в библиотеке, есть PDF.
- Отдельные книги по стеку TCP/IP: первые две общетеоретические, TCP/IP в них есть, но не слишком подробно.
Почему изучение начинают с модели OSI
Открываешь учебник по сетям — вначале всегда изложена модель OSI. В последнее время много кто пишет: зачем вы занимаетесь этой чушью, модель теоретическая, в TCP/IP всё устроено не так. Но когда начинаешь разбираться, как иначе изложить архитектуру стека, выясняется, что либо предлагается какая-то собственная модель, либо это попытка рассказать всё поверх TCP/IP, и попытка безуспешная: TCP/IP — штука типовая, в нём слишком много свободы, чтобы через него разбираться в идеологии. Получается тот самый мем про теорию заговора — человек на фоне доски с красными нитями.
Так что к модели мы обратимся, и вот почему: она классная с точки зрения описания идеологии построения сетевого стека. Тем благолепием, которым мы сейчас пользуемся в сетевой инфраструктуре, мы обязаны аналитикам, которые эту модель делали.
Немного истории
TCP/IP и OSI развивались параллельно, проекты запустили примерно в одно и то же время. Только OSI делали аналитики, а прото-TCP/IP делали айтишники. Айтишники написали сразу один большой монолит — хороший монолит, который всё умел. Потом вышла модель OSI, посмотрели, что это композиция, и стек постепенно стал становиться тем, что есть сейчас.
Монолит появился не случайно. До TCP/IP и OSI сетевые решения покупались от одного вендора целиком — от проводов до прикладного обеспечения. Сейчас это трудно себе представить: тебе нужна какая-то железка, а вместе с ней ты обязан купить и сетевой стек, и сетевую карту, и провода — всё от IBM, или от DEC, или от Xerox. Американские военные сказали: нам надо строить военные сети, мы не можем попадать в зависимость от одного вендора, давайте сделаем открытый стандарт. Такую задачу и поставили перед одним из комитетов ISO — международной стандартизирующей организации.
Принципы модели
Принцип первый — привычная архитектурная декомпозиция: вся задача делится на отдельные, сравнительно изолированные этапы, каждый этап реализуется отдельным модулем, из модулей получается дата-пайплайн. Посмотрели на все задачи сетевой коммуникации, разбили на этапы — получилось семь уровней. Каждый реализован программным или программно-аппаратным модулем, и все вместе, последовательно работая, они реализуют сетевой стек.
Важно: OSI не описывает внутреннюю алгоритмику работы отдельного модуля. Стандартизируются интерфейсы и функции «вход-выход», а сами алгоритмы — нет.
Что это даёт на практике — платформонезависимость и возможность легко модифицировать отдельные модули внутри, не ломая остальное. Дома компьютер, 100-мегабитная сеть, хотим быстрее: покупаем гигабитный адаптер, договариваемся с провайдером, ставим драйвера. Надо ли перекомпилировать браузеры? Нет, они работают прозрачно. Менять компоненты ядра ОС? Нет (в Linux разве что модули подгрузить). Переписывать сетевой стек? Тоже нет. Именно потому, что интерфейсы — аппаратные и интерфейсы операционной системы к передающему устройству — сохранились: операционной системе всё равно, с каким драйвером она взаимодействует, а внутри пусть будет другой логический код, другой физический код. Так же и с TLS: меняем 1.3 обратно на 1.2, чтобы рукопожатие проходило и не падало, — и не надо переписывать ни IP, ни TCP, ни HTTP. Интерфейсная прослойка внутри себя поменялась, а остальное работает.
Семь уровней
| № | Уровень | Что делает |
|---|---|---|
| 7 | Прикладной | интерфейс доступа к стеку для пользователя или приложения, командный протокол |
| 6 | Представления | сжатие, шифрование, символьная кодировка |
| 5 | Сеансный | установление и поддержание логического сеанса между процессами |
| 4 | Транспортный | сегментация, подтверждение приёма |
| 3 | Сетевой | межсетевая коммуникация, адресация, маршрутизация |
| 2 | Канальный | приём-передача в пределах локальной сети, драйвер + железка |
| 1 | Физический | спецификация на линии связи |
Если понадобится выучить последовательность уровней, есть стишок: «Постарайтесь представить себе тачку, спешащую к финишу» — первые буквы совпадают с названиями уровней сверху вниз. Говорят, есть и неприличные стишки на эту тему.
Инкапсуляция и деинкапсуляция
Есть данные, которые надо передать с одной системы на другую сквозь сеть.

- Данные попадают на прикладной уровень системы-отправителя, обрабатываются и снабжаются своим заголовком — AH, Application Header. Произошла первая инкапсуляция: пользовательские данные инкапсулированы в сообщение прикладного уровня.
- Сообщение падает по стеку вниз, на уровень представления. Там своя обработка, и все полученные сверху байты инкапсулируются второй раз — добавляется presentation header.
- Так продолжается дальше по стеку: каждый уровень выполняет свои задачи и снабжает полученные данные своим заголовком. Матрёшка собирается — всё больше, больше, больше.
- На канальном уровне — уровне приёмо-передающего оборудования — кроме заголовка добавляется ещё и признак конца сообщения (data trailer).
- Всё это в виде физического сигнала запихивается на физический уровень, который тупой: там нет ни софта, ни железа, это просто спецификация на провода.
- На принимающей стороне сигнал приходит на физический уровень, попадает на канальный, принимается до data trailer. Получили трейлер — всё, конец, начинаем обрабатывать. Канальный уровень читает свой заголовок, делает деинкапсуляцию, вынимает инкапсулированное поле данных и пихает на сетевой уровень. Сетевой читает свой заголовок, обрабатывает — и так далее, пока данные не окажутся у пользователя или приложения на второй системе.
Сначала матрёшку собираем, потом разбираем.
Три вопроса к этой схеме
Важно ли нижележащему уровню, где данные, а где заголовок вышележащего? Нет. В идеальном мире розовых пони и единорогов — а модель OSI это он и есть — не важно: уровень получает просто байты.
Для кого имеет смысл заголовок, который добавил, например, сеансный уровень? Для аналогичного уровня на принимающей стороне. Простой пример: уровень представления занимается сжатием и шифрованием. Получил данные, сжал их и записал в заголовок признак использованного алгоритма сжатия. Эти данные нужны только уровню представления принимающей стороны: он прочитает заголовок, поймёт, каким алгоритмом разжимать, разожмёт и отдаст дальше.
Получается, что физическое взаимодействие вертикальное (данные гоняются по стеку вверх-вниз), а логическое — горизонтальное: каждый уровень живёт в святой убеждённости, что данные к нему на вход попали сразу от такого же уровня передающей стороны. Перейти на вторую систему уровни, конечно, не могут — в реальности это не так.
Более живой пример — поход на localhost. Идём на адрес из сети 127.0.0.0/8 или на ::1 в IPv6: спускаемся до сетевого уровня, тот видит предопределённые адреса и разворачивает всю коммуникацию обратно наверх, на транспортный уровень. Канальный и физический уровни не задействуются вообще, а транспортному по барабану — он честно отрабатывает туда и обратно.
Почему признак конца кадра появляется только на канальном уровне? Потому что всё, что выше канального, — это программный стек внутри одной операционки: размер буфера ты можешь с собой как-то внутри договорить, память выделена, всё спокойно читается. А когда мы покидаем канальный уровень, дальше идёт физический сигнал по физической среде, и сложиться может что угодно. Нет способа сказать канальному уровню принимающей стороны «ты должен принять 154 байта». Поэтому в конец сообщения добавляется специальный хвостик, который никогда не встретится внутри данных или заголовка — за счёт кодовой таблицы. Принимающая сторона по этому хвостику понимает: о, получил, это конец кадра, значит можно обрабатывать. Количество байт на заголовок известно, размер трейлера известен, общий размер известен — поехали. Аналогия простая: «люблю, целую, точка» — конец письма, будем писать ответ.
Объём сообщения растёт не всегда
Может сложиться впечатление, что размер сообщения от уровня к уровню только растёт: чем ниже, тем больше. Правильнее сказать, что растёт объём данных — нарастают служебные заголовки. А размер сообщения растёт только до транспортного уровня.
На транспортном уровне пришедшее сверху сообщение рубится на части 1, 2, …, n — в зависимости от фиксированного объёма или чего-то ещё, вариантов определения много. Каждый кусочек снабжается своим заголовком транспортного уровня, потом отдельно инкапсулируется в сетевой, отдельно в канальный, и передаётся. На транспортном уровне принимающей стороны происходит обратная сборка: заполняется буфер, буфер заполнился — деинкапсулируем дальше.

Служебных данных при этом передаётся даже больше. Зачем так? Соображения ровно два.
Первое — разделение канала. Пришёл утром в универ, четыре-пять пар, потом на работу, приехал домой без чувств, надо переключиться, решил посмотреть сериал. Хороший 4K-файлик, нажал кнопку «пыщ» — и началась закачка здоровенного файла на 10 гигабайт. Если бы сегментации не было, ты бы занял целиком единственную линию связи, которая приходит в дом на всех соседей. Ты качаешь, а соседи шлют тебе лучи добра, потому что никакой иллюзии разделения канала во времени больше нет — канал занят полностью. За счёт сегментации множество потоков данных от разных людей перемешивается и передаётся по общему каналу — то самое квантование, то самое разделение во времени.
Второе — конечная достоверность. Опять отучился, поработал, приехал домой, нажал «пыщ», качается, качается, уже практически 10 гигабайт скачано — и тут транспортный уровень говорит: хозяин, ошибка. Без сегментации единственный выход — качать всё заново. С сегментацией нужно перекачать только сбойный фрагмент. В TCP механизмов два: базовый — качать заново начиная со сбойного фрагмента, и более-менее современный, нормальный — качать именно сбойные фрагменты, а потом выстраивать их в нужном порядке в буфере.
По какому принципу транспортный уровень делит поток на части
Принцип первый — техническая обусловленность, злое слово MTU (Maximum Transmission Unit): максимальный размер кадра канального уровня. Он диктует размер поля данных канального уровня, это определяет размер сообщения сетевого уровня с заголовком, а отсюда уже считается размер транспортного сегмента.
Иногда MTU стараются сделать побольше — чтобы поднять производительность сети: снизить издержки на служебные заголовки, на паузы между пакетами, на время обработки заголовков. Делать это можно только там, где физический канал хороший и надёжный, какая-нибудь опорная сеть.
Плюс размер фрагмента может определяться алгоритмом работы TCP: он смотрит на условия передачи данных и на то, что хозяин написал ему в параметрах операционной системы, и от этого играет.
Слой и протокол: важный дисклеймер
Основная проблема модели OSI в том, что в ней нет протоколов. Мы привыкли, что, скажем, на прикладном уровне TCP/IP протоколов 100–500: HTTP, FTP, SMTP, POP3, IMAP4 и так далее, перечислять можно часа три. В OSI идеология другая: один уровень — один протокол.
Поэтому надо разнести два понятия:
- слой (уровень, layer) — функционально ограниченный корпус задач, который надо решать;
- протокол — реализация функций слоя.
В модели OSI на каждом слое по факту написан один протокол, поэтому там эти понятия почти тождественны: говорят «уровень» — подразумевается, что там есть какой-то софт и приёмо-передающее устройство, никакого выбора между TCP и UDP нет, просто транспорт. В TCP/IP это не так: на транспортном уровне два чистых протокола — TCP и UDP.
Строгая и нестрогая инкапсуляция. В OSI инкапсуляция строгая: только так и никак иначе, в нужном порядке сверху вниз собрали матрёшку, в обратную сторону разобрали, данные отдали. В стеке TCP/IP и многих других инкапсуляция нестрогая, а TCP/IP в этом смысле вообще хиповая штука: там практически всё можно инкапсулировать во всё. IP-пакеты в IP-пакеты, IP в UDP, даже канальный уровень можно инкапсулировать в IP и гонять через туннели. Благодаря этому работают все те VPN, которыми мы сейчас пользуемся исключительно в образовательных целях.
Разбор уровней с примерами
Примеры будут из TCP/IP — они реальные, но в TCP/IP уровней четыре, а строго говоря три с половиной. Идеального соответствия модели не получится, всё время будет какое-то перекрытие функциональности.
Прикладной
По большому счёту прикладной уровень — это командный протокол. Когда запускаешь curl, он прямо пишет протокольчик: видно, какие команды он кидает в сторону сервака и какой ответ сервак ему даёт.
Никто, кроме здравого смысла, не мешает взять терминал, подключиться к серверу и писать команды HTTP руками — всё будет работать. Давно в сетевых курсах были лабы по почтовым протоколам: подключаешься на 25-й порт TCP и пишешь письмо руками. HELO — сервер отвечает OK, RCPT TO с адресом получателя — OK, MAIL FROM с адресом отправителя — OK, DATA, перевод строки, текст письма, перевод строки, точка, перевод строки — OK, письмо отправлено. Аутентификации в базовом SMTP не было — это одна из его проблем: письмо можно было написать от кого угодно кому угодно. Именно поэтому нормально такую лабу сейчас не сделать: всё обмазано SMTPS и аутентификацией, а хеши на калькуляторе посчитать и набрать в терминале без ошибки нереально.
Но там, где шифрования нет, командами вполне можно швыряться: FTP, DNS, нешифрованный HTTP. Реально это, конечно, мазохизм и очень экзотичный случай — для диагностики. Обычно мы либо пользуемся приложением, либо как разработчики зовём фреймворк, и об этом не думаем. К счастью.
Представления
Сжатие, шифрование, символьная кодировка. Самый яркий пример — слой SSL/TLS, то, что делает из HTTP HTTPS. Эта архитектурная прослойка занимается аутентификацией (односторонней или двусторонней) и шифрованием, ну и сжатием: сейчас перед шифрованием обычно всё сжимают.
Сеансный
Из курса операционных систем помним IPC, inter-process communication: через процессные пайпы процессы могут понять, жив визави или нет, то есть работают как бы синхронно. Сделать синхронное взаимодействие через сеть — такое себе, но можно попытаться эту штуку эмулировать.
Один из самых старых сеансных протоколов — RPC, remote procedure call. Считайте, что это эмуляция межпроцессного взаимодействия через сетевой стек: можно понимать, жив визави или нет, гонять друг другу команды. Сейчас во взаимодействии между серверами используют gRPC — благодаря Google это делается более новым и удобным способом, хотя RPC до сих пор везде прекрасно себя чувствует.
Транспортный
Сегментация и подтверждение приёма. TCP и UDP — самые яркие и, наверное, самые точные примеры соответствия уровню.
Сетевой
Главная задача — межсетевая коммуникация: таскать данные между компьютерами по составной сети, которая собрана из кучи локалок. В качестве адреса — IP-адрес, он идентифицирует компьютер.
Заметим: если сеть чисто локальная — два компа, натянули между ними Ethernet-провод, — сетевой уровень всё равно работает. Сеть вроде бы не составная, а инкапсуляция на сетевом уровне происходит и адресация отрабатывает.
Сюда же протоколы маршрутизации. Составные сети — это сложно связанные графы, а по графам мы умеем искать кратчайшие пути и оптимальные маршруты. Дискретка, привет: типичное прикладное применение дискретной математики — это как раз маршрутизация. Алгоритм Дейкстры используется в протоколе OSPF (его мы будем настраивать), причём основная фишка там не столько в поиске, сколько в быстрой перестройке графа: не надо всё пересчитывать заново, поменялось в одном месте — пересчитал только это.
Самые яркие примеры — IPv4 и IPv6. На сетевом уровне софтверная история заканчивается (если не брать аппаратную обработку IP-заголовков).
Канальный (уровень сетевых интерфейсов)
Драйвер плюс железка, приёмо-передающее устройство. Нюанс: с точки зрения архитектуры операционок в канальном уровне обычно выделяют логический интерфейс и собственно аппаратный. Операционка видит сетевую карту, а на самом деле сетевой карты может и не быть — это может быть программная эмуляция, которая уже мапится на реальные приёмо-передающие устройства, если нужно, а если не нужно — не мапится.
Это пригодится в первой лабораторной: конфигурирование сетевых интерфейсов в Linux, псевдоинтерфейсы, бондинг — объединение нескольких физических интерфейсов в единый логический. Когда будете делать лабу, поймёте, что с точки зрения логики это немножко разные сущности.
Физический
В модели OSI — просто спецификация на линии связи, а для радиосигнала спецификация и требования на полосу пропускания, физический контур. Никакой интеллектуальной обработки здесь нет, это просто про проводочки с определёнными характеристиками.
Так зачем всё-таки OSI
Реализация стека по модели OSI существовала, но работала плохо — модель очень избыточная. Аналитики любят декомпозировать всё до атома, как в ООП: главное — вовремя отвести за руку, иначе будет класс «атом», потом «молекула». Плюс строгая инкапсуляция неудобна: не нужен тебе в каком-то случае RPC, а делать надо. Поэтому американские военные довольно быстро перешли на TCP/IP — параллельный проект, живой, всё хорошо.
Почему же мы потратили на OSI время: модель очень хорошо иллюстрирует логику работы сетевого стека — какие задачи стоят по дороге и в какой последовательности мы их решаем. Плюс терминология: она вся пришла из OSI. Приходишь на какой-нибудь Next Hop, видишь программу конференции, там доклад про сетевой уровень — уже понятно, про что и интересно ли.
Зачем адресовать одну и ту же сущность дважды
Частый вопрос у тех, кто начинает читать про сетевой и канальный уровни: там адреса компьютеров есть, и там адреса компьютеров есть — зачем дважды адресовать одну и ту же сущность? Вопрос не такой простой, как кажется. Знаете же, как бывает: начинаешь разбираться, задаёшь вопрос, отвечаешь себе, а потом разбираешься больше и понимаешь, что вопрос был очень хороший, а твой ответ — вообще ни фига не ответ. Есть чудесный мем про понимание с нарисованными человечками.
Условные обозначения

Сетевой узел (маленькая окружность) — сущность, которая имеет адрес канального уровня и адрес сетевого уровня. В реальности это выглядит так: есть сетевой адаптер (какой-нибудь чипсет 219v), операционная система видит сетевой интерфейс «адаптер Ethernet», и у него есть:
- физический адрес — адрес канального уровня, он же MAC-адрес. Прошит внутри сетевого адаптера, где-то записан и используется как адрес отправителя в заголовке канального уровня;
- IPv4- и IPv6-адреса — адреса сетевого уровня.
Плюс понадобится основной шлюз — то, что мы пишем при настройке TCP/IP вместе с IP и маской. IP-адрес шлюза — это IP-адрес роутера, через который я покидаю локальную сеть и иду во внешний мир. Это выход из моей квартиры.
На схеме: маленькие латинские буквы — канальные (физические) адреса, циферки — сетевые адреса. Обратите внимание, что у сетевых адресов в пределах одной локалки есть общая часть — префикс, а постфиксы узлов различаются: в первой сети все адреса начинаются с единички, во второй — с двоечки, в третьей — с троечки.
Квадратики M1 и M2 — маршрутизаторы. M1 подключён и к первой, и ко второй сети: у него есть интерфейс, смотрящий в первую сеть, и интерфейс во вторую. Фактически это два разных адаптера: можно поставить комп, воткнуть две сетевые карты и сделать роутер на Linux, можно взять аппаратный роутер — с точки зрения архитектуры два разъёма и есть два разных адаптера. У каждого адаптера свой MAC-адрес, и на каждом надо прописать свой IP: один для первой сети, другой — для второй. Роутер смотрит одним глазом в одну сеть, вторым — в другую.
Таблица маршрутизации
Чтобы это всё работало, роутерам надо каким-то образом описать структуру сети — чтобы они понимали, куда что пересылать. Для этого пишется таблица маршрутизации. Она есть везде, где есть TCP/IP-стек: на обычной рабочей машине, которая никаким роутером не является, таблица маршрутизации тоже есть.
В минимальном упрощённом виде: три локальные сети — три строки, каждая строка описывает, как с этого роутера попасть в нужную сеть. Приходит IP-пакет, который идёт в третью сеть. Роутер смотрит заголовок, видит адрес назначения, идёт в таблицу, ищет подходящую строку и видит: надо идти через порт 2.1 на шлюз 2.2. Порт — это мой интерфейс, то, через что я выхожу из роутера. Шлюз — следующий по дороге роутер.
Важно: в TCP/IP маршруты пишутся на шаг. Я запулил соседа пакетом, а дальше это его головная боль — куда он там что будет передавать.
И ещё: если мы подключены к сети непосредственно, порт и шлюз в строке совпадают. Например, у маршрутизатора M2 для третьей сети порт и шлюз совпадают — значит, он понимает: окей, это та самая сетка, я к ней подключён напрямую.
Сквозная передача с узла 1.1 на узел 3.3
Всю машинерию выше сетевого уровня опускаем — что-то там произошло.
- Сетевой уровень исходного хоста формирует IP-пакет: адрес отправителя 1.1, адрес получателя 3.3 (3.3 ему сказал хозяин).
- Пакет падает ниже по стеку, его надо инкапсулировать в кадр канального уровня. Смотрим: 3.3 — это чужое, не моя локальная сеть, префикс другой. Значит, передавать надо через шлюз. В кадре MAC-адрес отправителя — MAC узла, MAC-адрес получателя — MAC моего шлюза.
- Кадр запуливается в локальную сеть 1, гуляет по ней и попадает на канальный уровень порта маршрутизатора M1. Тот смотрит: MAC-адрес мой? Мой — принимаю, деинкапсулирую, вынимаю сетевой пакет и пихаю выше по стеку.
- Там работает программный роутер: 3.3, к этой сети я не подключён, иду в таблицу маршрутизации, нахожу маршрут — выйти через порт 2.1 на шлюз 2.2. Беру тот же IP-пакет и инкапсулирую в новый кадр канального уровня, уже для сети 2: MAC отправителя — MAC этого интерфейса, MAC получателя — MAC следующего по дороге маршрутизатора.
- Кадр гуляет по сети 2, приходит на M2. Канальный уровень буферизирует кадр, анализирует заголовок: MAC мой — надо обрабатывать. Деинкапсуляция, пакет уходит на сетевой уровень, роутер читает адрес получателя 3.3, идёт в таблицу и видит: порт и шлюз совпали, я подключён к этой сети непосредственно. Значит, при инкапсуляции в качестве MAC-адреса получателя подставляется уже MAC конечного узла.
- Кадр гуляет по сети 3, попадает на сетевой интерфейс конечного узла, тот сравнивает MAC — моё, деинкапсулирует, получает IP-пакет, пихает выше; сетевой уровень смотрит адрес — моё, деинкапсулирует, пихает выше.
Вот такая история с перепаковкой по дороге. Физически трафик, конечно, проходит через все промежуточные сети — просто в таблице маршрутизации мы описали, как туда попасть, а адресация канального уровня переписывается на каждом шаге.
Чего мы этим добились
Независимости канальных уровней. Согласовывать канальные протоколы локальных сетей не нужно: в первой сети может быть Ethernet, во второй Wi-Fi, в третьей вообще Bluetooth — всё равно, в каждой локалке будет своя оболочка канального уровня. Всё равно и то, какой там метод доступа к среде: у меня в Ethernet CSMA/CD, а в соседней сети, куда я иду, CSMA/CA — плевать, там хоть COM-порт.
Отвязки адресации сетевого уровня от железа. Был такой стек IPX/SPX — в своё время неплохой, очень приличный, работал быстро. Но сетевые адреса в нём формировались на основании канальных: к MAC-адресу прибавлялся префикс — и получался сетевой адрес. Поменял сетевой адаптер — поменялся сетевой адрес. Тупо. К счастью, IPX/SPX до наших дней не дожил, а если бы дожил, представьте эпоху виртуализации: виртуалка гуляет по гипервизорам — что, каждый раз менять сетевой адрес? Конечно, нет: твоему сетевому визави вообще без разницы, на каком железе ты работаешь и в какой локальной сети. Сетевой адрес — это такой сервисный адрес в составной сети.
За счёт многоуровневой реализации мы разделили работу канального уровня с одной локалкой и работу сетевого уровня с составной сетью. Ещё раз, чтобы не было иллюзий: если у вас локальная сетка и никакого роутера нет, инкапсуляция всё равно есть, сетевой уровень всё равно есть, канальный всё равно есть.
Маленькое следствие. На схеме есть два совпадающих канальных адреса в разных локальных сетях — aaa и AAA. Проблема ли это? Нет: область действия MAC-адреса — локальная сеть. Внутри локалки он должен быть уникальным, потому что при неуникальном MAC коммуникационное оборудование попадает в затруднительное положение, а целенаправленная подмена MAC — это атака на инфраструктуру, ARP spoofing.
Вопрос из зала: пойдёт ли через мой узел чужой трафик
Видно ли на интерфейсе чужой трафик — зависит от канального протокола. В Wi-Fi чужой трафик видно. В Ethernet в некоторых ситуациях тоже: чужие кадры канального уровня приходят на интерфейс, и вопрос только в том, отбрасываешь ты их или нет. Хаб, например, всегда работает «всем». На опорных сетях это, понятное дело, стараются исключить.
ARP
Ради динамики повествования одну проблему мы обошли: мы не знаем MAC-адрес того, к кому хотим обратиться.
Смотрите, что у нас есть в самом начале. IP-пакет сделали: свой адрес прочитали в конфиге, адрес получателя сказал хозяин. Инкапсулируем — свой MAC-адрес мы тоже знаем, посмотрели в конфиге. А откуда мы узнали MAC-адрес шлюза? Про шлюз мы знаем только его сетевой адрес.
Для этого в IPv4 есть специальный протокол — ARP, Address Resolution Protocol. Он broadcast-ный: сообщение по этому протоколу, хочешь не хочешь, получают все узлы локальной сети. Когда узел упирается в эту неразрешимую проблему — какой MAC вписывать, если я знаю только сетевой адрес 1.3, — он буквально кричит в локальную сеть: друзья и подруги, у кого адрес 1.3? И тот, у кого адрес 1.3, отвечает: у меня.
Дальше берётся тетрадочка, и в неё записывается ответ — это ARP-кэш. Повзаимодействовали с адресами, динамически определили через ARP их MAC — записали себе, чтобы второй раз не спрашивать, а брать отсюда.
В таблице ARP-кэша есть и статические записи, и из них нас пока интересует адрес ff:ff:ff:ff:ff:ff — шестнадцатеричный адрес из одних единиц. Это broadcast-адрес: когда надо наорать на всех в локальной сети, шлём сообщение на этот адрес, и его получат все.
Обратите внимание, что вся эта машинерия с ARP происходит на каждой из локальных сетей по дороге.
ARP-кэш можно сбрасывать, если есть привилегии, и можно заносить в него статические записи руками — если зачем-то нужны мегаскорости. В IPv6 этой проблемы нет, там всё решается по-другому.
Вопрос из зала: а адрес роутера откуда берётся
Адрес роутера через ARP не узнаётся — его задают: либо в конфигурации как шлюз по умолчанию, либо прописывают в таблице маршрутизации. Он должен откуда-то взяться: либо пришёл сетевой инженер в свитере с оленями и с бородой и написал руками, либо отработали протоколы маршрутизации или динамическая конфигурация, которые эти параметры поставили.
А ARP делает другое: по сетевому адресу определяет канальный. Зная, что шлюз — это 1.3, с помощью ARP мы узнаём, что MAC-адрес у него, например, ad3, и только после этого можем сделать инкапсуляцию.
Про срок аренды адресов и про то, как она продлевается, будет отдельная лекция — про DHCP. Плюс лаба: настройка своего DHCP-сервера с разными скоупами. А кто не отчислится, в следующем семестре получит курс по Windows Server, и там лаба по настройке ещё более хитрая.
Инкапсуляция в реальности: Wireshark
До сих пор мы рисовали квадратики, а теперь посмотрим на скриншоты из Wireshark — это сетевой снифер, по нему будет большая мутная лаба про диагностику.
На скриншоте перехвачен один пакет и разобран по всем заголовкам — вся сетевая матрёшка нарисована целиком.

Снаружи — кадр канального уровня, формат Ethernet II: прямо в заголовке MAC отправителя и MAC получателя.

Внутри него вложен IPv4-пакет: IP отправителя, IP получателя и прочие жуткие поля.

Внутри IP-пакета лежит TCP-сообщение транспортного уровня: порты отправителя и получателя, куча чек-сумм и так далее.

А внутри транспортного — HTTP, и судя по всему это ответ сервера. Просто команды высокого уровня.
